Bash 매개변수 배열 인덱싱 및 수정 $@

Bash 매개변수 배열 인덱싱 및 수정 $@

에서 인덱스를 참조할 수 있나요 $@? 어디에서나 사용할 수 있는 참조를 찾을 수 없습니다.회색 고양이 위키,게다가고급 스크립팅 가이드그리고다른 사람수정하기 전에 다른 변수에 할당하십시오.

$ echo ${@[0]}
-bash: ${@[0]}: bad substitution

목표는마른: 첫 번째 인수는 한 가지에 대한 것이고 나머지는 다른 것에 대한 것입니다. 정규화, 배열에 대한 코드를 반복하거나 이를 위한 별도의 함수를 생성하는 것을 피하고 싶습니다 $@(아마도 이 시점에서 가장 쉬운 방법일지라도).

설명: 목적은 수정하는 것입니다.가변 길이 $@만들다암호디버깅이 더 쉽습니다. 현재 버전은 나에게는 너무 구식이지만 이상한 경로에서도 작동합니다.

$'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'

고쳐 쓰다: 불가능할 것 같습니다.암호이제 코드와 데이터 복사를 모두 사용하지만 적어도 작동합니다.

path_common()
{
    # Get the deepest common path.
    local common_path="$(echo -n "${1:-}x" | tr -s '/')"
    common_path="${common_path%x}"
    shift # $1 is obviously part of $1
    local path

    while [ -n "${1+defined}" ]
    do
        path="$(echo -n "${1}x" | tr -s '/')"
        path="${path%x}"
        if [[ "${path%/}/" = "${common_path%/}/"* ]]
        then
            shift
        else
            new_common_path="${common_path%/*}"
            [ "$new_common_path" = "$common_path" ] && return 1 # Dead end
            common_path="$new_common_path"
        fi
    done
    printf %s "$common_path"
}

이 혼란에서 벗어날 수 있는 사람은 누구나 현상금을 받게 될 것입니다.중복된 코드반복되는 슬래시를 접거나데이터 복제$1코드를 적당한 크기로 유지하고 모든 단위 테스트를 성공적으로 통과하면서 및/또는 기타 매개변수를 저장합니다 .

test "$(path_common /a/b/c/d /a/b/e/f; echo x)" = /a/bx
test "$(path_common /long/names/foo /long/names/bar; echo x)" = /long/namesx
test "$(path_common / /a/b/c; echo x)" = /x
test "$(path_common a/b/c/d a/b/e/f ; echo x)" = a/bx
test "$(path_common ./a/b/c/d ./a/b/e/f; echo x)" = ./a/bx
test "$(path_common $'\n/\n/\n' $'\n/\n'; echo x)" = $'\n/\n'x
test "$(path_common --/-- --; echo x)" = '--x'
test "$(path_common '' ''; echo x)" = x
test "$(path_common /foo/bar ''; echo x)" = x
test "$(path_common /foo /fo; echo x)" = x
test "$(path_common $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n' $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'; echo x)" = $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'x
test "$(path_common /foo/bar //foo//bar//baz; echo x)" = /foo/barx
test "$(path_common foo foo; echo x)" = foox
test "$(path_common /fo /foo; echo x)" = x

답변1

POSIX

모든 인수에서 슬래시를 정규화하기 위해 인수 회전 트릭을 사용하겠습니다. 이를 끄고 $1변환한 다음 결과를 인수 목록 끝에 넣습니다. 인수 수만큼 이 작업을 수행하면 모든 인수가 변환되어 원래 순서로 복원됩니다.

코드의 두 번째 부분에서는 덜 혼란스럽도록 논리를 변경했습니다. 외부 루프는 매개변수를 반복하고, 내부 루프는 경로 구성요소를 반복합니다. for x; do … done위치 매개변수를 반복하는 것은 편리한 관용구입니다. POSIX 호환 방법인 구문을 사용하여 패턴과 문자열을 일치시킵니다 case.

테스트에는 dash 0.5.5.1, pdksh 5.2.14, bash 3.2.39, bash 4.1.5, ksh 93s+, zsh 4.3.10을 사용하십시오.

참고 사항: bash 4.1.5(3.2에는 없음)에 버그가 있는 것 같습니다. 케이스 모드가 이면 "${common_path%/}"/*테스트 중 하나가 실패합니다.

posix_path_common () {
  for tmp; do
    tmp=$(printf %s. "$1" | tr -s "/")
    set -- "$@" "${tmp%.}"
    shift
  done
  common_path=$1; shift
  for tmp; do
    while case ${tmp%/}/ in "${common_path%/}/"*) false;; esac; do
      new_common_path=${common_path%/*}
      if [ "$new_common_path" = "$common_path" ]; then return 1; fi
      common_path=$new_common_path
    done
  done
  printf %s "$common_path"
}

배쉬, 크쉬

bash(또는 ksh)를 사용하는 경우 배열을 사용할 수 있습니다. 왜 위치 인수로 제한하는지 이해할 수 없습니다. 배열을 사용하는 버전입니다. POSIX 버전보다 특별히 명확하지는 않지만 초기 n^2 셔플링을 방지한다는 점을 인정해야 합니다.

슬래시 정규화 부분에서는 ksh93 생성자를 사용하여 ${foo//PATTERN/REPLACEMENT}in의 모든 항목을 . 이 패턴은 bash에서 하나 이상의 슬래시를 일치시키는 데 사용되며 유효해야 합니다(startup bash를 사용하는 것과 동일). 이 구성은 위치 인수를 배열의 첨자 목록으로 설정합니다. 이는 배열 요소를 반복하는 편리한 방법을 제공합니다.PATTERN$fooREPLACEMENT+(\/)shopt -s extglobbash -O extglobset ${!a[@]}a

두 번째 부분에서는 POSIX 버전과 동일한 루프 논리를 사용합니다. 이번에는 [[ … ]]여기의 모든 대상 쉘이 지원하기 때문에 사용할 수 있습니다 .

테스트에는 bash 3.2.39, bash 4.1.5, ksh 93s+를 사용하세요.

array_path_common () {
  typeset a i tmp common_path new_common_path
  a=("$@")
  set ${!a[@]}
  for i; do
    a[$i]=${a[$i]//+(\/)//}
  done
  common_path=${a[$1]}; shift
  for tmp; do
    tmp=${a[$tmp]}
    while [[ "${tmp%/}/" != "${common_path%/}/"* ]]; do
      new_common_path="${common_path%/*}"
      if [[ $new_common_path = $common_path ]]; then return 1; fi
      common_path="$new_common_path"
    done
  done
  printf %s "$common_path"
}

다루기 힘든

불행하게도 zsh에는 ${!array[@]}ksh93 버전을 있는 그대로 실행하는 기능이 부족합니다. 다행스럽게도 zsh에는 첫 번째 부분을 쉽게 만드는 두 가지 기능이 있습니다. 배열과 같은 위치 매개변수를 인덱싱할 수 있으므로 @중간 배열을 사용할 필요가 없습니다. zsh에는 하나가 있습니다배열 반복 구성: "${(@)array//PATTERN/REPLACEMENT}"각 배열 요소에 대해 차례로 패턴 대체를 수행하고 결과 배열을 평가합니다(혼돈스럽게도 결과가 여러 단어인 경우에도 큰따옴표가 필요합니다. 이는 일반화입니다 "$@"). 두 번째 부분은 본질적으로 변경되지 않았습니다.

zsh_path_common () {
  setopt local_options extended_glob
  local tmp common_path new_common_path
  set -- "${(@)@//\/##//}"
  common_path=$1; shift
  for tmp; do
    while [[ "${tmp%/}/" != "${common_path%/}/"* ]]; do
      new_common_path="${common_path%/*}"
      if [[ $new_common_path = $common_path ]]; then return 1; fi
      common_path="$new_common_path"
    done
  done
  printf %s "$common_path"
}

테스트 케이스

내 솔루션은 최소한으로 테스트되고 주석이 달렸습니다. 쉘 없이 $'…'구문 분석하고 보다 편리한 방법으로 오류를 보고 할 수 있도록 테스트 사례의 구문을 변경했습니다 .

do_test () {
  if test "$@"; then echo 0; else echo $? "$@"; failed=$(($failed+1)); fi
}

run_tests () {
  function_to_test=$1; shift
  failed=0
  do_test "$($function_to_test /a/b/c/d /a/b/e/f; echo x)" = /a/bx
  do_test "$($function_to_test /long/names/foo /long/names/bar; echo x)" = /long/namesx
  do_test "$($function_to_test / /a/b/c; echo x)" = /x
  do_test "$($function_to_test a/b/c/d a/b/e/f ; echo x)" = a/bx
  do_test "$($function_to_test ./a/b/c/d ./a/b/e/f; echo x)" = ./a/bx
  do_test "$($function_to_test '
/
/
' '
/
'; echo x)" = '
/
'x
  do_test "$($function_to_test --/-- --; echo x)" = '--x'
  do_test "$($function_to_test '' ''; echo x)" = x
  do_test "$($function_to_test /foo/bar ''; echo x)" = x
  do_test "$($function_to_test /foo /fo; echo x)" = x
  do_test "$($function_to_test '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
' '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
'; echo x)" = '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
'x
  do_test "$($function_to_test /foo/bar //foo//bar//baz; echo x)" = /foo/barx
  do_test "$($function_to_test foo foo; echo x)" = foox
  do_test "$($function_to_test /fo /foo; echo x)" = x
  if [ $failed -ne 0 ]; then echo $failed failures; return 1; fi
}

답변2

$1, $2.. $9, ${10}, ${11}.. 등을 사용하면 어떨까요? 더 나아가마른-무엇을 하고 싶은가가 더 중요해요 :)

$ 사이의 관계에 대해 자세히 알아보기숫자그리고 $@:

$@는 "모든 인수를 포함하는 배열의 모든 요소"에 대한 약칭으로 생각할 수 있습니다.

따라서 $@는 ${args[@]}의 약칭입니다(여기서 args는 모든 인수를 포함하는 "가상" 배열입니다. 실제 변수는 아닙니다.)

$1은 ${args[1]}이고 $2는 ${args[2]}입니다.

[9]를 누르면 중괄호를 사용합니다. ${10}은 ${args[10]}이고, ${11}은 ${args[11]}입니다.


명령줄 매개변수를 간접적으로 사용

argnum=3  # You want to get the 3rd arg
do-something ${!argnum}  # Do something with the 3rd arg

예:

argc=$#
for (( argn=1; argn<=argc; argn++)); do
    if [[ ${!argn} == "foo" ]]; then
        echo "Argument $argn of $argc is 'foo'"
    fi
done

답변3

첫 번째 매개변수는 한 가지에만 사용되고 나머지는 다른 목적에 사용됩니다.

내 생각엔 당신이 원하는 건shift

$ set one two three four five
$ echo $@
one two three four five
$ echo $1
one
$ foo=$1
$ echo $foo
one
$ shift
$ echo $@
two three four five
$ shift 2
$ echo $@
four five
$ echo $1
four

답변4

파일 이름에는 공백이 지원됩니다.

function SplitFilePath {
    IFS=$'/' eval "${1}"=\( \${2} \)
}
function JoinFilePath {
    IFS=$'/' eval echo -n \"\${*}\"
    [ $# -eq 1 -a "${1}" = "" ] && echo -n "/"
}
function path_common {
    set -- "${@//\/\///}"       ## Replace all '//' with '/'
    local -a Path1
    local -i Cnt=0
    SplitFilePath Path1 "${1}"
    IFS=$'/' eval set -- \${2} 
    for CName in "${Path1[@]}" ; do
        [ "${CName}" != "${1}" ] && break;
        shift && (( Cnt++ ))
    done
    JoinFilePath "${Path1[@]:0:${Cnt}}"
}

공백이 있는 파일 이름 테스트 사례를 추가하고 선행/가 누락된 2개의 테스트를 수정했습니다.

    do_test () {

  if test "${@}"; then echo 0; else echo $? "$@"; failed=$(($failed+1)); fi
}

run_tests () {
  function_to_test=$1; shift
  failed=0
  do_test "$($function_to_test /a/b/c/d /a/b/e/f; echo x)" = /a/bx
  do_test "$($function_to_test /long/names/foo /long/names/bar; echo x)" = /long/namesx
  do_test "$($function_to_test / /a/b/c; echo x)" = /x      
  do_test "$($function_to_test a/b/c/d a/b/e/f ; echo x)" = a/bx
  do_test "$($function_to_test ./a/b/c/d ./a/b/e/f; echo x)" = ./a/bx
  do_test "$($function_to_test '
/
/
' '
/
'; echo x)" = '
/
'x
  do_test "$($function_to_test --/-- --; echo x)" = '--x'
  do_test "$($function_to_test '' ''; echo x)" = x
  do_test "$($function_to_test /foo/bar ''; echo x)" = x
  do_test "$($function_to_test /foo /fo; echo x)" = /x      ## Changed from x
  do_test "$($function_to_test '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
' '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
'; echo x)" = '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
'x
  do_test "$($function_to_test /foo/bar //foo//bar//baz; echo x)" = /foo/barx
  do_test "$($function_to_test foo foo; echo x)" = foox
  do_test "$($function_to_test /fo /foo; echo x)" = /x          ## Changed from x
  do_test "$($function_to_test "/fo d/fo" "/fo d/foo"; echo x)" = "/fo dx"

  if [ $failed -ne 0 ]; then echo $failed failures; return 1; fi
}

관련 정보