Bash 배열에 값이 포함되어 있는지 확인


443

Bash에서 배열에 특정 값이 포함되어 있는지 테스트하는 가장 간단한 방법은 무엇입니까?

편집 : 답변과 의견의 도움으로 몇 가지 테스트 후에 다음과 같은 결과를 얻었습니다.

function contains() {
    local n=$#
    local value=${!n}
    for ((i=1;i < $#;i++)) {
        if [ "${!i}" == "${value}" ]; then
            echo "y"
            return 0
        fi
    }
    echo "n"
    return 1
}

A=("one" "two" "three four")
if [ $(contains "${A[@]}" "one") == "y" ]; then
    echo "contains one"
fi
if [ $(contains "${A[@]}" "three") == "y" ]; then
    echo "contains three"
fi

그것이 최상의 솔루션인지 확실하지 않지만 작동하는 것 같습니다.

답변:


458

이 방법은 모든 요소를 ​​반복 할 필요가 없다는 장점이 있습니다 (적어도 명시 적으로는 아님). 하지만 이후array_to_string_internal()array.c 여전히 문자열로 배열 요소와 서로 연결하여 그들을 통해 루프, 그것은 루핑 솔루션을 제안보다 아마 더 효율적 아니지만, 더 많은 읽을 수 있습니다.

if [[ " ${array[@]} " =~ " ${value} " ]]; then
    # whatever you want to do when array contains value
fi

if [[ ! " ${array[@]} " =~ " ${value} " ]]; then
    # whatever you want to do when array doesn't contain value
fi

검색하는 값이 공백이있는 배열 요소의 단어 중 하나 인 경우 오탐 (false positive)이 표시됩니다. 예를 들어

array=("Jack Brown")
value="Jack"

정규식은 배열에 "Jack"이없는 것으로 간주합니다. 따라서 변경해야합니다IFS 솔루션을 계속 사용하려면 정규식에서 구분 기호 문자 합니다.

IFS=$'\t'
array=("Jack Brown\tJack Smith")
unset IFS
value="Jack"

if [[ "\t${array[@]}\t" =~ "\t${value}\t" ]]; then
    echo "true"
else
    echo "false"
fi

"false"가 인쇄됩니다.

분명히 이것은 또한 하나의 라이너로 표현 될 수 있도록 테스트 문으로 사용될 수 있습니다

[[ " ${array[@]} " =~ " ${value} " ]] && echo "true" || echo "false"

1
첫 번째 정규 표현식 값 일치 시작 부분에 공백을 추가하여 단어로 끝나는 것이 아니라 단어와 만 일치하도록했습니다. 잘 작동합니다. 그러나 두 번째 조건을 사용하는 이유를 이해하지 못합니다. 첫 번째로는 잘 작동하지 않습니까?
JStrahl

1
@AwQiruiGuo 팔로우하고 있는지 잘 모르겠습니다. 달러 리터럴이있는 배열에 대해 이야기하고 있습니까? 그렇다면 백 슬래시와 일치하는 값으로 달러를 피하십시오.
Keegan

10
Oneliner : [[ " ${branches[@]} " =~ " ${value} " ]] && echo "YES" || echo "NO";
ericson.cepeda

3
Shellcheck는이 솔루션, SC2199 및 SC2076에 대해 불평합니다. 기능을 중단하지 않고 경고를 수정할 수 없습니다. 해당 줄에 대해 shellcheck를 비활성화하는 것 이외의 다른 생각이 있습니까?
Ali Essam 2016 년

4
SC2076 은 쉽게 고칠 수 if있습니다. 에서 큰 따옴표를 제거하면 됩니다. 이 접근법으로 SC2199 를 피할 수있는 방법이 없다고 생각 합니다. 다른 솔루션 중 일부에 표시된 것처럼 배열을 통해 명시 적으로 루프하거나 경고를 무시해야 합니다.
키건

388

아래는이를 달성하기위한 작은 기능입니다. 검색 문자열은 첫 번째 인수이고 나머지는 배열 요소입니다.

containsElement () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 0; done
  return 1
}

해당 기능의 테스트 실행은 다음과 같습니다.

$ array=("something to search for" "a string" "test2000")
$ containsElement "a string" "${array[@]}"
$ echo $?
0
$ containsElement "blaha" "${array[@]}"
$ echo $?
1

5
잘 작동합니다! 따옴표와 같이 배열을 전달해야한다는 것을 기억해야합니다 "${array[@]}". 그렇지 않으면 공백이 포함 된 요소가 기능을 중단합니다.
Juve

26
좋은. 첫 번째 인수가 두 번째 인수에 포함되어 있는지 확인하기 때문에 elementIn ()이라고합니다. containsElements ()는 배열이 먼저가는 것처럼 들립니다. 나와 같은 초보자에게는 "if"문에서 stdout에 쓰지 않는 함수를 사용하는 방법에 대한 예가 도움이 될 것입니다. 도와 if elementIn "$table" "${skip_tables[@]}" ; then echo skipping table: ${table}; fi; 주셔서 감사합니다!
GlenPeterson

5
@Bluz && 구문은 부울 AND 연산자입니다. 부울 연산자를 사용하면 부울 명령문이 작성됩니다. 부울 논리는 && 앞뒤에있는 명령문이 모두 true로 평가되는 경우 전체 명령문이 참일 수 있다고 말합니다. 테스트가 평가되고 false 인 경우 테스트가 실패하고 실행되지 않은 경우 전체 명령문과 관련이 없으므로 리턴을 평가할 필요가 없습니다. 테스트가 성공하면 부울 명령문이 성공하면 리턴 결과를 판별하여 코드가 실행되도록 요구합니다.
peteches

4
@James 관례에 따르면 bash의 성공 코드는 "0"이고 오류는 모든> = 1입니다. 이것이 성공하면 0을 반환하는 이유입니다. :)
tftd November

11
@Stelios shift는 인수 목록을 1 씩 왼쪽으로 이동하고 (첫 번째 인수는 삭제함 ) 암시 적으로 인수 목록을 반복 for하지 않습니다 in.
Christian

58
$ myarray=(one two three)
$ case "${myarray[@]}" in  *"two"*) echo "found" ;; esac
found

69
이것은 배열의 각 요소를 개별적으로 반복하지는 않습니다. 대신 배열을 연결하고 "2"를 하위 문자열로 일치시킵니다. 정확한 단어 "2"가 배열의 요소인지 테스트하는 경우 바람직하지 않은 동작이 발생할 수 있습니다.
MartyMacGyver

파일 형식을 비교할 때 이것이 효과가 있다고 생각했지만 카운터가 증가함에 따라 너무 많은 값을 세고 있음을 알았습니다.
Mike Q

17
잘못된! 이유 : case "${myarray[@]}" in *"t"*) echo "found" ;; esac출력 :found
Sergej Jevsejev

@MartyMacGyver,이 답변에 대한 추가 내용을
보시겠습니까?

45
for i in "${array[@]}"
do
    if [ "$i" -eq "$yourValue" ] ; then
        echo "Found"
    fi
done

문자열의 경우 :

for i in "${array[@]}"
do
    if [ "$i" == "$yourValue" ] ; then
        echo "Found"
    fi
done

즉, 인덱스 된 for 루프를 사용하고 배열 요소에 IFS가 포함 된 경우 종료되지 않을 수 있습니다. for ((i = 0; i <$ {# array [@]}; i ++))
mkb

@Matt : ${#}Bash는 희소 배열을 지원하므로 주의해서 사용해야 합니다.
추후 공지가있을 때까지 일시 중지되었습니다.

@Paolo, 배열에 공백이 있으면 문자열로 비교하십시오. 공백도 문자열입니다.
Scott

@Paolo : 함수를 만들 수 있지만 배열을 인수로 전달할 수 없으므로 전역으로 취급해야합니다.
추후 공지가있을 때까지 일시 중지되었습니다.

데니스 말이 맞아 bash 참조 매뉴얼에서 : "단어가 큰 따옴표로 묶이면 ... $ {name [@]}은 이름의 각 요소를 별도의 단어로 확장합니다"
mkb

37

단선 솔루션

printf '%s\n' ${myarray[@]} | grep -P '^mypattern$'

설명

그만큼 printf문은 별도의 행에 각 배열 요소를 출력한다.

grep문은 특수 문자를 사용 ^하고 $이 들어있는 라인을 찾을 수 없습니다 정확히 로 주어진 패턴을 mypattern(더 이상, 더 이하).


용법

이것을 if ... then진술에 넣으려면 :

if printf '%s\n' ${myarray[@]} | grep -q -P '^mypattern$'; then
    # ...
fi

일치하는 항목이 인쇄되지 않도록 표현식에 -q플래그를 추가했습니다 grep. 일치하는 항목을 "true"로 취급합니다.


좋은 해결책! GNU grep에는 "-line-regexp"도 있는데 "-P"와 ^와 $를 패턴으로 바꿀 수 있습니다 : printf '% s \ n'$ {myarray [@]} | 그렙 -q --line-정규 표현식 'mypattern'
presto8

19

성능이 필요한 경우 검색 할 때마다 전체 배열을 반복하고 싶지 않습니다.

이 경우 해당 배열의 인덱스를 나타내는 연관 배열 (해시 테이블 또는 사전)을 만들 수 있습니다. 즉, 각 배열 요소를 배열의 인덱스에 매핑합니다.

make_index () {
  local index_name=$1
  shift
  local -a value_array=("$@")
  local i
  # -A means associative array, -g means create a global variable:
  declare -g -A ${index_name}
  for i in "${!value_array[@]}"; do
    eval ${index_name}["${value_array[$i]}"]=$i
  done
}

그런 다음 다음과 같이 사용할 수 있습니다.

myarray=('a a' 'b b' 'c c')
make_index myarray_index "${myarray[@]}"

다음과 같이 멤버십을 테스트하십시오.

member="b b"
# the "|| echo NOT FOUND" below is needed if you're using "set -e"
test "${myarray_index[$member]}" && echo FOUND || echo NOT FOUND

또는 :

if [ "${myarray_index[$member]}" ]; then 
  echo FOUND
fi

테스트 된 값이나 배열 값에 공백이 있어도이 솔루션은 올바른 작업을 수행합니다.

보너스로 다음을 사용하여 배열 내 값의 색인을 얻습니다.

echo "<< ${myarray_index[$member]} >> is the index of $member"

연관 배열을 사용해야한다는 생각으로 +1입니다. 나는 코드 make_index가 간접적으로 인해 조금 더 고안 되었다고 생각한다 . 훨씬 간단한 코드로 고정 배열 이름을 사용할 수 있습니다.
musiphil

17

나는 일반적으로 다음을 사용합니다.

inarray=$(echo ${haystack[@]} | grep -o "needle" | wc -w)

0이 아닌 값은 일치가 발견되었음을 나타냅니다.


사실, 이것은 가장 쉬운 해결책입니다. 제 의견으로는 대답해야합니다. 최소한 내 공감대를 가져라! [:
ToVine

2
비슷한 바늘에는 효과가 없습니다. 예를 들어haystack=(needle1 needle2); echo ${haystack[@]} | grep -o "needle" | wc -w
Keegan

1
매우 사실입니다. 어떤 요소에도없는 구분자와 결합하여 바늘에 추가하면 도움이 될 것입니다. 아마도 ... ( inarray=$(printf ",%s" "${haystack[@]}") | grep -o ",needle" | wc -w)
unested

2
grep -x를 사용하면 오 탐지를 피할 수 있습니다. inarray=$(printf ",%s" "${haystack[@]}") | grep -x "needle" | wc -l
jesjimher

아마도 inarray=$(echo " ${haystack[@]}" | grep -o " needle" | wc -w)-x가 grep이 전체 입력 문자열과 일치하도록 시도하는 것처럼 보일 수 있습니다.
MI Wright

17

함수가없는 또 다른 라이너 :

(for e in "${array[@]}"; do [[ "$e" == "searched_item" ]] && exit 0; done) && echo "found" || echo "not found"

공간에 대해 머리를 숙여 주셔서 감사합니다 @ Qwerty!

해당 기능 :

find_in_array() {
  local word=$1
  shift
  for e in "$@"; do [[ "$e" == "$word" ]] && return 0; done
  return 1
}

예:

some_words=( these are some words )
find_in_array word "${some_words[@]}" || echo "expected missing! since words != word"

1
왜 여기에 서브 쉘이 필요한가요?
codeforester

1
@ codeforester 이것은 오래되었지만 ... 쓰여졌을 때 그것을 깨기 위해서는 그것이 필요합니다 exit 0.
estani

하나의 라이너의 단부되어야 || echo not found대신 || not found또는 쉘의 이름으로 명령을 실행하려고 할 하지 인자로 발견 요청 된 값이 배열되지 않은 경우.
zoke

11
containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }

이제 빈 배열을 올바르게 처리합니다.


@patrik의 답변과 다른 점은 무엇입니까? 내가 보는 유일한 차이점은 "$e" = "$1"(대신 "$e" == "$1") 버그처럼 보입니다.
CivFan

1
그렇지 않습니다. @patrik은 당시의 원래 답변 (패치 # 4)에 내 의견을 병합했습니다. 참고 : "e" == "$1"구문 상 더 명확합니다.
Yann

@CivFan 현재 형태에서 이것은 우아한 $ {@ : 2}와 자체 문서화 $ 1로 인해 patrik의 답변보다 짧습니다. [[]] 내에서 인용이 필요하지 않다고 덧붙입니다.
Hontvári Levente

9

다음은 약간의 기여입니다.

array=(word "two words" words)  
search_string="two"  
match=$(echo "${array[@]:0}" | grep -o $search_string)  
[[ ! -z $match ]] && echo "found !"  

참고 :이 방법은 대소 문자 "두 단어"를 구별하지 않지만 질문에는 필요하지 않습니다.


이것은 저에게 많은 도움이되었습니다. 감사!
Ed Manet 2019

이 질문은 명시 적으로 정답을 제공해야한다고 말하지는 않았지만 질문에 암시 적이라고 생각합니다 ... 배열에 "two"값이 포함되어 있지 않습니다.
tetsujin

위의 내용은 'rd'에 대한 일치를보고합니다.
Noel Yap

6

정확한 배열을 얻기 위해 전체 배열을 반복 할 가치가 있는지 확인하기 위해 빠르고 더러운 테스트를 수행하려는 경우 Bash는 배열을 스칼라처럼 처리 할 수 ​​있습니다. 스칼라에서 일치를 테스트 한 후 루프를 건너 뛰면 시간이 절약됩니다. 분명히 당신은 오 탐지를 얻을 수 있습니다.

array=(word "two words" words)
if [[ ${array[@]} =~ words ]]
then
    echo "Checking"
    for element in "${array[@]}"
    do
        if [[ $element == "words" ]]
        then
            echo "Match"
        fi
    done
fi

"Checking"및 "Match"가 출력됩니다. 로 array=(word "two words" something)단지 출력 "확인"을합니다. 으로 array=(word "two widgets" something)출력이 없습니다.


전체 문자열과 만 일치 words하는 정규 표현식으로 바꾸면 왜 ^words$각 항목을 개별적으로 검사 할 필요가 없습니까?
Dejay Clayton

@DejayClayton : 스칼라 인 것처럼 전체 배열을 한 번에 pattern='^words$'; if [[ ${array[@]} =~ $pattern ]]확인하므로 일치하지 않기 때문 입니다. 내 답변의 개별 확인은 대략 일치하는 결과를 바탕으로 진행 해야하는 이유가있는 경우에만 수행해야합니다.
추후 공지가있을 때까지 일시 중지되었습니다.

아, 당신이하려는 일을 봅니다. 더 성능적이고 안전한 변형 답변을 제안했습니다.
Dejay Clayton

6

이것은 나를 위해 일하고 있습니다 :

# traditional system call return values-- used in an `if`, this will be true when returning 0. Very Odd.
contains () {
    # odd syntax here for passing array parameters: http://stackoverflow.com/questions/8082947/how-to-pass-an-array-to-a-bash-function
    local list=$1[@]
    local elem=$2

    # echo "list" ${!list}
    # echo "elem" $elem

    for i in "${!list}"
    do
        # echo "Checking to see if" "$i" "is the same as" "${elem}"
        if [ "$i" == "${elem}" ] ; then
            # echo "$i" "was the same as" "${elem}"
            return 0
        fi
    done

    # echo "Could not find element"
    return 1
}

호출 예 :

arr=("abc" "xyz" "123")
if contains arr "abcx"; then
    echo "Yes"
else
    echo "No"
fi

5
a=(b c d)

if printf '%s\0' "${a[@]}" | grep -Fqxz c
then
  echo 'array “a” contains value “c”'
fi

원하는 경우 동등한 긴 옵션을 사용할 수 있습니다.

--fixed-strings --quiet --line-regexp --null-data

1
--null-data가 없으므로 Mac의 BSD-grep에서는 작동하지 않습니다. :(

4

Dennis Williamson답변 에서 빌린 다음 솔루션은 배열, 쉘 안전 인용 및 정규 표현식을 결합하여 필요성을 피합니다. 반복 루프 반복; 파이프 또는 다른 하위 프로세스를 사용하는 것; 또는 비 -bash 유틸리티를 사용하십시오.

declare -a array=('hello, stack' one 'two words' words last)
printf -v array_str -- ',,%q' "${array[@]}"

if [[ "${array_str},," =~ ,,words,, ]]
then
   echo 'Matches'
else
   echo "Doesn't match"
fi

위의 코드는 Bash 정규 표현식을 사용하여 문자열 화 된 버전의 배열 내용과 일치시킵니다. 배열 내에서 영리한 값 조합으로 정규 표현식 일치를 속일 수 없도록하는 6 가지 중요한 단계가 있습니다.

  1. Bash의 내장 printf쉘 인용 을 사용하여 비교 문자열을 구성하십시오 %q. 쉘 인용은 역 슬래시로 이스케이프 처리하여 특수 문자가 "쉘 안전"이되도록합니다 \.
  2. 값 구분 기호로 사용할 특수 문자를 선택하십시오. 구분 기호는 사용할 때 이스케이프 될 특수 문자 중 하나 여야합니다 %q. 이것이 정규 표현식 일치를 속이는 현명한 방법으로 배열 내의 값을 구성 할 수 없도록 보장하는 유일한 방법입니다. 쉼표를 선택합니다,예상치 못한 방식으로 평가하거나 오용 할 때 해당 문자가 가장 안전하기 때문에 .
  3. 다음을 사용하여 모든 배열 요소를 단일 문자열로 결합 구분 기호로 사용할 특수 문자의 인스턴스를 . 예를 들어 쉼표를 사용 ,,%q하여에 대한 인수로 사용 했습니다 printf. 특수 문자의 두 인스턴스가 분리 문자로 나타날 때 나란히 나타날 수 있기 때문에 이것은 중요합니다. 특수 문자의 다른 모든 인스턴스는 이스케이프됩니다.
  4. 배열의 마지막 요소와 일치하도록 구분 기호의 후행 인스턴스 두 개를 문자열에 추가하십시오. 따라서 비교하는 대신${array_str} , 비교 대상 ${array_str},,.
  5. 검색하려는 대상 문자열이 사용자 변수에 의해 제공되는 경우 경우 특수 문자의 모든 인스턴스를 백 슬래시로 이스케이프 해야합니다 . 그렇지 않으면 정규 표현식 일치가 영리하게 만들어진 배열 요소에 속이기 쉽습니다.
  6. 문자열에 대해 Bash 정규식 일치를 수행하십시오.

매우 영리한. 대부분의 잠재적 인 문제가 예방되는 것을 볼 수 있지만 모퉁이에 문제가 없는지 테스트하고 싶습니다. 또한, 포인트 5 처리의 예를보고 싶습니다 printf -v pattern ',,%q,,' "$user_input"; if [[ "${array_str},," =~ $pattern ]].
추후 공지가있을 때까지 일시 중지되었습니다.

case "$(printf ,,%q "${haystack[@]}"),," in (*"$(printf ,,%q,, "$needle")"*) true;; (*) false;; esac
Tino

3

case배열에 특정 값이 포함되어 있는지 확인하기 위해 논리를 사용하는 것에 대한 @ ghostdog74의 답변에 약간의 추가 내용이 있습니다.

myarray=(one two three)
word=two
case "${myarray[@]}" in  ("$word "*|*" $word "*|*" $word") echo "found" ;; esac

또는 extglob옵션이 켜져 있으면 다음과 같이 할 수 있습니다.

myarray=(one two three)
word=two
shopt -s extglob
case "${myarray[@]}" in ?(*" ")"$word"?(" "*)) echo "found" ;; esac

또한 우리는 if진술로 그것을 할 수 있습니다 :

myarray=(one two three)
word=two
if [[ $(printf "_[%s]_" "${myarray[@]}") =~ .*_\[$word\]_.* ]]; then echo "found"; fi

2

주어진 :

array=("something to search for" "a string" "test2000")
elem="a string"

다음 간단한 점검 :

if c=$'\x1E' && p="${c}${elem} ${c}" && [[ ! "${array[@]/#/${c}} ${c}" =~ $p ]]; then
  echo "$elem exists in array"
fi

어디

c is element separator
p is regex pattern

([[]] 내에서 직접 표현식을 사용하지 않고 p를 별도로 할당하는 이유는 bash 4와의 호환성을 유지하기위한 것입니다)


"간단한"이라는 단어의 사용을 사랑하십시오 ... 😂
Christian

2

여기에 제시된 몇 가지 아이디어를 결합하면 정확한 단어 일치를 하는 루프가없는 문장이 있으면 우아하게 만들 수 있습니다 .

$find="myword"
$array=(value1 value2 myword)
if [[ ! -z $(printf '%s\n' "${array[@]}" | grep -w $find) ]]; then
  echo "Array contains myword";
fi

이 트리거되지 않습니다 word또는 val, 단지 전체 단어 일치. 각 배열 값에 여러 단어가 포함되어 있으면 중단됩니다.


1

나는 일반적으로 bash가 변수를 참조로 전달할 수 없기 때문에 변수 값 대신 변수 이름에서 작동하도록 이러한 종류의 유틸리티를 작성합니다.

다음은 배열 이름으로 작동하는 버전입니다.

function array_contains # array value
{
    [[ -n "$1" && -n "$2" ]] || {
        echo "usage: array_contains <array> <value>"
        echo "Returns 0 if array contains value, 1 otherwise"
        return 2
    }

    eval 'local values=("${'$1'[@]}")'

    local element
    for element in "${values[@]}"; do
        [[ "$element" == "$2" ]] && return 0
    done
    return 1
}

이것으로 질문 예제는 다음과 같습니다.

array_contains A "one" && echo "contains one"

기타


누군가가 if 내에서 사용 된 예제, 특히 배열을 전달하는 방법을 게시 할 수 있습니까? 매개 변수를 배열로 처리하여 스크립트에 대한 인수가 전달되었는지 확인하려고하지만 작동하지 않습니다. params = ( "$ @") check = array_에 $ {params} 'SKIPDIRCHECK'가 포함 된 경우 [[$ {check} == 1]]; then .... 그러나 'asas'를 인수로 사용하여 스크립트를 실행할 때 asas : command not found라고 계속 말합니다. : /
Steve Childs

1

사용 grep하여printf

각 배열 멤버를 새 줄로 포맷 한 다음 줄을 포맷 grep하십시오.

if printf '%s\n' "${array[@]}" | grep -x -q "search string"; then echo true; else echo false; fi
예:
$ array=("word", "two words")
$ if printf '%s\n' "${array[@]}" | grep -x -q "two words"; then echo true; else echo false; fi
true

이것은 델리 미터와 공간에는 문제가 없습니다.


1

'grep'및 루프가없는 한 줄 검사

if ( dlm=$'\x1F' ; IFS="$dlm" ; [[ "$dlm${array[*]}$dlm" == *"$dlm${item}$dlm"* ]] ) ; then
  echo "array contains '$item'"
else
  echo "array does not contain '$item'"
fi

이 방법은 grep루프 와 같은 외부 유틸리티를 사용하지 않습니다.

여기서 일어나는 일은 다음과 같습니다.

  • 와일드 카드 하위 문자열 매처를 사용하여 문자열로 연결된 배열에서 항목을 찾습니다.
  • 검색 항목을 한 쌍의 구분 기호로 묶어 가능한 오 탐지를 차단합니다.
  • 우리는 인쇄 할 수없는 문자를 구분자로 사용하여 안전한면에 있습니다.
  • IFS변수 값 을 임시로 대체하여 배열 연결에도 구분자를 사용 합니다.
  • IFS서브 쉘 (한 쌍의 괄호로 묶음)로 조건식을 평가 하여이 값을 임시로 대체합니다.

dlm을 제거하십시오. IFS를 직접 사용하십시오.
Robin A. Meade

이것이 가장 좋은 대답입니다. 나는 그것을 너무 좋아, 이 기술을 사용하여 함수를 작성했습니다 .
Robin A. Meade

1

매개 변수 확장 사용 :

$ {parameter : + word} 매개 변수가 널이거나 설정되지 않은 경우 대체되는 것이 없으며, 그렇지 않으면 단어의 확장이 대체됩니다.

declare -A myarray
myarray[hello]="world"

for i in hello goodbye 123
do
  if [ ${myarray[$i]:+_} ]
  then
    echo ${!myarray[$i]} ${myarray[$i]} 
  else
    printf "there is no %s\n" $i
  fi
done

${myarray[hello]:+_}연관 배열에는 효과적이지만 일반적인 인덱스 배열에는 적합하지 않습니다. 문제는 연관 배열의 키가 있는지 확인하지 않고 aray에서 값을 찾는 것입니다.
에릭

0

답변을 한 후, 나는 특히 마음에 드는 또 다른 답변을 읽었지만 결함이 있고 다운 보트되었습니다. 나는 영감을 얻었고 여기에 내가 볼 수있는 두 가지 새로운 접근법이 있습니다.

array=("word" "two words") # let's look for "two words"

사용 grep하여 printf:

(printf '%s\n' "${array[@]}" | grep -x -q "two words") && <run_your_if_found_command_here>

사용하여 for:

(for e in "${array[@]}"; do [[ "$e" == "two words" ]] && exit 0; done; exit 1) && <run_your_if_found_command_here>

not_found 결과에 대한 추가 || <run_your_if_notfound_command_here>


0

여기에 내가 걸릴 것입니다.

실행하는 데 시간이 걸리므로 피할 수 있다면 bash for 루프를 사용하지 않는 것이 좋습니다. 무언가가 반복되어야한다면, 그것은 쉘 스크립트보다 낮은 수준의 언어로 작성된 것입니다.

function array_contains { # arrayname value
  local -A _arr=()
  local IFS=
  eval _arr=( $(eval printf '[%q]="1"\ ' "\${$1[@]}") )
  return $(( 1 - 0${_arr[$2]} ))
}

이는 _arr입력 배열의 값에서 색인을 가져 오는 임시 연관 배열을 작성하여 작동합니다 . 연관 배열은 bash 4 이상에서 사용할 수 있으므로이 기능은 이전 버전의 bash에서는 작동하지 않습니다. $IFS공백에서 단어가 분리되지 않도록 설정 했습니다.

내부적으로 bash가 채우기 위해 입력 배열을 단계별로 실행하지만 함수에는 명시 적 루프가 없습니다 printf. printf 형식은 %q배열 키로 안전하게 사용할 수 있도록 입력 데이터를 이스케이프 처리하는 데 사용됩니다.

$ a=("one two" three four)
$ array_contains a three && echo BOOYA
BOOYA
$ array_contains a two && echo FAIL
$

이 함수가 사용하는 모든 것은 bash에 내장되어 있으므로 명령 확장에서도 외부 파이프가 끌어다 놓지 않습니다.

그리고 당신이 eval... 잘 사용하는 것을 좋아하지 않는다면 , 당신은 다른 접근법을 자유롭게 사용할 수 있습니다. :-)


배열에 대괄호가 포함되어 있으면 어떻게됩니까?
gniourf_gniourf

@gniourf_gniourf-대괄호가 균형을 잡으면 괜찮아 보이지만 배열에 불균형 대괄호가있는 값이 포함되어 있으면 문제가 있음을 알 수 있습니다. 이 경우 eval답변 끝에 명령을 호출합니다 . :)
ghoti

내가 싫어하는 것은 아닙니다. eval(대부분 울고있는 대부분의 사람들 이 악에 대해 이해하지 않고 eval악한 것과 달리 나는 반대 하지 않습니다 ). 당신의 명령이 깨졌습니다. 아마도 %q대신에 %s더 좋을 것입니다.
gniourf_gniourf

1
@gniourf_gniourf : 나는 "또 다른 접근 방식"비트만을 의미했지만 (나는 eval분명히 당신과 함께 있습니다. ) 당신은 %q내가 볼 수있는 다른 것을 깨지 않고 절대적으로 옳습니다 . (% q도 대괄호를 피할 수 있다는 것을 몰랐습니다.) 내가보고 고친 또 다른 문제는 공백에 관한 것입니다. 을 사용하면 a=(one "two " three)Keegan의 문제와 유사합니다 . 오 탐지 array_contains a "two "뿐만 아니라 array_contains a two오 탐지를 얻었습니다. 설정하여 쉽게 고칠 수 IFS있습니다.
ghoti

공백과 관련하여 따옴표가 없기 때문에 그렇지 않습니까? 또한 glob 문자로 구분됩니다. 나는 당신이 대신 이것을 원한다고 생각 eval _arr=( $(eval printf '[%q]="1"\ ' "\"\${$1[@]}\"") )합니다 local IFS=. Bash는 연관 배열에서 빈 키를 만들기를 거부하므로 배열의 빈 필드에는 여전히 문제가 있습니다. 그것을 해결하는 빠른 해키 방법은 더미 문자를 앞에 추가하는 말 x: eval _arr=( $(eval printf '[x%q]="1"\ ' "\"\${$1[@]}\"") )return $(( 1 - 0${_arr[x$2]} )).
gniourf_gniourf

-1

이미 제안 된 정규식 기술의 내 버전 :

values=(foo bar)
requestedValue=bar

requestedValue=${requestedValue##[[:space:]]}
requestedValue=${requestedValue%%[[:space:]]}
[[ "${values[@]/#/X-}" =~ "X-${requestedValue}" ]] || echo "Unsupported value"

여기서 일어나는 것은 지원되는 값의 전체 배열을 단어로 확장 하고이 경우 특정 문자열 "X-"를 각 문자열 앞에 추가하고 요청 된 값과 동일하게 수행한다는 것입니다. 이 문자열이 실제로 배열에 포함되어 있으면 결과 문자열은 결과 토큰 중 하나와 거의 일치하거나 전혀 일치하지 않습니다. 후자의 경우 || 연산자가 트리거되고 지원되지 않는 값을 처리하고 있음을 알고 있습니다. 그 전에 요청 된 값은 표준 셸 문자열 조작을 통해 모든 선행 및 후행 공백을 제거합니다.

지원되는 값의 배열이 특히 큰 경우 성능이 어느 정도인지 확실하지 않지만 깨끗하고 우아합니다.


-1

다음은이 문제에 대한 설명입니다. 짧은 버전은 다음과 같습니다.

function arrayContains() {
        local haystack=${!1}
        local needle="$2"
        printf "%s\n" ${haystack[@]} | grep -q "^$needle$"
}

그리고 긴 버전은 눈에 훨씬 더 쉽다고 생각합니다.

# With added utility function.
function arrayToLines() {
        local array=${!1}
        printf "%s\n" ${array[@]}
}

function arrayContains() {
        local haystack=${!1}
        local needle="$2"
        arrayToLines haystack[@] | grep -q "^$needle$"
}

예 :

test_arr=("hello" "world")
arrayContains test_arr[@] hello; # True
arrayContains test_arr[@] world; # True
arrayContains test_arr[@] "hello world"; # False
arrayContains test_arr[@] "hell"; # False
arrayContains test_arr[@] ""; # False

내가 힘든 시간 대답을 이해를 가지고 이제 오랫동안 떠들썩한 파티를 사용하지 않은, 또는 나 자신을 :) 쓴 심지어 내가이 질문은 여전히 :) 모든이 시간 이후 활동을 얻는 것을 믿을 수 없다
파올로 테 데스 코를

무엇에 대해 test_arr=("hello" "world" "two words")?
Qwerty

-1

다른 스크립트 / 명령으로 생성 된 ID 목록에 ID가 포함되어 있는지 확인 해야하는 경우가있었습니다. 나를 위해 다음을 수행했습니다.

# the ID I was looking for
ID=1

# somehow generated list of IDs
LIST=$( <some script that generates lines with IDs> )
# list is curiously concatenated with a single space character
LIST=" $LIST "

# grep for exact match, boundaries are marked as space
# would therefore not reliably work for values containing a space
# return the count with "-c"
ISIN=$(echo $LIST | grep -F " $ID " -c)

# do your check (e. g. 0 for nothing found, everything greater than 0 means found)
if [ ISIN -eq 0 ]; then
    echo "not found"
fi
# etc.

다음과 같이 짧게 / 압축 할 수도 있습니다.

if [ $(echo " $( <script call> ) " | grep -F " $ID " -c) -eq 0 ]; then
    echo "not found"
fi

필자의 경우 jq를 실행하여 ID 목록에 대해 일부 JSON을 필터링하고 나중에 내 ID 가이 목록에 있는지 확인해야하며 이것이 가장 효과적이었습니다. 수동으로 생성 된 형식의 배열에는 적용 LIST=("1" "2" "4")되지 않지만 줄 바꿈으로 구분 된 스크립트 출력 에는 작동하지 않습니다 .


추신 : 나는 비교적 새로운이기 때문에 대답을 말할 수 없습니다 ...


-2

다음 코드는 주어진 값이 배열에 있는지 확인하고 0부터 시작하는 오프셋을 반환합니다.

A=("one" "two" "three four")
VALUE="two"

if [[ "$(declare -p A)" =~ '['([0-9]+)']="'$VALUE'"' ]];then
  echo "Found $VALUE at offset ${BASH_REMATCH[1]}"
else
  echo "Couldn't find $VALUE"
fi

일치는 전체 값에서 수행되므로 VALUE = "three"를 설정하면 일치하지 않습니다.


-2

반복하지 않으려는 경우 조사 할 가치가 있습니다.

#!/bin/bash
myarray=("one" "two" "three");
wanted="two"
if `echo ${myarray[@]/"$wanted"/"WAS_FOUND"} | grep -q "WAS_FOUND" ` ; then
 echo "Value was found"
fi
exit

스 니펫 : http://www.thegeekstuff.com/2010/06/bash-array-tutorial/ 했습니다. 꽤 영리하다고 생각합니다.

편집 : 당신은 아마 그냥 할 수 있습니다 :

if `echo ${myarray[@]} | grep -q "$wanted"` ; then
echo "Value was found"
fi

그러나 후자는 배열에 고유 한 값이 포함 된 경우에만 작동합니다. "143"에서 1을 찾으면 오 탐지가 발생합니다.


-2

조금 늦었지만 이것을 사용할 수 있습니다.

#!/bin/bash
# isPicture.sh

FILE=$1
FNAME=$(basename "$FILE") # Filename, without directory
EXT="${FNAME##*.}" # Extension

FORMATS=(jpeg JPEG jpg JPG png PNG gif GIF svg SVG tiff TIFF)

NOEXT=( ${FORMATS[@]/$EXT} ) # Formats without the extension of the input file

# If it is a valid extension, then it should be removed from ${NOEXT},
#+making the lengths inequal.
if ! [ ${#NOEXT[@]} != ${#FORMATS[@]} ]; then
    echo "The extension '"$EXT"' is not a valid image extension."
    exit
fi

-2

zsh에서만 작동하는 것으로 밝혀졌지만 일반적인 접근 방식은 훌륭하다고 생각합니다.

arr=( "hello world" "find me" "what?" )
if [[ "${arr[@]/#%find me/}" != "${arr[@]}" ]]; then
    echo "found!"
else
    echo "not found!"
fi

각 요소가 시작 ${arr[@]/#pattern/}하거나 끝나는 경우에만 각 요소에서 패턴을 제거 ${arr[@]/%pattern/}합니다. 이 두 가지 대체는 bash에서 작동하지만 동시에 둘 다${arr[@]/#%pattern/} zsh에서만 작동합니다.

수정 된 배열이 원본과 같으면 요소를 포함하지 않습니다.

편집하다:

이것은 bash에서 작동합니다.

 function contains () {
        local arr=(${@:2})
        local el=$1
        local marr=(${arr[@]/#$el/})
        [[ "${#arr[@]}" != "${#marr[@]}" ]]
    }

대체 후 두 배열의 길이를 비교합니다. 배열에 요소가 포함되어 있으면 대체가 완전히 삭제하고 개수가 달라집니다.

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