bash 완료 컨텍스트에서 $ {array [*]}와 $ {array [@]}에 대한 혼동


89

나는 처음으로 bash 완성을 작성하는 데 찌르고 있으며 bash 배열 ( ${array[@]}${array[*]}) 을 역 참조하는 두 가지 방법에 대해 약간 혼란 스럽습니다 .

다음은 관련 코드 덩어리입니다 (그런데 작동하지만 더 잘 이해하고 싶습니다).

_switch()
{
    local cur perls
    local ROOT=${PERLBREW_ROOT:-$HOME/perl5/perlbrew}
    COMPREPLY=()
    cur=${COMP_WORDS[COMP_CWORD]}
    perls=($ROOT/perls/perl-*)
    # remove all but the final part of the name
    perls=(${perls[*]##*/})

    COMPREPLY=( $( compgen -W "${perls[*]} /usr/bin/perl" -- ${cur} ) )
}

bash의 문서에 따르면 다음과 같습니다.

배열의 모든 요소는 $ {name [subscript]}를 사용하여 참조 할 수 있습니다. 셸의 파일 이름 확장 연산자와의 충돌을 방지하려면 중괄호가 필요합니다. 아래 첨자가 '@'또는 '*'이면 단어는 배열 이름의 모든 구성원으로 확장됩니다. 이 아래 첨자는 단어가 큰 따옴표 안에있는 경우에만 다릅니다. 단어가 큰 따옴표 인 경우 $ {name [*]}은 각 배열 구성원의 값이 IFS 변수의 첫 번째 문자로 구분 된 단일 단어로 확장되고 $ {name [@]}은 이름의 각 요소를 확장합니다. 별도의 단어로.

이제 compgen -W가능한 대안의 단어 목록이 포함 된 문자열 이 예상 된다는 것을 이해한다고 생각 합니다. 그러나이 컨텍스트에서는 "$ {name [@]}이 이름의 각 요소를 별도의 단어로 확장합니다"가 의미하는 바를 이해하지 못합니다.

짧은 이야기 : ${array[*]}작품; ${array[@]}하지 않습니다. 그 이유를 알고 싶습니다 ${array[@]}. 정확히 무엇이 확장 되는지 더 잘 이해하고 싶습니다 .

답변:


119

(이것은 Kaleb Pederson의 답변에 대한 내 의견을 확장 한 것입니다 . [@]vs.[*]

bash (또는 유사한 셸)가 명령 줄을 구문 분석 할 때 일련의 "단어"(나중에 혼동을 피하기 위해 "셸 단어"라고 부름)로 분할합니다. 일반적으로 쉘 단어는 공백 (또는 기타 공백)으로 구분되지만 공백은 이스케이프하거나 인용하여 쉘 단어에 포함될 수 있습니다. 큰 따옴표로 묶인 배열 [@][*]확장 배열 의 차이점은 배열의 "${myarray[@]}"각 요소가 별도의 셸 단어로 처리되는 반면 "${myarray[*]}"배열의 모든 요소가 공백으로 구분 된 단일 셸 단어가됩니다 (또는 의 첫 문자가 무엇이든 IFS).

일반적으로 [@]행동은 당신이 원하는 것입니다. 우리가 가지고 perls=(perl-one perl-two)있고 사용 한다고 가정 ls "${perls[*]}"합니다 ls "perl-one perl-two". 이것은라는 이름의 단일 파일을 찾을 perl-one perl-two것입니다. ls "${perls[@]}"상당이다 ls "perl-one" "perl-two"훨씬 더 가능성이 유용한 일을 할 것입니다.

완성 단어 목록 (셸 단어와의 혼동을 피하기 위해 comp-words라고 부름)을 제공하는 compgen것은 다릅니다. 이 -W옵션은 comp-word 목록을 가져 오지만, comp-word가 공백으로 구분 된 단일 쉘-워드 형식이어야합니다. 항상 (적어도 내가 아는 한) 인수를받는 명령 옵션은 단일 쉘 단어를 사용합니다. 그렇지 않으면 옵션에 대한 인수가 언제 끝나고 일반 명령 인수 (/ other 옵션 플래그) 시작합니다.

더 자세하게:

perls=(perl-one perl-two)
compgen -W "${perls[*]} /usr/bin/perl" -- ${cur}

다음과 같습니다.

compgen -W "perl-one perl-two /usr/bin/perl" -- ${cur}

... 당신이 원하는 것을합니다. 반면에

perls=(perl-one perl-two)
compgen -W "${perls[@]} /usr/bin/perl" -- ${cur}

다음과 같습니다.

compgen -W "perl-one" "perl-two /usr/bin/perl" -- ${cur}

... 완전한 말도 안되는 말입니다. "perl-one"은 -W 플래그에 첨부 된 유일한 comp-word이고, compgen이 완성 될 문자열로 취할 첫 번째 실제 인수는 "perl-two"입니다. / usr / bin / perl "입니다. 나는 compgen이 추가 인수 ( "-"및 $ cur에있는 모든 것)가 주어 졌다고 불평 할 것으로 예상하지만 분명히 무시합니다.


3
이것은 훌륭합니다. 감사. 나는 그것이 더 큰 소리로 날아 가기를 정말로 바란다. 그러나 이것은 적어도 그것이 작동하지 않는 이유를 분명히 보여준다.
Telemachus

60

제목은 ${array[@]}대 에 대해 ${array[*]}묻지 만 어느 $array[*]대 에 대해 물어 보는 $array[@]것이 약간 혼란 스럽습니다. 나는 둘 다 대답 할 것이다 :

배열 변수를 인용 @하고 아래 첨자로 사용하면 배열의 각 요소는 $IFS해당 콘텐츠 내에 존재할 수있는 공백 (실제로는의 하나 )에 관계없이 전체 콘텐츠로 확장됩니다 . 별표 ( *)를 아래 첨자로 사용하면 ( 인용 여부에 관계없이)에서 각 배열 요소의 콘텐츠를 분할하여 만든 새 콘텐츠로 확장 될 수 있습니다 $IFS.

다음은 예제 스크립트입니다.

#!/bin/sh

myarray[0]="one"
myarray[1]="two"
myarray[3]="three four"

echo "with quotes around myarray[*]"
for x in "${myarray[*]}"; do
        echo "ARG[*]: '$x'"
done

echo "with quotes around myarray[@]"
for x in "${myarray[@]}"; do
        echo "ARG[@]: '$x'"
done

echo "without quotes around myarray[*]"
for x in ${myarray[*]}; do
        echo "ARG[*]: '$x'"
done

echo "without quotes around myarray[@]"
for x in ${myarray[@]}; do
        echo "ARG[@]: '$x'"
done

다음은 출력입니다.

with quotes around myarray[*]
ARG[*]: 'one two three four'
with quotes around myarray[@]
ARG[@]: 'one'
ARG[@]: 'two'
ARG[@]: 'three four'
without quotes around myarray[*]
ARG[*]: 'one'
ARG[*]: 'two'
ARG[*]: 'three'
ARG[*]: 'four'
without quotes around myarray[@]
ARG[@]: 'one'
ARG[@]: 'two'
ARG[@]: 'three'
ARG[@]: 'four'

나는 개인적으로 보통 원한다 "${myarray[@]}". 이제, 질문의 두 번째 부분에 대답 ${array[@]}$array[@].

인용 한 bash 문서를 인용합니다.

셸의 파일 이름 확장 연산자와의 충돌을 방지하려면 중괄호가 필요합니다.

$ myarray=
$ myarray[0]="one"
$ myarray[1]="two"
$ echo ${myarray[@]}
one two

그러나 그렇게 할 때 $myarray[@]달러 기호는 단단히 묶여 myarray있으므로 [@]. 예를 들면 :

$ ls $myarray[@]
ls: cannot access one[@]: No such file or directory

그러나 설명서에서 언급했듯이 대괄호는 파일 이름 확장을위한 것이므로 다음을 시도해 보겠습니다.

$ touch one@
$ ls $myarray[@]
one@

이제 확장 후 파일 이름 확장이 발생했음을 알 수 있습니다 $myarray.

그리고 $myarray아래 첨자가없는 또 하나의 메모 는 배열의 첫 번째 값으로 확장됩니다.

$ myarray[0]="one four"
$ echo $myarray[5]
one four[5]

1
또한 볼 방법에 대한 IFS출력이 다르게에 따라 영향을 @*하고 인용 부호로 둘러싸이지 않은 대 인용했다.
추후 공지가있을 때까지 일시 중지되었습니다.

이 맥락에서 매우 중요하기 때문에 사과하지만 항상 ${array[*]}또는 ${array[@]}. 교정기의 부족은 단순히 부주의했습니다. 그 외에도 명령 ${array[*]}에서 확장되는 내용을 설명 할 수 compgen있습니까? 즉, 그 맥락에서 배열을 각 요소로 개별적으로 확장한다는 것은 무엇을 의미합니까?
Telemachus

이것을 다른 방식으로 말하면, 당신 (거의 모든 출처와 마찬가지로)은 그것이 ${array[@]}보통가는 길이 라고 말합니다 . 내가 이해하려는 것은이 경우 에만 ${array[*]} 작동하는 이유입니다 .
Telemachus

1
-W 옵션과 함께 제공되는 단어 목록은 단일 단어로 제공되어야하기 때문입니다 (그런 다음 compgen은 IFS를 기반으로 분할 됨). compgen ([@]이하는 일)에 전달되기 전에 별도의 단어로 분리되면 compgen은 첫 번째 단어 만 -W와 함께 가고 나머지는 일반 인수라고 생각할 것입니다. 따라서 barf).
Gordon Davisson 2010-07-28

@Gordon : 대답으로 옮기면 받아 들일 게요. 그것이 제가 정말로 알고 싶었던 것입니다. 감사. (, BTW 그것은 확실한 방법으로하지 발프 않습니다 그것은 자동으로 barfs -.. 열심히 무엇이 잘못되었는지 알고있는 수)
텔레 마커스
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.