bash에서 element가 배열에 있는지 테스트하십시오.


17

배열에 bash에 요소가 있는지 확인하는 좋은 방법이 있습니까?

또는 숫자 또는 문자열이 사전 정의 된 상수 세트와 같은지 확인하는 다른 방법이 있습니까?

답변:


24

Bash 4에서는 연관 배열을 사용할 수 있습니다.

# set up array of constants
declare -A array
for constant in foo bar baz
do
    array[$constant]=1
done

# test for existence
test1="bar"
test2="xyzzy"

if [[ ${array[$test1]} ]]; then echo "Exists"; fi    # Exists
if [[ ${array[$test2]} ]]; then echo "Exists"; fi    # doesn't

처음에 어레이를 설정하려면 직접 할당을 수행 할 수도 있습니다.

array[foo]=1
array[bar]=1
# etc.

또는 이런 식으로 :

array=([foo]=1 [bar]=1 [baz]=1)

실제로 값이 비어있는 경우 [[]] 테스트가 작동하지 않습니다. 예를 들어 "array [ 'test'] = ''"입니다. 이 경우 키 'test'가 존재하며 $ {! array [@]}와 함께 나열되지만 "[[$ {array [ 'test']}]]; echo $?"로 표시됩니다. 에코 1이 아닌 0
haridsv

1
${array[$test1]}간단하지만 문제가 있습니다. set -u"언 바운드 변수"가 표시되므로 스크립트에서 사용하면 (권장) 작동하지 않습니다 .
tokland

@tokland : 누가 추천하나요? 나는 확실히하지 않습니다.
추후 공지가있을 때까지 일시 중지되었습니다.

@DennisWilliamson : 좋아, 어떤 사람들 은 그것을 추천하지만,이 플래그의 가치에 관계없이 작동하는 솔루션을 갖는 것이 좋을 것이라고 생각합니다.
tokland


10

오래된 질문이지만 가장 간단한 해결책은 아직 나타나지 않았다고 생각합니다 test ${array[key]+_}. 예:

declare -A xs=([a]=1 [b]="")
test ${xs[a]+_} && echo "a is set"
test ${xs[b]+_} && echo "b is set"
test ${xs[c]+_} && echo "c is set"

출력 :

a is set
b is set

이 작업 방법을 확인하려면 이것을 확인 하십시오 .


2
정보 매뉴얼 env에서는 별명, 프로그 및 "test"라는 이름을 채택한 기타 기능의 모호성을 피하기 위해 사용할 것을 권장합니다. 위와 같이 env test ${xs[a]+_} && echo "a is set". 또한 이중 브래킷을 사용하여이 기능을 사용할 수 있으며, 다음과 같은 트릭으로 null을 검사합니다.[[ ! -z "${xs[b]+_}" ]] && echo "b is set"
A.Danischewski

또한보다 간단한[[ ${xs[b]+set} ]]
Arne L을

5

연관 배열의 요소가 존재하는지 (설정되지 않은) 테스트하는 방법이 있는데 이는 비어있는 것과 다릅니다.

isNotSet() {
    if [[ ! ${!1} && ${!1-_} ]]
    then
        return 1
    fi
}

그런 다음 사용하십시오.

declare -A assoc
KEY="key"
isNotSet assoc[${KEY}]
if [ $? -ne 0 ]
then
  echo "${KEY} is not set."
fi

그냥 참고 : 선언-A는 bash 3.2.39 (데비안 레니)에서는 작동하지 않지만 bash 4.1.5 (데비안 압착)에서는 작동합니다.
Paweł Polewicz

연관 배열은 배쉬 4에 도입
샌디에고 F. 듀란에게

1
주의 if ! some_check then return 1=을 some_check. 따라서 : isNotSet() { [[ ... ]] }. 아래의 솔루션을 확인하십시오. 간단한 확인으로 해결할 수 있습니다.
tokland

3

어레이의 내용을 grep으로 파이프하여 항목이 있는지 확인할 수 있습니다.

 printf "%s\n" "${mydata[@]}" | grep "^${val}$"

grep -n을 사용하여 항목의 색인을 가져올 수도 있습니다. 일치 항목의 행 번호를 반환합니다 (0부터 시작하는 색인을 얻으려면 1을 빼야 함). 매우 큰 배열을 제외하고는 상당히 빠릅니다.

# given the following data
mydata=(a b c "hello world")

for val in a c hello "hello world"
do
           # get line # of 1st matching entry
    ix=$( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 )

    if [[ -z $ix ]]
    then
        echo $val missing
    else
         # subtract 1.  Bash arrays are zero-based, but grep -n returns 1 for 1st line, not 0 
        echo $val found at $(( ix-1 ))
    fi
done

a found at 0
c found at 2
hello missing
hello world found at 3

설명:

  • $( ... ) 백틱을 사용하여 명령의 출력을 변수로 캡처하는 것과 동일
  • printf mydata를 한 줄에 한 요소 씩 출력
  • ( 이 @대신에 필요한 모든 따옴표 *.는 "hello world"를 2 줄로 나누지 않도록합니다)
  • grep정확한 문자열을 검색하고 줄의 시작과 끝 ^$일치
  • grep -n 4 : hello world 형식으로 라인 #을 리턴합니다.
  • grep -m 1 첫 번째 경기 만 찾습니다
  • cut 줄 번호 만 추출
  • 리턴 된 행 번호에서 1을 뺍니다.

물론 빼기를 명령으로 접을 수 있습니다. 그러나 -1이 없는지 테스트하십시오.

ix=$(( $( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 ) - 1 ))

if [[ $ix == -1 ]]; then echo missing; else ... fi
  • $(( ... )) 정수 산술

1

배열에 데이터 가 매우 제한되어 있지 않으면 반복하지 않고 제대로 수행 할 수 있다고 생각하지 않습니다 .

다음은 간단한 변형 "Super User"입니다. 배열에 존재 한다고 올바르게 말합니다 . 그러나 그것은 또한"uper Use" 배열에 입니다.

MyArray=('Super User' 'Stack Overflow' 'Server Fault' 'Jeff' );
FINDME="Super User"

FOUND=`echo ${MyArray[*]} | grep "$FINDME"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

#
# If you where to add anchors < and > to the data it could work
# This would find "Super User" but not "uper Use"
#

MyArray2=('<Super User>' '<Stack Overflow>' '<Server Fault>' '<Jeff>' );

FOUND=`echo ${MyArray2[*]} | grep "<$FINDME>"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

문제는 배열을 반복하는 것 외에도 앵커를 쉽게 추가 할 수있는 방법이 없다는 것입니다. 배열에 넣기 전에 추가 할 수 없다면 ...


그래도 상수가 영숫자 인 경우 좋은 해결책입니다 (with grep "\b$FINDME\b"). 아마 비 영숫자와 공백이없는 상수와 함께 일할 수 "(^| )$FINDME(\$| )"(나는 정규 표현식 그렙 사용 어떤 맛을 배울 수 없었습니다 ... 그런이나 뭐.)
TGR

1
#!/bin/bash
function in_array {
  ARRAY=$2
  for e in ${ARRAY[*]}
  do
    if [[ "$e" == "$1" ]]
    then
      return 0
    fi
  done
  return 1
}

my_array=(Drupal Wordpress Joomla)
if in_array "Drupal" "${my_array[*]}"
  then
    echo "Found"
  else
    echo "Not found"
fi

1
이 방법을 제안하는 이유를 자세히 설명 할 수 있습니까? 그것을 할 수있는 방법이 있는지 영업 이익은 질문 배열을 반복하지 않고 당신이하고있는 무엇, in_array. 건배
bertieb

글쎄, 적어도 그 루프는 함수로 캡슐화되어있어 많은 경우 (작은 데이터 세트)에 충분할 수 있으며 bash 4 이상이 필요하지 않습니다. 아마 사용 ${ARRAY[@]}되어야합니다.
Tobias
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.