Como representar várias condições em um shell se instrução?


308

Quero representar várias condições como esta:

if [ ( $g -eq 1 -a "$c" = "123" ) -o ( $g -eq 2 -a "$c" = "456" ) ]   
then  
    echo abc;  
else  
    echo efg;   
fi  

mas quando executo o script, ele mostra

syntax error at line 15: `[' unexpected, 

onde a linha 15 é a que mostra se ....

O que há de errado com essa condição? Eu acho que algo está errado com o ().


6
Você não está perguntando sobre as condições do shell, mas as condições de teste . A expressão inteira no seu exemplo é avaliada por test( [) e não pelo shell. O shell avalia apenas o status de saída de [.
ceving 17/06/2015


Respostas:


381

Técnica clássica (metacaracteres de escape):

if [ \( "$g" -eq 1 -a "$c" = "123" \) -o \( "$g" -eq 2 -a "$c" = "456" \) ]
then echo abc
else echo efg
fi

Coloquei as referências $gentre aspas duplas; Essa é uma boa prática, em geral. Estritamente, os parênteses não são necessários porque a precedência -ae -oa corrige mesmo sem eles.

Note que o -ae -ooperadores são parte da especificação POSIX para test, aka [, principalmente para compatibilidade com versões anteriores (desde que eles eram uma parte testna 7ª Edição UNIX, por exemplo), mas eles são explicitamente marcados como 'obsoletos' por POSIX. O Bash (consulte expressões condicionais ) parece antecipar os significados clássico e POSIX para -ae -ocom seus próprios operadores alternativos que recebem argumentos.


Com algum cuidado, você pode usar o [[operador mais moderno , mas lembre-se de que as versões no Bash e Korn Shell (por exemplo) não precisam ser idênticas.

for g in 1 2 3
do
    for c in 123 456 789
    do
        if [[ ( "$g" -eq 1 && "$c" = "123" ) || ( "$g" -eq 2 && "$c" = "456" ) ]]
        then echo "g = $g; c = $c; true"
        else echo "g = $g; c = $c; false"
        fi
    done
done

Exemplo de execução, usando o Bash 3.2.57 no Mac OS X:

g = 1; c = 123; true
g = 1; c = 456; false
g = 1; c = 789; false
g = 2; c = 123; false
g = 2; c = 456; true
g = 2; c = 789; false
g = 3; c = 123; false
g = 3; c = 456; false
g = 3; c = 789; false

Você não precisa citar as variáveis [[como faz [porque não é um comando separado da mesma maneira que [é.


Não é uma pergunta clássica?

Eu teria pensado assim. No entanto, existe outra alternativa, a saber:

if [ "$g" -eq 1 -a "$c" = "123" ] || [ "$g" -eq 2 -a "$c" = "456" ]
then echo abc
else echo efg
fi

De fato, se você ler as diretrizes do 'shell portátil' para a autoconfferramenta ou pacotes relacionados, essa notação - usando ' ||' e ' &&' - é o que eles recomendam. Suponho que você poderia ir tão longe quanto:

if [ "$g" -eq 1 ] && [ "$c" = "123" ]
then echo abc
elif [ "$g" -eq 2 ] && [ "$c" = "456" ]
then echo abc
else echo efg
fi

Onde as ações são tão triviais quanto ecoantes, isso não é ruim. Quando o bloco de ação a ser repetido possui várias linhas, a repetição é muito dolorosa e uma das versões anteriores é preferível - ou você precisa agrupar as ações em uma função que é invocada nos diferentes thenblocos.


9
É bom saber que os parênteses escapados funcionam; como um aparte: nesse caso em particular, os parênteses nem são necessários, porque -ana verdade têm maior precedência do que -o(diferente do shell &&e ||no shell - no entanto, dentro de bash [[ ... ]] condicionais , && também tem maior precedência do que ||). Se você queria evitar -ae -ode robustez máxima e portabilidade - que a página homem POSIX si sugere - você também pode usar subshells para o agrupamento:if ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])
mklement0

Boa solução, mas eu gosto do formulário sem todos os parênteses e parênteses:if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...
Mogens TrasherDK

Estou um pouco atrasado para isso, mas ainda é um dos principais resultados da pesquisa, então gostaria de observar que usar &&ou ||também é preferível, pois se comporta mais como condicionais em outros idiomas e permite que você provoque um curto-circuito. Por exemplo: if [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]. Fazendo dessa maneira, se check_inodesestiver vazio, você evita duas chamadas para stat, enquanto uma condição de teste maior e complexa deve processar todos os argumentos antes da execução (o que também pode levar a erros se você esquecer esse comportamento).
Haravikk

182

No Bash:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]

9
Definitivamente, a melhor abordagem bash. Como um aparte: neste caso em particular, os parênteses nem são necessários, porque condicionais internos têm realmente maior precedência do que - diferentemente de fora desses condicionais. [[ ... ]] &&||
mklement0

Existe uma maneira de descobrir qual condição corresponde aqui? Foi o primeiro parênteses ou o outro?
Firelord

1
@Firelord: Você precisaria separar as condições em uma instrução if/ elsee ter o código entre thene ficolocar em uma função para evitar repeti-la. Em casos muito simples, você pode usar uma casedeclaração com ;&explicação (no Bash 4).
Pausado até novo aviso.

1
Qual é o objetivo dos dois colchetes?
precisa saber é o seguinte

2
@ Peter: Por favor, veja minha resposta aqui , Construções Condicionais no Manual do Bash e no BashFAQ / 31 .
Pausado até novo aviso.

37

Usar /bin/basho seguinte funcionará:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Entered $option"
fi

8

Cuidado se você tiver espaços em suas variáveis ​​de string e verificar a existência. Não deixe de citá-los corretamente.

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then

1
quando usamos os colchetes depois do símbolo do dólar !!
YouAreAwesome

6
$ g=3
$ c=133
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
efg
$ g=1
$ c=123
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
abc

2
Essa é uma subcamada inútil. { ;}poderia ser usado em seu lugar.
Phk

2

No bash para comparação de cadeias, você pode usar a seguinte técnica.

if [ $var OP "val" ]; then
    echo "statements"
fi

Exemplo:

var="something"
if [ $var != "otherthing" ] && [ $var != "everything" ] && [ $var != "allthings" ]; then
    echo "this will be printed"
else
    echo "this will not be printed"
fi

0
#!/bin/bash

current_usage=$( df -h | grep 'gfsvg-gfslv' | awk {'print $5'} )
echo $current_usage
critical_usage=6%
warning_usage=3%

if [[ ${current_usage%?} -lt ${warning_usage%?} ]]; then
echo OK current usage is $current_usage
elif [[ ${current_usage%?} -ge ${warning_usage%?} ]] && [[ ${current_usage%?} -lt ${critical_usage%?} ]]; then
echo Warning $current_usage
else
echo Critical $current_usage
fi

3
Bem-vindo ao Stack Overflow! Esta pergunta está procurando uma explicação , não apenas para o código de trabalho. Sua resposta não fornece informações para o interlocutor e pode ser excluída. Por favor edite a explicar o que faz com que os sintomas observados.
Toby Speight

0

você também pode encadear mais de 2 condições:

if [ \( "$1" = '--usage' \) -o \( "$1" = '' \) -o \( "$1" = '--help' \) ]
then
   printf "\033[2J";printf "\033[0;0H"
   cat << EOF_PRINT_USAGE

   $0 - Purpose: upsert qto http json data to postgres db

   USAGE EXAMPLE:

   $0 -a foo -a bar



EOF_PRINT_USAGE
   exit 1
fi
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.