Qual é a melhor prática para representar um valor booleano em um script de shell?

15

Eu sei que existem valores booleanos bash, mas nunca os vejo usados ​​em lugar algum.

Quero escrever um invólucro para algumas informações frequentemente pesquisadas na minha máquina, por exemplo, se essa unidade USB específica foi inserida / montada.

Qual seria a melhor prática para conseguir isso?

  • Uma linha?

    drive_xyz_available=true
  • Um número (0 para verdadeiro, ≠ 0 para falso)?

    drive_xyz_available=0    # evaluates to true
  • Uma função?

    drive_xyz_available() { 
        if available_magic; then 
                return 0 
        else 
                return 1 
        fi
    }
    

Eu me pergunto principalmente sobre o que seria esperado por outras pessoas que gostariam de usar o wrapper. Eles esperariam que um valor booleano, um comando como variável ou uma função chamasse?

Do ponto de vista da segurança, acho que a segunda opção é a mais segura, mas adoraria ouvir suas experiências.

Minix
fonte
3
help true ; help false ; help exit
Cost
3
@Costas Você se importaria de elaborar?
Minix

Respostas:

2
bool(){ return "$((!${#1}))"; }

if bool "$var"
then : do true
else : do false

Basta definir uma variável para qualquer coisa, exceto não nulo, para que o acima funcione, mas [ -n "$var" ]seria mais curto, se não tão óbvio.

Em geral, quando um script interpreta uma variável de ambiente como verdadeira ou falsa, ele interpreta qualquer valor como verdadeiro (e às vezes usa o referido valor para configurar alguma opção) ou um valor nulo como falso.

O exemplo acima retorna o !notvalor booleano do len do seu primeiro argumento - se o argumento contiver qualquer número de caracteres diferente de 0, ele retornará 0; caso contrário, se não houver caracteres, ele retornará 1. Este é o mesmo teste com o qual você pode executar [ -n "$var" ], basicamente , mas ele só quebra-lo em um pouco de função chamada bool().

Normalmente, é assim que uma variável de flag funciona. Por exemplo:

[ -d "$dir" ] || dir=

Onde outras partes de um script precisam apenas procurar qualquer valor $dirpara avaliar sua utilidade. Isso também é útil no que diz respeito à substituição de parâmetros - pois os parâmetros podem ser expandidos para valores padrão para preencher valores vazios ou não definidos, mas, caso contrário, serão expandidos para um valor predefinido como ...

for set in yes ''
do echo "${set:-unset or null}"
done

... o que imprimiria ...

yes
unset or null

Obviamente, também é possível fazer o oposto, :+mas isso só pode fornecer um padrão predefinido ou nada, enquanto o formulário acima pode fornecer um valor ou um valor padrão.

E assim, com relação às três opções - qualquer uma pode funcionar dependendo de como você escolhe implementá-la. O retorno da função é autoteste, mas, se esse retorno precisar ser salvo por qualquer motivo, precisará ser colocado em uma variável. Depende do caso de uso - é o valor booleano que você deseja avaliar um teste uma vez e o tipo pronto? Nesse caso, execute a função; caso contrário, provavelmente será necessário um dos outros dois.

mikeserv
fonte
1
Eu não acho que você está respondendo minha pergunta. Não estou perguntando como os booleanos poderiam ser usados ​​em um script de shell, mas de que maneira é a mais comum e seria esperada por outro usuário. Se minha pergunta não estiver clara, ficarei feliz em editá-la. Também uma breve explicação para o que sua resposta faz seria legal. Obrigado.
Minix
@Minix Melhor?
mikeserv
Eu geralmente atribuir trueou falsea uma variável, você pode então fazerif $variable; then ...
wurtel
@wurtel - que se baseia no padrão $IFS- e se o valor do var puder conter qualquer tipo de entrada do usuário, também haverá pura sorte. Se o valor não for desconhecido, para começar, há pouca necessidade de testá-lo. Mais segurança você pode fazer if ${var:+":"} false; thenao trabalhar com valores nulos booleanos / não nulos. Mas isso raramente é mais útil do que[ -n "$var" ] &&
mikeserv
Se eu iniciar o meu script variable=falsee defini-lo como verdadeiro de acordo com qualquer condição (como faria ao usar uma variável em C), não haverá problema algum: qual é a sua obsessão com valores variáveis ​​do IFS e valores aleatórios de variáveis ​​etc.? .
wurtel
4

Em bashtoda variável há essencialmente uma string (ou uma matriz ou uma função, mas vamos falar sobre variáveis ​​regulares aqui).

As condições são analisadas com base nos valores de retorno dos comandos de teste - o valor de retorno não é uma variável, é um estado de saída. Quando você avalia if [ ... ]ou if [[ ]]ou if grep somethingalgo assim, o valor de retorno 0 (não a cadeia 0, mas o status de saída 0 = sucesso) significa verdadeiro e o restante significa falso (portanto, exatamente o oposto do que você está acostumado nas linguagens de programação compiladas, mas como há uma maneira de obter sucesso e muitas maneiras de falhar, e o resultado esperado da execução geralmente é bem-sucedido, 0 é usado como o resultado padrão mais comum se nada der errado). Isso é muito útil porque qualquer binário pode ser usado como teste - se falhar, é falso, caso contrário, é verdade.

truee falseprogramas (geralmente substituídos por componentes internos) são apenas pequenos programas úteis que não fazem nada - trueconseguem não fazer nada e sai com 0, enquanto falsetentam não fazer nada e "falham", saindo com 1. Parece inútil, mas é muito útil para scripts.

Quanto a como divulgar a verdade, depende de você. É bastante comum usar apenas "y" ou "yes" como verdade e uso if [ x"$variable" = x"yes" ](anexada a string fictícia xporque, se tiver $variablecomprimento zero, isso protege contra a criação de um comando falso if [ = "yes" ]que não analisa). Também pode ser útil simplesmente usar uma string vazia para false e [ -z "$variable ]testar se o comprimento é zero (ou -nnão é zero).

De qualquer forma, é muito raro precisar transmitir valores booleanos bash- é muito mais comum simplesmente exitfalhar ou retornar um resultado útil (ou zero se algo der errado e testar a cadeia vazia), e a maioria dos casos pode teste a falha diretamente do status de saída.


No seu caso, você deseja uma função que atue como qualquer outro comando (portanto, retorne 0 em caso de sucesso); portanto, sua última opção parece a escolha certa.

Além disso, você pode nem precisar de returninstruções. Se a função for simples o suficiente, você poderá usar o fato de simplesmente retornar o status do último comando executado na função. Portanto, sua função pode ser simplesmente

drive_xyz_available() {
   [ -e /dev/disk/by-uuid/whatever ]
}

se você estiver testando a existência de um nó de dispositivo (ou grep /proc/mountspara verificar se ele está montado?).

orion
fonte
Esse é um resumo muito bom, obrigado por reservar um tempo para escrevê-lo. Posso deduzir do seu último parágrafo que você consideraria a opção de drive_xyz_available()ser a mais comum?
Minix
Você pode fornecer alguns exemplos para o y = truecaso comum ? Na minha experiência, a maioria dos scripts de wrapper testa qualquer valor não nulo para considerá-lo verdadeiro - pelo menos no que diz respeito às variáveis ​​de ambiente interpretadas. Caso contrário, eles ignoram completamente os vagões.
mikeserv
3
A sequência fictícia para o ifteste não é necessária se a variável for citada; if [ "$variable" = "yes" ]funciona bem mesmo que a variável $ não esteja definida.
Daniel kullmann
-2

Basicamente, qualquer número que não seja 0 é verdadeiro e 0 é falso. Razão para retornar valores como 0 para um final bem-sucedido de um script ou qualquer outro número para identificar tipos diferentes de erros.

$? retornará o código de saída do comando / executável anterior, sendo 0 sucesso e qualquer outro número o erro que foi retornado.

Então, eu usaria esse método para verdadeiro / falso. if (( ! $? ));then OK;else NOOK;fi

YoMismo
fonte
Vou escrever uma para a última opção, então. Obrigado.
Minix
5
Na verdade, um número diferente de zero é falso e zero é verdadeiro. Basta olhar para a saída de true; echo $?e false; echo $?.
Ruslan
@Ruslan. Você verificou a saída do meu comando? Acho que não, caso contrário você não teria declarado o que fez. 0 é falso, qualquer outro número é verdadeiro, motivo para negar !o resultado para que seja VERDADEIRO. O resultado de um comando é 0 quando finalizado corretamente, o que não significa que 0 seja verdadeiro. A palavra truepode ser 0, mas 0 nunca é verdadeiro como a ifcondição demonstra.
YoMismo 23/02
É o mesmo que dizer que em C / C ++, 0é trueporque if(!x){True();}else{False();}chamará True()quando x==0. Mas a verificação correta não seria !x, mas sim !!x.
Ruslan
Você está errado. Eu não estou falando sobre o que e qual ordem tem o que você escreve depois de ifeu apenas declarar o fato de que aqui (em bash, ou ksh, ou tsh, ou ....) como em C / C ++ a 0 é FALSO, qualquer outro número é TRUE, como você pode ler no link a seguir, implementações iniciais de C não forneciam tipo booleano, sendo definidas como ints em que 0 era FALSE e 1 TRUE en.wikipedia.org/wiki/Boolean_data_type .
YoMismo 23/02