Compreendendo operadores booleanos no script bash

11
phone_missing=false
echo "missing $phone_missing"

if [ ! $phone_missing ]
then
        echo "Lost phone at $readabletime"
        $phone_missing=true
fi

Eu simplesmente não consigo envolver minha cabeça com isso, por mais idiota que possa parecer. A linha

echo "missing $phone_missing"

echos missing false , eu esperaria muito que a declaração

if [ ! $phone_missing ]

seja verdadeiro e insira a cláusula if , mas isso não acontece? O que estou perdendo aqui?

    
por asco 19.12.2013 / 19:31

7 respostas

12

$ phone_missing é uma string que contém "false". E uma string não vazia é avaliada como verdadeira. Veja também link

    
por 19.12.2013 / 19:46
10

Costumo usar "true" e "false", pois eles também são comandos que meramente retornam sucesso e falha, respectivamente. Então você pode fazer

if "$phone_missing"; then ...
    
por 19.12.2013 / 19:58
5

Aqui está uma maneira de fazer isso, mantendo os valores verdadeiro / falso.

phone_missing=false
if [ "$phone_missing" != false ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
if [ "$phone_missing" == true ]; then
    echo "phone_missing is true."
fi

As aspas duplas em torno de $phone_missing são para proteger contra o caso em que a variável phone_missing não está definida. Outro idioma comum para evitar isso é [ x$phone_missing != xfalse ] , mas as citações parecem mais naturais para mim.

A dica está na página de ajuda bash de test :

  STRING      True if string is not empty.
  ...
  ! EXPR      True if expr is false.

Então, basicamente, [ $foo ] será verdadeiro se $foo não estiver vazio. Não é verdade ou falsa, apenas não vazia. [ ! $foo ] é verdadeiro se $ foo estiver vazio ou indefinido.

Você sempre pode alterar seu código para definir apenas phone_missing como um valor não vazio, o que denota true. Se phone_missing não estiver definido (ou vazio - phone_missing="" ), será falso. Caso contrário, você deve estar usando os operadores de teste de string ( = e != ).

O outro pequeno problema é a atribuição. Você tem isso como $phone_missing=true , enquanto deveria ser phone_missing=true (sem sinal de dólar).

Desculpe se isso é um pouco denso, é porque eu sou. Tem sido um longo dia. :)

    
por 19.12.2013 / 21:54
0

Eu teria feito isso como um comentário para apoiar James Ko, mas não tinha o representante para comentar ou votar publicamente.

O problema aqui é que os colchetes [] são notados para fazer um teste de comparação, como valor ou igualdade de string.

A exatidão de string no bash para uma string vazia é "" (string vazia) avaliada como false (valor de retorno 1) e qualquer string não vazia "false" "true" ou "bob's your tcle" é avaliada como true 0).

Você pode provar isso para si mesmo com:

    if [ "foo" ]; then 
      echo true is $?; 
    else 
      echo false is $?; 
    fi

O $? acima é uma variável especial que mantém o status de saída dos últimos comandos (0 com êxito e qualquer > 0 para código de erro) e emitirá true is 0 . Você pode substituir "foo" com o que quiser e o resultado será o mesmo, exceto se você substituí-lo por uma string vazia "" ou '' , caso em que avaliará a condição else e a saída false is 1 .

Ao usar os colchetes [], a declaração interna, como! $ phone_missing é avaliado e, em seguida, retorna um 0 para true ou 1 para false para a instrução de controle if. Como o colchete avalia as comparações de string e valor, o $ phone_missing é primeiro expandido para a string não vazia "false", que é avaliada pelo [] como uma string não vazia (ou verdadeira) e, em seguida, o! inverte o resultado que resulta na instrução if obtendo um valor false (ou valor de retorno 1) e pula o corpo condicional executando a instrução else se presente.

Como James Ko disse, a notação seria apenas passar a variável mantendo seu 'booleano' para a declaração de controle if. Note que no bash um booleano verdadeiro e falso deve ser minúsculo como em: bool_true = true bool_false = false Qualquer outro caso será avaliado como strings e não como booleano.

Então, usando o seu exemplo e a resposta de James Ko, seria:

phone_missing=false
echo "missing $phone_missing"

if ! $phone_missing 
then
  echo "Lost phone at $readabletime"
  $phone_missing=true
fi

ou como eu prefiro ler (sintaticamente o mesmo e o de James Ko)

phone_missing=false
echo "missing $phone_missing"

if ( ! $phone_missing ); then
  echo "Lost phone at $readabletime"
  $phone_missing=true
fi

Outras respostas, como as de Alexios, estão literalmente verificando o conteúdo da string. De modo que qualquer um dos seguintes resultaria em falso:

phone_missing=false
if [ "$phone_missing" != false ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
if [ "$phone_missing" == true ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
if [ "$phone_missing" == Bobs_your_uncle ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
    
por 14.09.2015 / 20:26
0

Outras respostas deram solução, mas vou explicar o que havia de errado com o pensamento original.

Variáveis Bash não possuem tipos, portanto não existe uma variável booleana ou valor como true ou false. Basicamente, todas as variáveis bash são apenas strings.

Quando você testa uma variável / string no bash sem especificar o tipo de teste ( -n ou -z ), o padrão será um teste -n (string de comprimento diferente de zero).

Portanto, [ "$var" ] é equivalente a [ -n "$var" ] . Desde que $var contenha pelo menos 1 caractere, foi avaliado como verdadeiro.

Como você negou a expressão, [ ! "$var" ] é equivalente a [ ! -n "$var" ] . Portanto, se $var contiver pelo menos 1 caractere, a expressão será falsa.

Como false (que é apenas uma string) contém 5 caracteres, a expressão é falsa. Se não importa qual cadeia você definiu phone_missing para ( true , 0, 1), a expressão será sempre false porque é phone_missing é diferente de zero. A única maneira de torná-lo true é phone_missing="" , já que é o comprimento zero.

    
por 01.06.2018 / 05:47
0

Não use string para booleano. Use inteiro.

Entrada:

val=1
((val)) && echo "true" || echo "false"
val=0
((val)) && echo "true" || echo "false"

Saída:

true
false

Fonte :

((expression))

The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression".

    
por 30.07.2018 / 20:19
-1

A sintaxe correta é if ! $bool; then [statements]; fi .

Exemplo:

bool=false

if ! $bool; then
    echo "This is correct!"
fi

if [ ! $bool ]; then
    echo "This is wrong!"
fi

Saída: This is correct!

    
por 24.05.2015 / 07:45