Bash에서 배열이 비어 있는지 확인하십시오.


110

스크립트가 실행될 때 다른 오류 메시지로 채워지는 배열이 있습니다.

스크립트 끝이 비어 있지 않은지 확인하고 특정 조치를 취해야합니다.

이미 일반 VAR처럼 처리하고 -z를 사용하여 확인하려고 시도했지만 작동하지 않는 것 같습니다. 배열이 비어 있는지 Bash에 있는지 확인하는 방법이 있습니까?

답변:


143

배열이이라고 가정하면 $errors요소 개수가 0인지 확인하십시오.

if [ ${#errors[@]} -eq 0 ]; then
    echo "No errors, hooray"
else
    echo "Oops, something went wrong..."
fi

10
그주의 사항 =문자열 연산자입니다. 이 경우에는 잘 작동하지만 -eq대신 적절한 산술 연산자를 사용합니다 ( -ge또는 -lt등 으로 전환하려는 경우를 대비하여 ).
musiphil

6
작동하지 않음 set -u: "언 바운드 변수"-배열이 비어있는 경우
Igor

@ Igor : Bash 4.4에서 작동합니다. set -u; foo=(); [ ${#foo[@]} -eq 0 ] && echo empty. I unset foo이면을 인쇄 foo: unbound variable하지만 다릅니다. 배열 변수는 존재하지 않고 비어 있지 않고 전혀 존재하지 않습니다.
Peter Cordes

set -u변수를 먼저 선언 하는 한 Bash 3.2 (OSX)에서도 테스트되었습니다 . 이것은 완벽하게 작동합니다.
zeroimpl

15

이 경우 일반적으로 산술 확장을 사용합니다.

if (( ${#a[@]} )); then
    echo not empty
fi

멋지고 깨끗합니다! 나는 그것을 좋아한다. 또한 배열의 첫 번째 요소가 항상 비어 있지 않으면 (첫 번째 요소의 (( ${#a} ))길이)도 작동합니다. 그러나,이 실패합니다 a=('')반면, (( ${#a[@]} ))성공이 질문에 주어진.
cxw

8

배열을 간단한 변수로 간주 할 수도 있습니다. 그런 식으로

if [ -z "$array" ]; then
    echo "Array empty"
else
    echo "Array non empty"
fi

또는 다른 쪽을 사용

if [ -n "$array" ]; then
    echo "Array non empty"
else
    echo "Array empty"
fi

해당 솔루션의 문제점은 배열이 다음과 같이 선언 된 경우 array=('' foo)입니다. 이러한 검사는 어레이가 비어있는 것으로보고되지만 명확하지는 않습니다. (@musiphil 감사합니다!)

사용 [ -z "$array[@]" ]하는 것도 해결책이 아닙니다. 중괄호를 지정하지 않으면 $array문자열 ( [@]이 경우 간단한 리터럴 문자열 임) 로 해석하려고 하므로 항상 거짓으로보고됩니다. "리터럴 문자열이 [@]비어 있습니까?" 분명히 아닙니다.


7
[ -z "$array" ]또는 [ -n "$array" ]작동하지 않습니다. 를 시도 array=('' foo); [ -z "$array" ] && echo empty하면 분명히 비어 있지 않은 empty경우에도 인쇄됩니다 array.
musiphil

2
[[ -n "${array[*]}" ]]전체 배열을 문자열로 보간하여 길이가 0이 아닌지 확인합니다. array=("" "")비어있는 요소가 두 개가 아닌 비어있는 것으로 간주 하면 유용 할 수 있습니다.
Peter Cordes

@ PetterCordes 나는 그것이 효과가 있다고 생각하지 않습니다. 이 표현식은 단일 공백 ​​문자로 평가되며 [[ -n " " ]]"true"이며 이는 유감입니다. 귀하의 의견은 정확히 내가 하고 싶은 일입니다.
마이클

@ 마이클 : 크랩, 당신 말이 맞아요. 빈 문자열의 1 요소 배열에서만 작동하며 2 요소는 아닙니다. 나는 심지어 오래된 bash를 확인했는데 여전히 잘못되었습니다. 당신이 말하는 것처럼 set -x그것이 어떻게 확장되는지 보여줍니다. 게시하기 전에 해당 주석을 테스트하지 않은 것 같습니다. >. < 확장은 요소를 IFS의 첫 문자로 분리 IFS=''하므로 설정 (이 명령문 주위에 저장 / 복원) 하여 작업을 수행 할 수 있습니다 "${array[*]}". (설정되지 않은 경우 공간). 그러나 " IFS가 null 인 경우 매개 변수는 중간 구분 기호없이 연결됩니다. "($ * 위치 매개 변수에 대한 문서이지만 배열에 대해서는 동일하다고 가정)
Peter Cordes

@ Michael : 이것을 수행하는 답변을 추가했습니다.
Peter Cordes

3

나는 그것을 확인했다 bash-4.4.0:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]} ]]; then
        echo not empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

그리고 bash-4.1.5:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]:+${array[@]}} ]]; then
        echo non-empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

후자의 경우 다음 구성이 필요합니다.

${array[@]:+${array[@]}}

비어 있거나 설정되지 않은 배열에서 실패하지 않습니다. set -eu내가 평소처럼 한다면 보다 엄격한 오류 검사를 제공합니다. 에서 워드 프로세서 :

-이자형

단일 단순 명령 (단순 명령 참조), 목록 (목록 참조) 또는 복합 명령 (복합 명령 참조)으로 구성 될 수있는 파이프 라인 (파이프 라인 참조)이 0이 아닌 상태를 반환하면 즉시 종료하십시오. 실패한 명령이 while 또는 until 키워드 바로 뒤에 나오는 명령 목록의 일부인 if 문의 테스트 부분, && 또는 ||에서 실행 된 명령의 일부인 경우 쉘이 종료되지 않습니다. 마지막 && 또는 || 뒤에 오는 명령, 파이프 라인의 마지막 명령이지만 마지막 명령 또는 명령의 반환 상태가!로 반전 된 경우를 제외하고 list. -e가 무시되는 동안 명령이 실패하여 서브 쉘 이외의 복합 명령이 0이 아닌 상태를 리턴하면 쉘이 종료되지 않습니다. 설정된 경우 ERR의 트랩은 쉘이 종료되기 전에 실행됩니다.

이 옵션은 쉘 환경과 각 서브 쉘 환경에 별도로 적용되며 (명령 실행 환경 참조) 서브 쉘에서 모든 명령을 실행하기 전에 서브 쉘이 종료 될 수 있습니다.

-e가 무시되는 상황에서 복합 명령 또는 쉘 함수가 실행되면 복합 명령 또는 함수 본문 내에서 실행 된 명령은 -e가 설정되고 명령이 a를 반환하더라도 -e 설정의 영향을받지 않습니다. 실패 상태. 복합 명령 또는 쉘 함수가 -e가 무시되는 컨텍스트에서 실행되는 동안 -e를 설정하면 복합 명령 또는 함수 호출을 포함하는 명령이 완료 될 때까지 해당 설정이 적용되지 않습니다.

-유

파라미터 확장을 수행 할 때 특수 파라미터 '@'또는 '*'이외의 설정되지 않은 변수 및 파라미터를 오류로 취급하십시오. 오류 메시지가 표준 오류에 기록되고 비 대화식 쉘이 종료됩니다.

필요하지 않은 경우 자유롭게 생략하십시오 :+${array[@]}.

또한 [[여기에서 연산자 를 사용하는 것이 중요합니다 [.

$ cat 1.sh
#!/usr/bin/env bash
set -eu
array=(a b c d)
if [ "${array[@]}" ]; then
    echo non-empty
else
    echo empty
fi

$ ./1.sh
_/1.sh: line 4: [: too many arguments
empty

으로 -u당신은 실제로 사용해야 ${array[@]+"${array[@]}"}CF stackoverflow.com/a/34361807/1237617
야쿱 Bochenski

@JakubBochenski 어떤 버전의 bash를 이야기하고 있습니까? gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9
x-yuri

단일 괄호 예제에서 문제는 @반드시입니다. *과 같은 배열 확장을 사용할 [ "${array[*]}" ]수 있습니까? 여전히 [[잘 작동합니다. 빈 문자열이 여러 개있는 배열에 대한이 두 가지의 동작은 약간 놀라운 것입니다. 모두 [ ${#array[*]} ][[ "${array[@]}" ]]에 대한 거짓 array=()array=('')하지만에 대한 사실 array=('' '')(두 개 이상의 빈 문자열). 하나 이상의 빈 문자열을 모두 true로 원하면을 사용할 수 있습니다 [ ${#array[@]} -gt 0 ]. 당신이 그들 모두를 거짓으로 원한다면, 당신은 아마 //그들을 밖으로 할 수 있습니다.
eisd

@eisd을 사용할 수는 [ "${array[*]}" ]있지만 그러한 표현에 빠지면 그것이 무엇을 이해하는지가 더 어려워집니다. 이후는 [...]보간의 결과에 문자열의 관점에서 작동한다. 반대로 [[...]]보간 된 것을 알 수 있습니다. 즉, 배열이 전달되었음을 알 수 있습니다. [[ ${array[@]} ]]"배열이 비어 있지 않은지 확인"으로, [ "${array[*]}" ]"모든 배열 요소의 보간 결과가 비어 있지 않은 문자열 인지 확인"으로 읽습니다 .
x-yuri

... 두 개의 빈 문자열이있는 행동은 전혀 놀랍지 않습니다. 놀라운 것은 하나의 빈 문자열이있는 동작입니다. 그러나 틀림없이 합리적입니다. 에 관해서는 [ ${#array[*]} ], 아마도 [ "${array[*]}" ]많은 요소에 대해 전자가 사실이기 때문에 아마도 의미했을 것입니다 . 요소 수는 항상 비어 있지 않은 문자열이기 때문입니다. 두 개의 요소가있는 후자에 대해서는 괄호 안의 표현식 ' '이 비어 있지 않은 문자열로 확장됩니다 . 에 관해서는 [[ ${array[@]} ]], 그들은 두 요소의 배열이 비어 있지 않다고 생각합니다.
x-yuri

2

당신은 빈 배열을 검색하도록하려면 요소 처럼,arr=("" "") 등, 빈과 동일arr=()

모든 요소를 ​​붙여넣고 결과 길이가 0인지 확인할 수 있습니다. (배열이 매우 클 수 있으면 배열 내용의 병합 사본을 작성하는 것은 성능에 이상적이지 않습니다. 그러나 그런 프로그램에 bash를 사용하지 않기를 바랍니다 ...)

그러나 "${arr[*]}"의 첫 문자로 구분 된 요소로 확장됩니다 IFS. 따라서 IFS를 저장 / 복원 IFS=''하고이 작업을 수행해야 합니다. 그렇지 않으면 문자열 길이 == # 배열 요소 수-1 ( n요소 배열에 n-1구분 기호가 있음)을 확인해야합니다. 한 번에 하나씩 처리하려면 연결을 1만큼 채우는 것이 가장 쉽습니다.

arr=("" "")

## Assuming default non-empty IFS
## TODO: also check for ${#arr[@]} -eq 0
concat="${arr[*]} "      # n-1 separators + 1 space + array elements
[[ "${#concat}" -ne "${#arr[@]}" ]]  && echo not empty array || echo empty array

테스트 케이스 set -x

### a non-empty element
$ arr=("" "x")
  + arr=("" "x")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat=' x '
  + [[ 3 -ne 2 ]]
  + echo not empty array
not empty array

### 2 empty elements
$ arr=("" "")
  + arr=("" "")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat='  '
  + [[ 2 -ne 2 ]]
  + echo empty array
empty array

불행히도 이것은 실패합니다 arr=(): [[ 1 -ne 0 ]]. 따라서 실제로 빈 배열을 먼저 별도로 확인해야합니다.


또는IFS='' . 아마도 서브 쉘을 사용하는 대신 IFS를 저장 / 복원하고 싶을 것입니다. 서브 쉘에서 쉽게 결과를 얻을 수 없기 때문입니다.

# inside a () subshell so we don't modify our own IFS
(IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)

예:

$ arr=("" "")
$ (IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)
   + IFS=
   + [[ -n '' ]]
   + echo empty array
empty array

않습니다 와 함께 작동 arr=()- 그것은 여전히 빈 문자열입니다.


[[ "${arr[*]}" = *[![:space:]]* ]]비공식적 이지만 적어도 하나의 비 WS 문자를 사용할 수 있기 때문에를 사용하기 시작했습니다 .
Michael

@Michael : yup, 거부 할 필요가없는 경우 좋은 옵션 arr=(" ")입니다.
Peter Cordes

0

제 경우 에는 공백이있을 수 있기 때문에 두 번째 대답으로 는 충분하지 않았습니다. 나는 함께했다 :

if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "No options"
else
  echo "Options found"
fi

echo | wc쉘 내장을 사용하는 것에 비해 불필요하게 비효율적 인 것 같습니다.
Peter Cordes

@PeterCordes를 이해하고 있는지 확실하지 않은 경우 [ ${#errors[@]} -eq 0 ];공백 문제를 해결하기 위해 두 번째 답변을 수정할 수 있습니까? 또한 내장 기능을 선호합니다.
Micha

공백이 정확히 문제를 일으키는 원인은 무엇입니까? $#숫자로 확장되고 이후에도 잘 작동합니다 opts+=(""). 예를 들어, unset opts; opts+=("");opts+=(" "); echo "${#opts[@]}"내가 얻을 2. 작동하지 않는 예를 보여줄 수 있습니까?
Peter Cordes

오래 전입니다. 원본 출처 IIRC는 항상 ""이상을 인쇄했습니다. 따라서 opts = ""또는 opts = ( "")의 경우 빈 줄 바꿈이나 빈 문자열을 무시하고 1이 아닌 0이 필요했습니다.
Micha

좋아, 그래서 당신 opts=("")은 같은 취급해야 opts=()합니까? 그것은 빈 배열이 아니지만 빈 배열이나 빈 첫 번째 요소를 확인할 수 opts=(""); [[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty있습니다. 귀하의 현재 답변에는에 대한 "옵션 없음"이라고 표시되어 opts=("" "-foo")있으며 이는 완전히 가짜이며 이는 해당 동작을 재현합니다. 당신은 수 [[ -z "${opts[*]}" ]]I는 평면 문자열로 모든 배열 요소 보간 추측 -z길이가 0이 아닌를 확인합니다. 첫 번째 요소를 검사하면 충분 -z "$opts"합니다.
Peter Cordes

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.