Teste se o elemento está na matriz no bash

14

Existe uma boa maneira de verificar se um array tem um elemento no bash (melhor que o looping)?

Alternativamente, existe outra maneira de verificar se um número ou string é igual a um conjunto de constantes predefinidas?

    
por Tgr 04.10.2010 / 12:54

6 respostas

21

No Bash 4, você pode usar matrizes associativas:

# 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

Para configurar o array inicialmente, você também pode fazer atribuições diretas:

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

ou desta forma:

array=([foo]=1 [bar]=1 [baz]=1)
    
por 04.10.2010 / 17:46
8

É uma pergunta antiga, mas acho que a solução mais simples ainda não apareceu: test ${array[key]+_} . Exemplo:

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"

Saídas:

a is set
b is set

Para ver como este trabalho verifica este .

    
por 25.05.2012 / 23:57
5

Existe uma maneira de testar se existe um elemento de um array associativo (não definido), isso é diferente do vazio:

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

Então use:

declare -A assoc
KEY="key"
isNotSet assoc[${KEY}]
if [ $? -ne 0 ]
then
  echo "${KEY} is not set."
fi
    
por 16.11.2011 / 20:49
2

Você pode ver se uma entrada está presente canalizando o conteúdo da matriz para grep.

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

Você também pode obter o índice de uma entrada com grep -n, que retorna o número da linha de uma correspondência (lembre-se de subtrair 1 para obter um índice baseado em zero) Isso será razoavelmente rápido, exceto para matrizes muito grandes.

# 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

explicação:

  • $( ... ) é o mesmo que usar backticks para capturar a saída de um comando em uma variável
  • printf gera mydata um elemento por linha
  • (todas as citações necessárias, juntamente com @ em vez de *. , evitam dividir "hello world" em duas linhas)
  • grep procura por uma string exata: ^ e $ correspondem ao início e ao fim da linha
  • grep -n retorna linha #, em forma de 4: hello world
  • grep -m 1 encontra apenas a primeira correspondência
  • cut extrai apenas o número da linha
  • subtrai 1 do número da linha retornada.

Você pode, naturalmente, dobrar a subtração no comando. Mas, em seguida, teste para -1 por falta:

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

if [[ $ix == -1 ]]; then echo missing; else ... fi
  • $(( ... )) faz aritmética inteira
por 17.07.2012 / 18:19
1

Eu não acho que você possa fazer isso adequadamente sem o loop, a menos que tenha muito dados limitados no array.

Aqui está uma variante simples, isso diria corretamente que "Super User" existe na matriz. Mas também diria que "uper Use" está no array.

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

O problema é que não há maneira fácil de adicionar as âncoras (que eu posso pensar) além de fazer um loop através da matriz. A menos que você possa adicioná-los antes de colocá-los na matriz ...

    
por 04.10.2010 / 15:55
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
    
por 27.02.2018 / 10:34

Tags