No bash, existe uma maneira de fazer declarações mais curtas (se ou)?

7
if [ foo = bar -o foo = car -o foo = jar -o foo = foo ]
  then
    echo yes
fi

Para manter as coisas mais organizadas, eu gostaria de tentar combiná-lo a uma lista, como

if [ foo = {bar,car,jar,foo} ] ; then

Obviamente, o método de expansão de chaves não funcionou ou eu não estaria aqui! Mas eu gostaria de saber se algo assim é possível.

    
por TuxForLife 09.05.2015 / 04:27

3 respostas

9

Eu sugiro usar a estrutura do caso. Algo como:

case $foo in
    foo|car|bar|jar) echo yes ;;
    *) ;;
esac

Se desejar, você pode adicionar um comando entre *) e ;; a ser executado quando $foo não corresponder a foo , car , bar ou jar . Por exemplo, você pode imprimir uma mensagem com echo 'wrong input' ou algo parecido.

    
por Sergiy Kolodyazhnyy 09.05.2015 / 04:43
14

Parece um bug no seu código, mas podemos reduzi-lo muito bem ...

Se você tivesse realmente escrito sua expressão com [ foo = bar -o foo = car -o foo = jar -o foo = foo ] , poderia ter sido simplificado para:

echo yes

Esse teste é sempre verdadeiro porque foo = foo é sempre true, o que, por sua vez, ocorre porque a string literal foo é sempre a mesma que ela mesma.

Embora você tenha esclarecido que esta sintaxe não apareceu no código que você estava realmente usando, mantive esta seção introdutória, pois é um erro comum fazê-lo, especialmente entre os novatos. Tentar verificar o valor de foo desta maneira não funciona em nenhum shell estilo Bourne - para examinar o valor de uma variável com [ , você deve executar a expansão de parâmetro com $ (veja abaixo). Esse aspecto específico do problema não é específico do shell; por exemplo, aqui está um teste em 7 shells ao estilo Bourne .

Corrigindo o código original

Talvez, em vez disso, você queira verificar o valor da variável foo (ou seja, seu conteúdo , em vez de seu nome) em relação a essas sequências. Nesse caso, seu código original se tornaria:

if [ "$foo" = bar -o "$foo" = car -o "$foo" = jar -o "$foo" = foo ]; then
    echo yes
fi

Isso pode ser reduzido um pouco com o operador && de curto-circuito:

[ "$foo" = bar -o "$foo" = car -o "$foo" = jar -o "$foo" = foo ] && echo yes

Mas ainda é mais longo e mais complicado e repetitivo do que você provavelmente prefere. Então, seguindo em frente ...

Formulários mais curtos via correspondência de padrões

Você pode usar o operador de correspondência de expressão regular integrada do recurso [[ :

[[ $foo =~ ^(bar|car|jar|foo)$ ]] && echo yes

Embora usar case seja provavelmente a maneira mais comum, [[ com =~ é:

  • o mesmo comprimento que um uso compacto de case (veja abaixo)
  • indiscutivelmente mais fiel à lógica do seu código original, pois ele usa [[ , que funciona muito como uma expressão de teste.

Então você pode preferir isso. É o método que eu provavelmente usaria, para fazer isso no bash.

Se você quisesse fazer algo assim, mas corresponder usando grep em vez de [[ com =~ , isso é semelhante:

grep -Eq '^(bar|car|jar|foo)$' <<< "$foo" && echo yes

Ou isto:

grep -Fqe{bar,car,jar,foo} <<< "$foo" && echo yes

Um One-Liner com case

Como Serg diz , pode ser simplificado usando case em vez de if e [ . Essa é a maneira clássica e provavelmente a mais comum.

O método em resposta de Serg funciona perfeitamente bem, mas para o código específico que você está refatorando, podemos escrever algo mais simples, usando apenas um padrão para case . Não há necessidade de ter uma correspondência *) pega-tudo no final. Se a string não corresponder a nenhum dos padrões, o controle simplesmente passará pela construção da ocorrência e nenhum comando será executado, o que equivale à sua construção if sem else .

case $foo in bar|car|jar|foo) echo yes;; esac

Isso faz com que seja curto e simples o suficiente para se ajustar facilmente - e ser legível - em uma única linha (o que parece ser o que você está procurando). Este método também é portável para shells estilo Bourne além de bash.

    
por Eliah Kagan 09.05.2015 / 05:13
3

Ainda mais curto:

[[ $foo =~ ^([bcj]ar|foo)$ ]] && echo yes

Dessa forma, $foo é correspondido a um regex começando com b , c ou j seguido por ar ou correspondendo exatamente a foo .

Isso usa o operador && como um curto-circuito para executar echo yes somente se a expressão [[ $foo =~ ^([bcj]ar|foo)$ ]] for avaliada como verdadeira.

Se isso é muito compacto para ser lido com facilidade, você ainda pode usar a construção full if, que faz exatamente o mesmo, mas é mais legível:

if [[ $foo =~ ^([bcj]ar|foo)$ ]]
then
    echo yes
fi
    
por kos 09.05.2015 / 06:22

Tags