Dúvidas sobre o if (bash)

Iniciado por rudregues, 25 de Fevereiro de 2015, 08:09

tópico anterior - próximo tópico

rudregues

Galera, gostaria de saber porque o if tem que ser assim:
if [ "$a" = "0" ]; then  echo 'oi'; fi;

Mas não pode ser de uma das três maneiras a seguir:
if [ $a = "9" ]; then  echo 'oi'; fi;     -> $a pra mim seria um número e "$a" seria a string '$a'
if ["$a"="9"]; then  echo 'oi'; fi;     -> já que tem que estar entre aspas, porque não posso deixar tudo junto, tem que estar espaçado?
Gentoo — Controle total sobre o sistema.

rudregues

Citação de: rudregues online 25 de Fevereiro de 2015, 08:09
if ["$a"="9"]; then  echo 'oi'; fi;     -> já que tem que estar entre aspas, porque não posso deixar tudo junto, tem que estar espaçado?

Depois de uma boa pesquisa, descobri porque tem que ser separado: o argumento entre colchetes é uma lista. Ou seja, fica if [ lista ], neste caso a lista precisa ter cada um dos argumentos executados e comparados separadamente, se juntar é como se fosse um comando só. Mas obviamente se eu juntar "$a"="9" não é um comando, porém "$a" é um comando e "9" é outro.

Mas ainda não entendi porque precisa ser "$a" ao invés de $a
Gentoo — Controle total sobre o sistema.

byjav

Senão, o valor do variável podia ter espaços que seriam encarados como delimitadores de comandos. Por exemplo,
$ if [ "-n foo -a 0" = 0 ]; then echo sim; else echo não; fi
não

porque o if testa se a cadeia "-n foo -a 0" é igual à cadeia "0", enquanto
$ if [ -n foo -a 0 = 0 ]; then echo sim; else echo não; fi
sim

porque o if testa se a cadeia "-n" não é vazia e que no mesmo tempo a cadeia "0" é igual à cadeia "0". Então, se o variável a tiver o valor de "-n foo -a 0" e não houver aspas, o resultado será inesperado.

irtigor

Mais ou menos certo, mas totalmente errado.


a=0
if [ $a = "0" ]; then  echo 'oi'; fi;
if [ "$a" = "0" ]; then  echo 'oi'; fi;


Ambos vão funcionar, mas tem que ficar atento, porque o "=" serve somente pra comparações de strings. Para comparações aritméticas, o equivalente é o "-eq" ("[" é o comando test, "$(which [) --help" ).

Agora o ultimo realmente não vai funcionar nunca, isso porque "[" , assim como o "echo" e outros, historicamente são programas que hoje em dia estão embutidos na maioria do shells ("type ["). Então existe o programa "[", que aceita alguns argumentos, e espera como o ultimo deles um "]". Isso cria limitações, "[qualquer_coisa" não vai ser encontrado no $PATH (porque o nome do programa é só "["), e sem um espaço entre qualquer_coisa e "]", o ultimo argumento não vai ser "]" e sim "qualquer_coisa]". Quando embutiram esse programa nos shells, essas limitações foram mantidas "artificialmente", pra ter compatibilidade com o programa original.

byjav

$ a="-n foo -a 0"
$ if [ "$a" = 0 ]; then echo sim; else echo não; fi
não
$ if [ $a = 0 ]; then echo sim; else echo não; fi
sim


Tudo funciona perfeitamente, só é preciso compreender o que se passa. :)

irtigor

Por isso você está mais ou menos certo, mas completamente errado. O segundo if está correto, mas não é recomendado, e a sua mensagem também não explicava o porque do espaço depois do [ e antes do ], ou da não diferença entre "$a" e $a quanto ao tipo.

byjav

Então acha que há erros. O que é, precisamente, que escrevi falso?

Ao contrário, eu afirmo que não é correcto dizer que «ambos vão funcionar». Como já demonstrei, os resultados podem ser diferentes. Além disso, a versão sem aspas pode acabar com erro:
$ a="foo bar"
$ if [ $a = 0 ]; then echo sim; else echo não; fi
bash: [: too many arguments
não

O que é preciso compreender é que as aspas não servem como delimitadores de cadeias (nem fazem parte da sintaxe do if), mas para privar alguns caracteres, incluindo o espaço, das funções especiais.

irtigor

O que estou dizendo é que levando em conta o contexto, a sua resposta no melhor dos casos é incompleta, não que aquilo não acontece. Não tem motivo nenhum pra ficar na defensiva.

rudregues

Acho que eu entendi (tive que ler algumas vezes rs). Vamos lá:

Se eu declaro:

a="-n foo bla 0 8"

e coloco $a dentro da condição do if, o cifrão meio que come as aspas e transforma aquilo numa cadeia de comandos:

$a será igual a -n foo bla 0 8

porém se eu coloco "$a" ele verá essa cadeia de comandos como uma string, ou seja:
"$a" será igual a "-n foo bla 0 8"


É isso mesmo ???
Gentoo — Controle total sobre o sistema.

irtigor

Isso foge um pouco da pergunta original, mas mostra porque uma das formas deve ser evitada. As aspas curvas e a contrabarra influenciam o jeito como a sua entrada é tratada, por isso há casos em que deve-se preferir um método especifico. Ex


#!/usr/bin/bash -x

z="xx yy kk"
echo $z
echo "$z"


Você não armazena "xx yy kk" na variável z, a sua entrada é xx{espaço}yy{espaço}kk, e as aspas ao redor indicam que essa cadeia não deve ser separada em palavras por espaços. O mesmo acontece no echo "$z", essas aspas não impedem a expansão de parâmetros, então o bash substitui $z pelo seu conteúdo que é xx{espaço}yy{espaço}kk, mas elas não vão deixar a separação da cadeia em palavras com base no espaço.

Existem outras consequências: http://wiki.bash-hackers.org/syntax/quoting

irtigor

Pra expandir em um ponto que toquei de leve, e aparentemente passou batido, no bash você até pode declarar uma variável com um tipo, mas o comum é usá-lo sem tipo (com tudo como strings, e com inferência/definição implícita em alguns casos). Isso faz sentido porque o shell é usado como uma linguagem de cola, você normalmente passa argumentos pra programas externos, e como a coisa é representada não faz parte do seu domínio.

http://mywiki.wooledge.org/BashGuide/Parameters#Variable_Types

rudregues

muito interessante irtigor, problema resolvido!
(aprendi até coisas que nem imaginava rs)
Gentoo — Controle total sobre o sistema.