Usando case e arrays juntos em bash

2

É possível verificar se uma variável está contida dentro de uma matriz usando case ? Eu gostaria de fazer algo parecido com

ARR=( opt1 opt2 opt3 );

case $1 in
    $ARR)
        echo "Option is contained in the array";
    *)
        echo "Option is not contained in the array";
esac
    
por red_trumpet 15.12.2017 / 09:46

3 respostas

4

Com ksh93 , graças a este bug , você pode fazer:

IFS='|'
ARR=( opt1 opt2 opt3 )

IFS='|'
case $1 in
  (@("${ARR[*]}"))
    echo "Option is contained in the array";;
  (*)
    echo "Option is not contained in the array";;
esac

(Eu não confiaria nele como o bug pode ser consertado no futuro).

Com zsh , você poderia fazer:

case ${ARR[(Ie)$1]}
  (0)
    echo "Option is not contained in the array";;
  (*)
    echo "Option is contained in the array";;
esac

(no entanto, você provavelmente preferiria usar if ((ARR[(Ie)$1])); then echo is present... aqui em vez de uma construção de caso).

${array[(I)pattern]} retorna o índice do último elemento que corresponde ao padrão na matriz ou 0 do contrário. O e flag é para correspondência exata (em oposição à correspondência padrão ).

Com bash , ksh , yash , zsh , se você estiver pronto para assumir que $ARR e $1 não contêm um determinado caractere como @ e $ARR não estará vazio, você pode fazer:

IFS=@
case "@${ARR[*]}@" in
  (*"@$1@"*)
    echo "Option is contained in the array";;
  (*)
    echo "Option is not contained in the array";;
esac

Com bash -O extglob , zsh -o kshglob -o globsubst , você poderia definir um ajudante que cria um padrão com base nos elementos da matriz:

arraypat() {
  awk '
    BEGIN{
      if (ARGC <= 1) print "!(*)"
      else {
        for (i = 1; i < ARGC; i++) {
          gsub(/[][|<>\?*()]/, "[&]", ARGV[i])
          s = s sep ARGV[i]
          sep = "|"
        }
        print "@(" s ")"
      }
    }' "$@"
}

case $1 in
  ($(arraypat "${ARR[@]}"))
    echo "Option is contained in the array";;
  (*)
    echo "Option is not contained in the array";;
esac
    
por 15.12.2017 / 10:30
3

Não é realmente de forma compacta e fácil de usar. Lembre-se de que $ARR será expandido apenas para o primeiro elemento da matriz, opt1 em seu exemplo.

Você poderia usar "${ARR[@]}" , mas usando seus dados isso daria um falso positivo para a string 1 opt .

Com versões mais recentes de bash , você pode considerar o uso de uma matriz associativa:

declare -A arr
arr=( [opt1]=1 [opt2]=1 [opt3]=1 )

if [[ "${arr[$1]}" -eq 1 ]]; then
   # $1 is a key in arr
else
   # is not
fi
    
por 15.12.2017 / 09:59
2

Por que você quer fazer isso com case ? Ele é destinado à correspondência de padrões de sequência, não à correspondência por elemento.

Francamente, se você precisar do teste "contains" com frequência e quiser torná-lo curto por causa disso, basta colocar a parte difícil em uma função em vez de usar soluções alternativas:

#!/bin/bash
ARR=( foo bar doo );

contains() {
        typeset _x;
        typeset -n _A="$1"
        for _x in "${_A[@]}" ; do
                [ "$_x" = "$2" ] && return 0
        done
        return 1
}

if contains ARR "$1" ; then
        echo "\"$1\" is contained in ARR"
else
        echo "\"$1\" not contained in ARR"
fi

(Isso também deve funcionar em ksh )

    
por 15.12.2017 / 11:49