[bash] `set -u`로 빈 배열 확장을 배시
나는 bash 스크립트를 작성 중이며 set -u
빈 배열 확장에 문제가 있습니다. bash는 확장 중에 빈 배열을 설정되지 않은 변수로 취급하는 것으로 보입니다.
$ set -u
$ arr=()
$ echo "foo: '${arr[@]}'"
bash: arr[@]: unbound variable
( declare -a arr
도 도움이되지 않습니다.)
이에 대한 일반적인 해결책은 ${arr[@]-}
대신 사용 하여 ( “정의되지 않은”) 빈 배열 대신 빈 문자열로 대체하는 것입니다. 그러나 이것은 좋은 해결책이 아닙니다. 지금은 하나의 빈 문자열이있는 배열과 빈 배열을 구별 할 수 없기 때문입니다. (@ -expansion는 확장, bash는 특별한 "${arr[@]}"
에 "${arr[0]}" "${arr[1]}" …
그 명령 라인을 구축하기위한 완벽한 도구 만드는.)
$ countArgs() { echo $#; }
$ countArgs a b c
3
$ countArgs
0
$ countArgs ""
1
$ brr=("")
$ countArgs "${brr[@]}"
1
$ countArgs "${arr[@]-}"
1
$ countArgs "${arr[@]}"
bash: arr[@]: unbound variable
$ set +u
$ countArgs "${arr[@]}"
0
그렇다면 배열의 길이를 확인하거나 if
(아래 코드 샘플 참조) -u
짧은 부분에 대한 설정을 끄는 것 외에 그 문제를 해결할 수있는 방법이 있습니까?
if [ "${#arr[@]}" = 0 ]; then
veryLongCommandLine
else
veryLongCommandLine "${arr[@]}"
fi
업데이트 :bugs
ikegami의 설명으로 인해 태그가 제거 되었습니다.
답변
유일한 안전 관용구이다${arr[@]+"${arr[@]}"}
이것은 이미 ikegami의 답변 에서 권장 사항 이지만이 스레드에는 많은 잘못된 정보와 추측이 있습니다. 같은 다른 패턴, ${arr[@]-}
또는 ${arr[@]:0}
이다 하지 배쉬의 모든 주요 버전에서 안전합니다.
아래 표에서 볼 수 있듯이 모든 최신 Bash 버전에서 신뢰할 수있는 유일한 확장은 ${arr[@]+"${arr[@]}"}
(열 +"
)입니다. 참고로, Bash 4.2에서는 (불행히도) 짧은 ${arr[@]:0}
관용구를 포함하여 몇 가지 다른 확장이 실패하며 , 이는 잘못된 결과를 생성 할뿐만 아니라 실제로 실패합니다. 4.4 이전 버전, 특히 4.2 버전을 지원해야하는 경우 이것이 유일하게 작동하는 관용구입니다.
안타깝게도 +
한눈에 똑같이 보이는 다른 확장팩은 실제로 다른 동작을 내 보냅니다. :+
확장은 안전 하지 않습니다. :
-expansion 은 하나의 빈 요소 ( ('')
)가 있는 배열을 “null”로 취급하므로 동일한 결과로 (일관되게) 확장되지 않기 때문입니다.
"${arr[@]+${arr[@]}}"
대략적으로 동일 할 것으로 예상했던 중첩 배열 ( ) 대신 전체 확장을 인용하는 것은 4.2에서 비슷하게 안전하지 않습니다.
이 요점 에서 몇 가지 추가 버전의 bash에 대한 결과와 함께이 데이터를 생성 한 코드를 볼 수 있습니다 .
답변
문서에 따르면
첨자에 값이 할당 된 경우 배열 변수가 설정된 것으로 간주됩니다. 널 문자열은 유효한 값입니다.
값이 할당 된 첨자가 없으므로 배열이 설정되지 않습니다.
그러나 문서는 여기에 오류가 적절하다고 제안하지만 4.4 이후 에는 더 이상 그렇지 않습니다 .
$ bash --version | head -n 1
GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-gnu)
$ set -u
$ arr=()
$ echo "foo: '${arr[@]}'"
foo: ''
이전 버전에서 원하는 것을 얻기 위해 인라인을 사용할 수있는 조건이 있습니다 . ${arr[@]+"${arr[@]}"}
대신 "${arr[@]}"
.
$ function args { perl -E'say 0+@ARGV; say "$_: $ARGV[$_]" for 0..$#ARGV' -- "$@" ; }
$ set -u
$ arr=()
$ args "${arr[@]}"
-bash: arr[@]: unbound variable
$ args ${arr[@]+"${arr[@]}"}
0
$ arr=("")
$ args ${arr[@]+"${arr[@]}"}
1
0:
$ arr=(a b c)
$ args ${arr[@]+"${arr[@]}"}
3
0: a
1: b
2: c
bash 4.2.25 및 4.3.11로 테스트되었습니다.
답변
@ikegami의 수락 된 대답은 미묘하게 잘못되었습니다! 올바른 주문은 ${arr[@]+"${arr[@]}"}
다음과 같습니다.
$ countArgs () { echo "$#"; }
$ arr=('')
$ countArgs "${arr[@]:+${arr[@]}}"
0 # WRONG
$ countArgs ${arr[@]+"${arr[@]}"}
1 # RIGHT
$ arr=()
$ countArgs ${arr[@]+"${arr[@]}"}
0 # Let's make sure it still works for the other case...
답변
최근 릴리스 (2016/09/16) bash 4.4 (예 : Debian stretch에서 사용 가능)에서 배열 처리가 변경된 것으로 나타났습니다.
$ bash --version | head -n1
bash --version | head -n1
GNU bash, version 4.4.0(1)-release (x86_64-pc-linux-gnu)
이제 빈 배열 확장이 경고를 표시하지 않습니다.
$ set -u
$ arr=()
$ echo "${arr[@]}"
$ # everything is fine
답변
이것은 arr [@] 중복을 선호하지 않고 빈 문자열을 사용 해도되는 다른 옵션 일 수 있습니다.
echo "foo: '${arr[@]:-}'"
테스트하려면 :
set -u
arr=()
echo a "${arr[@]:-}" b # note two spaces between a and b
for f in a "${arr[@]:-}" b; do echo $f; done # note blank line between a and b
arr=(1 2)
echo a "${arr[@]:-}" b
for f in a "${arr[@]:-}" b; do echo $f; done
답변
@ikegami의 대답은 정확하지만 구문을 고려합니다. ${arr[@]+"${arr[@]}"}
. 긴 배열 변수 이름을 사용하면 평소보다 더 빨리 스파게티처럼 보이기 시작합니다.
대신 이것을 시도하십시오.
$ set -u
$ count() { echo $# ; } ; count x y z
3
$ count() { echo $# ; } ; arr=() ; count "${arr[@]}"
-bash: abc[@]: unbound variable
$ count() { echo $# ; } ; arr=() ; count "${arr[@]:0}"
0
$ count() { echo $# ; } ; arr=(x y z) ; count "${arr[@]:0}"
3
Bash 배열 슬라이스 연산자가 매우 관대 해 보입니다.
그렇다면 Bash는 왜 배열의 엣지 케이스를 처리하기 어렵게 만들었습니까? 한숨. 버전이 배열 슬라이스 연산자의 남용을 허용 할 것이라고 보장 할 수는 없지만 멋지게 작동합니다.
주의 사항 : 사용중인 GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
마일리지는 다를 수 있습니다.
답변
참으로 “흥미로운”불일치.
더욱이,
$ set -u
$ echo $#
0
$ echo "$1"
bash: $1: unbound variable # makes sense (I didn't set any)
$ echo "$@" | cat -e
$ # blank line, no error
@ikegami가 설명하는 의미에서 현재 동작이 버그가 아닐 수 있다는 데 동의하지만, IMO는 버그가 정의 ( “세트”) 자체 및 / 또는 일관성없이 적용된다는 사실에 있다고 말할 수 있습니다. 맨 페이지의 앞 단락은 다음과 같습니다.
…
${name[@]}
이름의 각 요소를 별도의 단어로 확장합니다. 배열 구성원이 없으면${name[@]}
아무것도 확장되지 않습니다.
에서 위치 매개 변수의 확장에 대해 말하는 내용과 완전히 일치 "$@"
합니다. 배열과 위치 매개 변수의 동작에 다른 불일치가 없다는 것은 아닙니다.하지만 나에게이 세부 사항이 둘 사이에 불일치해야한다는 힌트는 없습니다.
계속,
$ arr=()
$ echo "${arr[@]}"
bash: arr[@]: unbound variable # as we've observed. BUT...
$ echo "${#arr[@]}"
0 # no error
$ echo "${!arr[@]}" | cat -e
$ # no error
그래서 우리는 요소의 개수 (0) 또는 키의 (빈) 목록을 얻을 수 arr[]
없을 정도로 바인딩 이 해제되지 않았 습니까? 나에게 이것들은 합리적이고 유용합니다. 유일한 이상 치는 ${arr[@]}
(및 ${arr[*]}
) 확장 인 것 같습니다 .