`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의 설명으로 인해 태그가 제거 되었습니다.
문서에 따르면
첨자에 값이 할당 된 경우 배열 변수가 설정된 것으로 간주됩니다. 널 문자열은 유효한 값입니다.
값이 할당 된 첨자가 없으므로 배열이 설정되지 않습니다.
그러나 문서는 여기에 오류가 적절하다고 제안하지만 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...
이것은 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
최근 릴리스 (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
참으로 "흥미로운"불일치.
더욱이,
$ 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[@]}
아무것도 확장되지 않습니다.
which is entirely consistent with what it says about the expansion of positional parameters in "$@"
. Not that there aren't other inconsistencies in the behaviors of arrays and positional parameters... but to me there's no hint that this detail should be inconsistent between the two.
Continuing,
$ arr=()
$ echo "${arr[@]}"
bash: arr[@]: unbound variable # as we've observed. BUT...
$ echo "${#arr[@]}"
0 # no error
$ echo "${!arr[@]}" | cat -e
$ # no error
So arr[]
isn't so unbound that we can't get a count of its elements (0), or a (empty) list of its keys? To me these are sensible, and useful -- the only outlier seems to be the ${arr[@]}
(and ${arr[*]}
) expansion.
@ikegami's answer is correct, but I consider the syntax "${arr[@]:+${arr[@]}}"
dreadful. If you use long array variable names, it starts to looks spaghetti-ish quicker than usual.
Try this instead:
$ 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
It looks like the Bash array slice operator is very forgiving.
So why did Bash make handling the edge case of arrays so difficult? Sigh. I cannot guarantee you version will allow such abuse of the array slice operator, but it works dandy for me.
Caveat: I am using GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Your mileage may vary.
Here are a couple of ways to do something like this, one using sentinels and another using conditional appends:
#!/bin/bash
set -o nounset -o errexit -o pipefail
countArgs () { echo "$#"; }
arrA=( sentinel )
arrB=( sentinel "{1..5}" "./*" "with spaces" )
arrC=( sentinel '$PWD' )
cmnd=( countArgs "${arrA[@]:1}" "${arrB[@]:1}" "${arrC[@]:1}" )
echo "${cmnd[@]}"
"${cmnd[@]}"
arrA=( )
arrB=( "{1..5}" "./*" "with spaces" )
arrC=( '$PWD' )
cmnd=( countArgs )
# Checks expansion of indices.
[[ ! ${!arrA[@]} ]] || cmnd+=( "${arrA[@]}" )
[[ ! ${!arrB[@]} ]] || cmnd+=( "${arrB[@]}" )
[[ ! ${!arrC[@]} ]] || cmnd+=( "${arrC[@]}" )
echo "${cmnd[@]}"
"${cmnd[@]}"
I am complementing on @ikegami's (accepted) and @kevinarpe's (also good) answers.
You can do "${arr[@]:+${arr[@]}}"
to workaround the problem. The right-hand-side (i.e., after :+
) provides an expression that will be used in case the left-hand-side is not defined/null.
The syntax is arcane. Note that the right hand side of the expression will undergo parameter expansion, so extra attention should be paid to having consistent quoting.
: example copy arr into arr_copy
arr=( "1 2" "3" )
arr_copy=( "${arr[@]:+${arr[@]}}" ) # good. same quoting.
# preserves spaces
arr_copy=( ${arr[@]:+"${arr[@]}"} ) # bad. quoting only on RHS.
# copy will have ["1","2","3"],
# instead of ["1 2", "3"]
Like @kevinarpe mentions, a less arcane syntax is to use the array slice notation ${arr[@]:0}
(on Bash versions >= 4.4
), which expands to all the parameters, starting from index 0. It also doesn't require as much repetition. This expansion works regardless of set -u
, so you can use this at all times. The man page says (under Parameter Expansion):
${parameter:offset}
${parameter:offset:length}
... If parameter is an indexed array name subscripted by
@
or*
, the result is the length members of the array beginning with${parameter[offset]}
. A negative offset is taken relative to one greater than the maximum index of the specified array. It is an expansion error if length evaluates to a number less than zero.
This is the example provided by @kevinarpe, with alternate formatting to place the output in evidence:
set -u
function count() { echo $# ; };
(
count x y z
)
: prints "3"
(
arr=()
count "${arr[@]}"
)
: prints "-bash: arr[@]: unbound variable"
(
arr=()
count "${arr[@]:0}"
)
: prints "0"
(
arr=(x y z)
count "${arr[@]:0}"
)
: prints "3"
This behaviour varies with versions of Bash. You may also have noticed that the length operator ${#arr[@]}
will always evaluate to 0
for empty arrays, regardless of set -u
, without causing an 'unbound variable error'.
Interesting inconsistency; this lets you define something which is "not considered set" yet shows up in the output of declare -p
arr=()
set -o nounset
echo $arr[@]
=> -bash: arr[@]: unbound variable
declare -p arr
=> declare -a arr='()'
The most simple and compatible way seems to be:
$ set -u
$ arr=()
$ echo "foo: '${arr[@]-}'"
참고URL : https://stackoverflow.com/questions/7577052/bash-empty-array-expansion-with-set-u
'Programing' 카테고리의 다른 글
일반적으로 좋은 인덱스를 만드는 열은 무엇입니까? (0) | 2020.09.16 |
---|---|
Mac에서 .c 파일을 어떻게 컴파일합니까? (0) | 2020.09.16 |
Razor MVC 모델 배열로 자바 스크립트 배열 채우기 (0) | 2020.09.15 |
Visual Studio 2017-파일 또는 어셈블리 'System.Runtime, Version = 4.1.0.0'또는 해당 종속성 중 하나를로드 할 수 없습니다. (0) | 2020.09.15 |
Gradle을 사용할 때 전이 종속성의 모든 인스턴스를 제외하려면 어떻게해야합니까? (0) | 2020.09.15 |