Como declarar e usar variables ​​booleanas no shell script?

Eu tentei declarar uma variável booleana em um script de shell usando a seguinte syntax:

variable=$false variable=$true 

Isso está correto? Além disso, se eu quisesse atualizar essa variável, usaria a mesma syntax? Finalmente, é a seguinte syntax para usar variables ​​booleanas como expressões corretas:

 if [ $variable ] if [ !$variable ] 

Resposta revisada (12 de fevereiro de 2014)

 the_world_is_flat=true # ...do something interesting... if [ "$the_world_is_flat" = true ] ; then echo 'Be careful not to fall off!' fi 

Resposta Original

Advertências: https://stackoverflow.com/a/21210966/89391

 the_world_is_flat=true # ...do something interesting... if $the_world_is_flat ; then echo 'Be careful not to fall off!' fi 

De: Usando variables ​​booleanas no Bash

A razão pela qual a resposta original está incluída aqui é porque os comentários antes da revisão de 12 de fevereiro de 2014 referem-se apenas à resposta original, e muitos dos comentários estão errados quando associados à resposta revisada. Por exemplo, o comentário de Dennis Williamson sobre o bash builtin true em 2 de junho de 2010 só se aplica à resposta original, não à revista.

TL; DR

 bool=true if [ "$bool" = true ] 

Problemas com a resposta ( original ) de Miku

Eu não recomendo a resposta aceita 1 . Sua syntax é bonita, mas tem algumas falhas.

Diga que temos a seguinte condição.

 if $var; then echo 'Muahahaha!' fi 

Nos casos a seguir 2 , essa condição será avaliada como true e executará o comando nested.

 # Variable var not defined beforehand. Case 1 var='' # Equivalent to var="". Case 2 var= # Case 3 unset var # Case 4 var='' # Case 5 

Normalmente, você só deseja que sua condição seja avaliada como verdadeira quando sua variável “booleana”, var neste exemplo, estiver explicitamente definida como true. Todos os outros casos são perigosamente enganosos!

O último caso (# 5) é especialmente impertinente porque ele executará o comando contido na variável (é por isso que a condição é avaliada como verdadeira para os comandos válidos 3, 4 ).

Aqui está um exemplo inofensivo:

 var='echo this text will be displayed when the condition is evaluated' if $var; then echo 'Muahahaha!' fi # Outputs: # this text will be displayed when the condition is evaluated # Muahahaha! 

Citando suas variables ​​é mais seguro, por exemplo, if "$var"; then if "$var"; then . Nos casos acima, você deve receber um aviso de que o comando não foi encontrado. Mas ainda podemos fazer melhor (veja minhas recomendações na parte inferior).

Veja também a explicação de Mike Holt da resposta original de Miku.

Problemas com a resposta de Hbar

Essa abordagem também possui um comportamento inesperado.

 var=false if [ $var ]; then echo "This won't print, var is false!" fi # Outputs: # This won't print, var is false! 

Você esperaria que a condição acima fosse avaliada como falsa, nunca executando a instrução aninhada. Surpresa!

Citando o valor ( "false" ), citando a variável ( "$var" ), ou usando test ou [[ invés de [ , não faça diferença.

O que eu recomendo:

Aqui estão algumas maneiras que eu recomendo que você verifique seus “booleanos”. Eles funcionam como esperado.

 bool=true if [ "$bool" = true ]; then if [ "$bool" = "true" ]; then if [[ "$bool" = true ]]; then if [[ "$bool" = "true" ]]; then if [[ "$bool" == true ]]; then if [[ "$bool" == "true" ]]; then if test "$bool" = true; then if test "$bool" = "true"; then 

Eles são todos bastante equivalentes. Você terá que digitar mais algumas teclas do que as abordagens nas outras respostas, mas seu código será mais defensivo.


Notas de rodapé

  1. A resposta de Miku já foi editada e não contém mais falhas (conhecidas).
  2. Não é uma lista exaustiva.
  3. Um comando válido neste contexto significa um comando que existe. Não importa se o comando é usado corretamente ou incorretamente. Por exemplo, man woman ainda seria considerado um comando válido, mesmo que não exista nenhuma página de manual.
  4. Para comandos inválidos (inexistentes), o Bash simplesmente reclamará que o comando não foi encontrado.
  5. Se você se preocupa com o comprimento, a primeira recomendação é a mais curta.

Parece haver algum mal-entendido aqui sobre o Bash construído de forma true e, mais especificamente, sobre como o Bash expande e interpreta expressões dentro de colchetes.

O código na resposta do miku não tem absolutamente nada a ver com o Bash construído em true , nem o /bin/true , nem qualquer outro sabor do true comando. Nesse caso, true nada mais é do que uma cadeia de caracteres simples, e nenhuma chamada ao comando / builtin true é feita, nem pela atribuição de variável, nem pela avaliação da expressão condicional.

O código a seguir é funcionalmente idêntico ao código na resposta do miku:

 the_world_is_flat=yeah if [ "$the_world_is_flat" = yeah ]; then echo 'Be careful not to fall off!' fi 

A única diferença aqui é que os quatro personagens que estão sendo comparados são ‘y’, ‘e’, ​​’a’ e ‘h’ em vez de ‘t’, ‘r’, ‘u’ e ‘e’. É isso aí. Não há nenhuma tentativa de chamar um comando ou builtin chamado yeah , nem existe (no exemplo do miku) qualquer tipo de manipulação especial acontecendo quando Bash analisa o token true . É apenas uma string, e completamente arbitrária.

Atualização (2014-02-19): Depois de seguir o link na resposta do miku, agora vejo de onde vem a confusão. A resposta de Miku usa colchetes, mas o trecho de código ao qual ele se conecta não usa colchetes. É apenas:

 the_world_is_flat=true if $the_world_is_flat; then echo 'Be careful not to fall off!' fi 

Os dois trechos de código se comportarão da mesma maneira, mas os colchetes mudam completamente o que está acontecendo sob o capô.

Aqui está o que Bash está fazendo em cada caso:

Sem parênteses:

  1. Expanda a variável $the_world_is_flat para a string "true" .
  2. Tente analisar a string "true" como um comando.
  3. Localize e execute o comando true (um construído ou /bin/true , dependendo da versão do Bash).
  4. Compare o código de saída do comando true (que é sempre 0) com 0. Lembre-se de que na maioria dos shells, um código de saída de 0 indica sucesso e qualquer outra coisa indica falha.
  5. Como o código de saída era 0 (sucesso), execute a cláusula then da declaração

Suportes:

  1. Expanda a variável $the_world_is_flat para a string "true" .
  2. Analise a expressão condicional agora totalmente expandida, que é da forma string1 = string2 . O operador = é o operador de comparação de strings do bash. Assim…
  3. Faça uma comparação de string em "true" e "true" .
  4. Sim, as duas seqüências eram as mesmas, então o valor da condicional é verdadeiro.
  5. Execute a cláusula then da declaração.

O código sem colchetes funciona, porque o comando true retorna um código de saída 0, que indica sucesso. O código entre colchetes funciona, porque o valor de $the_world_is_flat é idêntico à string literal true no lado direito do = .

Apenas para direcionar o ponto para casa, considere os seguintes dois trechos de código:

Este código (se executado com privilégios de root) irá reiniciar o seu computador:

 var=reboot if $var; then echo 'Muahahaha! You are going down!' fi 

Este código apenas imprime “Boa tentativa”. O comando de reboot não é chamado.

 var=reboot if [ $var ]; then echo 'Nice try.' fi 

Atualização (2014-04-14) Para responder à pergunta nos comentários sobre a diferença entre = e == : AFAIK, não há diferença. O operador == é um sinônimo específico de Bash para = e, até onde eu vi, eles funcionam exatamente da mesma forma em todos os contextos.

Note, no entanto, que estou falando especificamente sobre os operadores de comparação de string = e == usados ​​em testes [ ] ou [[ ]] . Eu não estou sugerindo que = e = == são intercambiáveis ​​em todos os lugares no bash.

Por exemplo, você obviamente não pode fazer atribuição de variável com == , como var=="foo" (bem tecnicamente você pode fazer isso, mas o valor de var será "=foo" , porque Bash não está vendo um == operador aqui, ele está vendo um operador = (atribuição), seguido pelo valor literal ="foo" , que apenas se torna "=foo" ).

Além disso, embora = e == sejam intercambiáveis, você deve ter em mente que o modo como esses testes funcionam depende se você está usando dentro de [ ] ou [[ ]] , e também se os operandos estão ou não citados. Você pode ler mais sobre isso no Advanced Bash Scripting Guide: 7.3 Outros operadores de comparação (role para baixo até a discussão de = e == ).

Use expressões aritméticas.

 #!/bin/bash false=0 true=1 ((false)) && echo false ((true)) && echo true ((!false)) && echo not false ((!true)) && echo not true 

Saída:

verdade
não é falso

Há muito tempo atrás, quando tudo o que tínhamos era sh , booleanos eram manipulados confiando em uma convenção do programa de test que o test retornava um status falso de saída se fosse executado sem argumentos. Isso permite pensar em uma variável não definida como false e em variável definida como qualquer valor como true. Hoje, o teste está embutido para bash e é comumente conhecido pelo seu apelido de um caractere [ (ou um executável para ser usado em shells sem ele, como diz o dólmen):

 FLAG="up or " if [ "$FLAG" ] ; then echo 'Is true' else echo 'Is false' fi # unset FLAG # also works FLAG= if [ "$FLAG" ] ; then echo 'Continues true' else echo 'Turned false' fi 

Por causa das convenções de cotação, os autores de scripts preferem usar o comando composto [[ que simula o test mas tem uma syntax mais agradável: variables ​​com espaços não precisam ser citadas, pode-se usar && e || como operadores lógicos com precedência estranha, e não há limitações POSIX no número de termos.

Por exemplo, para determinar se o FLAG está definido e COUNT é um número maior que 1:

 FLAG="up" COUNT=3 if [[ $FLAG && $COUNT -gt '1' ]] ; then echo 'Flag up, count bigger than 1' else echo 'Nope' fi 

Esse material pode ficar confuso quando espaços, sequências de comprimento zero e variables ​​nulas são todos necessários e também quando o script precisa trabalhar com vários shells.

Como declarar e usar variables ​​booleanas no shell script?

Ao contrário de muitas outras linguagens de programação, o Bash não segrega suas variables ​​por “tipo”. [1]

Então a resposta é bem clara. Não há boolean variable no bash. Contudo :

Usando uma instrução declare, podemos limitar a atribuição de valor a variables. [2]

 #!/bin/bash declare -ir BOOL=(0 1) #remember BOOL can't be unset till this shell terminate readonly false=${BOOL[0]} readonly true=${BOOL[1]} #same as declare -ir false=0 true=1 ((true)) && echo "True" ((false)) && echo "False" ((!true)) && echo "Not True" ((!false)) && echo "Not false" 

A opção r em declare e readonly é usada para indicar explicitamente que as variables ​​são somente de leitura . Espero que o propósito seja claro.

Longa história curta:

Tu não são booleanos em festança

O que o bash tem é expressões booleanas em termos de comparação e condições. Dito isso, o que você pode declarar e comparar no bash são strings e números. É isso aí.

Onde quer que você veja true ou false no bash, é uma string ou um comando / builtin que é usado apenas para o seu código de saída.

Esta syntax …

 if true; then ... 

é essencialmente …

 if COMMAND; then ... 

A condição é verdadeira sempre que o comando retorna o código de saída 0.

Você poderia muito bem fazer isso:

 if which foo; then echo "program foo found"; fi 

Ao usar colchetes ou o comando de test , você depende do código de saída dessa construção. Tenha em mente que [ ] e [[ ]] também são apenas comandos / builtins como qualquer outro. Assim …

 if [[ 1 == 1 ]]; then echo yes; fi 

é apenas açúcar sintático para …

 [[ 1 == 1 ]] && echo yes 

Portanto, ao usar true e false em qualquer uma das construções acima mencionadas, na verdade, você só estará passando a string "true" ou "false" para o comando testing. Aqui um exemplo:

Acredite ou não, mas essas condições estão produzindo o mesmo resultado :

 if [[ false ]]; then ... if [[ "false" ]]; then ... if [[ true ]]; then ... if [[ "true" ]]; then ... 

TL; DR; sempre compare com strings ou números

Para deixar isso claro para futuros leitores, recomendo sempre usar aspas em torno de true e false :

FAZ

 if [[ "${var}" == "true" ]]; then ... if [[ "${var}" == "false" ]]; then ... if [[ -n "${var:-}" ]]; then echo "var is not empty" ... 

NÃO FAÇA

 if [ ... ]; then ... # always use double square brackets in bash! if [[ "${var}" ]]; then ... # this is not as clear or searchable as -n if [[ "${var}" != true ]]; then ... # creates impression of booleans if [[ "${var}" -eq "true" ]]; then ... # `-eq` is for numbers and doesn't read as easy as `==` 

Talvez

 if [[ "${var}" != "true" ]]; then ... # creates impression of booleans. Can be used for strict checking of dangerous operations. This condition is false for anything but the literal string "true". 

Em vez de fingir um booleano e deixar uma armadilha para futuros leitores, por que não apenas usar um valor melhor que true e false?

Por exemplo:

 build_state=success if something-horrible; then build_state=failed fi if [[ "$build_state" == success ]]; then echo go home, you are done else echo your head is on fire, run around in circles fi 

POSIX (Interface do Sistema Operacional Portátil)

Sinto falta do ponto chave, que é a portabilidade. É por isso que meu header tem POSIX em si.

Essencialmente, todas as respostas votadas estão corretas, com exceção do BASH, específico demais.

Então, basicamente, desejo apenas adicionar mais informações sobre portabilidade.


  1. [ e ] colchetes como em [ "$var" = true ] não são necessários, e você pode omiti-los e usar o comando test diretamente:

     test "$var" = true && CodeIfTrue || CodeIfFalse 
  2. Imagine o que essas palavras true e false significam para o shell, teste você mesmo:

     echo $((true)) 
     0 
     echo $((false)) 
     1 

    Mas usando citações:

     echo $(("true")) 
     bash: "true": syntax error: operand expected (error token is ""true"") sh (dash): sh: 1: arithmetic expression: expecting primary: ""true"" 

    O mesmo vale para:

     echo $(("false")) 

    O shell não pode interpretá-lo além de uma string. Espero que você esteja começando a idéia de como é bom usar palavras-chave adequadas sem aspas.

    Mas ninguém disse isso em respostas anteriores.

  3. O que isto significa? Bem, várias coisas.

    • Você deve se acostumar com as palavras-chave booleanas são realmente tratadas como números, que é true = 0 e false = 1 , lembre-se de todos os valores diferentes de zero são tratados como false .

    • Como eles são tratados como números, você deve tratá-los assim também, ou seja, se você definir a variável diga:

       var_a=true echo "$var_a" 
        true 

      você pode criar um valor oposto a ele com:

       var_a=$((1 - $var_a)) echo "$var_a" 
       1 

      Como você pode ver por si mesmo, o shell imprime a string true pela primeira vez, mas desde então, tudo funciona pelo número 0 ou 1 , respectivamente.


Finalmente, o que você deve fazer com toda essa informação

  • Primeiro bom hábito seria atribuir 0 vez de true ; 1 vez de false .

  • Segundo bom hábito seria testar se a variável é / não é igual a zero:

     test "$var" -eq 0 && CodeIfTrue || CodeIfFalse 

Bill Parker está sendo rejeitado , porque suas definições são invertidas da convenção de código normal. Normalmente, true é definido como 0 e false é definido como diferente de zero. 1 funcionará para falso, assim como 9999 e -1. O mesmo acontece com os valores de retorno da function – 0 é bem-sucedido e qualquer coisa diferente de zero é uma falha. Desculpe eu não tenho a credibilidade da rua ainda para votar ou para responder diretamente a ele.

Bash recomenda o uso de colchetes duplos agora como um hábito em vez de colchetes, e o link que Mike Holt deu explica as diferenças em como eles funcionam. 7.3. Outros operadores de comparação

Por um lado, -eq é um operador numérico, então ter o código

 #**** NOTE *** This gives error message ***** The_world_is_flat=0; if [ "${The_world_is_flat}" -eq true ]; then 

irá emitir uma declaração de erro, esperando uma expressão inteira. Isso se aplica a qualquer parâmetro, pois nenhum deles é um valor inteiro. No entanto, se colocarmos colchetes duplos em torno dele, ele não emitirá uma declaração de erro, mas produzirá um valor incorreto (bem, em 50% das possíveis permutações). Ele irá avaliar para [[0 -eq true]] = sucesso, mas também para [[0 -eq false]] = sucesso, o que está errado (hmmm …. o que dizer de ser um valor numérico?).

 #**** NOTE *** This gives wrong output ***** The_world_is_flat=true; if [[ "${The_world_is_flat}" -eq true ]]; then 

Existem outras permutações do condicional que também darão resultados errados. Basicamente, qualquer coisa (diferente da condição de erro listada acima) que define uma variável como um valor numérico e a compara a uma verdadeira / falsa interna, ou define uma variável como uma verdadeira / falsa interna e a compara a um valor numérico. Além disso, qualquer coisa que defina uma variável como uma verdadeira / falsa interna e faz uma comparação usando -eq . Portanto, evite -eq para comparações booleanas e evite usar valores numéricos para comparações booleanas. Veja um resumo das permutações que fornecerão resultados inválidos:

 #With variable set as an integer and evaluating to true/false #*** This will issue error warning and not run: ***** The_world_is_flat=0; if [ "${The_world_is_flat}" -eq true ]; then #With variable set as an integer and evaluating to true/false #*** These statements will not evaluate properly: ***** The_world_is_flat=0; if [ "${The_world_is_flat}" -eq true ]; then # if [[ "${The_world_is_flat}" -eq true ]]; then # if [ "${The_world_is_flat}" = true ]; then # if [[ "${The_world_is_flat}" = true ]]; then # if [ "${The_world_is_flat}" == true ]; then # if [[ "${The_world_is_flat}" == true ]]; then #With variable set as an true/false builtin and evaluating to true/false #*** These statements will not evaluate properly: ***** The_world_is_flat=true; if [[ "${The_world_is_flat}" -eq true ]]; then # if [ "${The_world_is_flat}" = 0 ]; then # if [[ "${The_world_is_flat}" = 0 ]]; then # if [ "${The_world_is_flat}" == 0 ]; then # if [[ "${The_world_is_flat}" == 0 ]]; then 

Então, agora para o que funciona. Use builtins true / false para sua comparação e suas avaliações (como Mike Hunt observou, não coloque-as entre aspas). Em seguida, use um ou sinal de igual simples ou duplo (= ou ==) e colchetes simples ou duplos ([] ou [[]]). Pessoalmente, eu gosto do sinal de dois iguais, porque ele me lembra comparações lógicas em outras linguagens de programação e aspas duplas só porque eu gosto de digitar. Então, esses trabalhos:

 #With variable set as an integer and evaluating to true/false #*** These statements will work properly: ***** # The_world_is_flat=true/false; if [ "${The_world_is_flat}" = true ]; then # if [[ "${The_world_is_flat}" = true ]]; then # if [ "${The_world_is_flat}" = true ]; then # if [[ "${The_world_is_flat}" == true ]]; then 

Lá você tem isso.

Aqui está uma implementação de um curto entregue if true .

 # Function to test if a variable is set to "true" _if () { [ "${1}" == "true" ] && return 0 [ "${1}" == "True" ] && return 0 [ "${1}" == "Yes" ] && return 0 return 1 } 

Exemplo 1

 my_boolean=true _if ${my_boolean} && { echo "True Is True" } || { echo "False Is False" } 

Exemplo 2

 my_boolean=false ! _if ${my_boolean} && echo "Not True is True" 

Aqui está um exemplo simples que funciona para mim:

 temp1=true temp2=false if [ "$temp1" = true ] || [ "$temp2" = true ] then echo "Do something." else echo "Do something else." fi 

Eu achei as respostas existentes confusas.

Pessoalmente, eu só quero ter algo que pareça e funcione como C.

Este snippet funciona muitas vezes ao dia em produção:

 snapshotEvents=true if ($snapshotEvents) then # do stuff if true fi 

e para manter todo mundo feliz, eu testei:

 snapshotEvents=false if !($snapshotEvents) then # do stuff if false fi 

Que também funcionou bem.

O $snapshotEvents avalia o conteúdo do valor da variável. Então você precisa do $ .

Você realmente não precisa dos parênteses, eu apenas os acho úteis.

  • Testado em: GNU Bash, versão 4.1.11 (2) -release

  • Guia de Bash para Iniciantes , Machtelt Garrels, v1.11, 2008

Aqui está uma melhoria na resposta original de miku, que aborda as preocupações de Dennis Williamson sobre o caso, onde a variável não está definida:

 the_world_is_flat=true if ${the_world_is_flat:-false} ; then echo "Be careful not to fall off!" fi 

E para testar se a variável é false :

 if ! ${the_world_is_flat:-false} ; then echo "Be careful not to fall off!" fi 

Sobre outros casos com um conteúdo desagradável na variável, isso é um problema com qualquer input externa alimentada para um programa.

Qualquer input externa deve ser validada antes de confiar nela. Mas essa validação deve ser feita apenas uma vez, quando essa input é recebida.

Não tem que impactar o desempenho do programa, fazendo-o em todo uso da variável como Dennis Williamson sugere.

Use o senso comum. Em muitos idiomas, 1 é verdadeiro e 0 é falso. Felizmente isso também acontece com o Bash.

Entrada:

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

Saída:

 true false 

Fonte :

((expressão))

A expressão é avaliada de acordo com as regras descritas abaixo em AVALIAÇÃO ARITMÉTICA. Se o valor da expressão for diferente de zero, o status de retorno será 0; caso contrário, o status de retorno é 1. Isso é exatamente o equivalente a deixar “expressão”.

Bash realmente confunde o problema com os gostos de [ , [[ , (( , $(( , etc.)

Todos tratando dos espaços de código dos outros. Eu acho que isso é principalmente histórico, onde Bash teve que fingir ser ocasionalmente.

Na maior parte do tempo, posso apenas escolher um método e continuar com ele. Neste caso, tenho a tendência de declarar (de preferência em um arquivo de biblioteca comum que eu possa include com o (s) meu (s) script (s) real (is).

 TRUE=1; FALSE=0 

Eu posso então usar o operador aritmético (()) para testar assim.

 testvar=$FALSE if [[ -d ${does_directory_exist} ]] then testvar=$TRUE; fi if (( testvar == TRUE )); then # do stuff because the directory does exist fi 
  1. Você tem que ser disciplinado. Seu testvar deve ser definido como $TRUE ou $FALSE em todos os momentos.

  2. Em (()) comparadores, você não precisa do $ anterior, o que o torna mais legível.

  3. Eu posso usar (()) porque $TRUE=1 e $FALSE=0 , ou seja, valores numéricos.

  4. A desvantagem é ter que usar $ ocasionalmente:

     testvar=$TRUE 

    o que não é tão bonito.

Não é uma solução perfeita, mas cobre todos os casos, eu preciso de tal teste.