Você não esperaria:
var='echo test && reboot'
echo $var
Para produzir test
e depois reiniciar, você faria?
Em vez disso, gera echo test && reboot
Isso é o mesmo para
var='echo test && reboot'
$var
Agora, onde se torna confuso é que na maioria dos shells parecidos com o Bourne, ele não falha com um erro que diz que o comando echo test && reboot
não foi encontrado. Em vez disso, e a menos que você tenha modificado $IFS
, ele gerará test && reboot
.
Isso porque, nesses shells, deixar uma variável sem aspas aplica algum tipo de operador split + glob.
Como $var
não é citado, ele é dividido em palavras (de acordo com o parâmetro $IFS
special): echo
, test
, &&
, reboot
e o comando a ser executado é derivado do primeiro ( echo
) e todas as palavras passadas como argumentos para esse comando. &&
é um argumento para echo
lá. Apenas um literal &&
sem aspas será entendido como o operador &&
da linguagem sh
. Como em C, var="foo, bar"; printf(var)
não significa que dois argumentos são passados para printf
apenas porque var
contém ,
, que é um operador na sintaxe da linguagem C.
Na verdade, se voltarmos na história para as primeiras versões do Unix no início dos anos 70, o sh
(hoje chamado de Thompson shell ) teria rodado reboot
lá . Bem, sh
naquela época não suportava variáveis, mas tinha parâmetros posicionais.
Aqui usando o Unix V1 em um emulador PDP11:
$ cat myscript
echo $1
$ sh myscript 'echo test; reboot'
echo test
No command
$ ls -l /bin/sh
total 12
88 lxrwr- 1 sys 5312 Jan 1 00:00:00 sh
(felizmente, não havia nenhum comando reboot
, portanto, o erro No command
).
Isso pode parecer loucura pelo padrão atual, mas lembre-se de que o Unix V1 era executado em máquinas com algumas centenas de kilobytes de memória e megabytes de armazenamento, então as coisas tinham que ser mantidas simples na época. Veja como sh
é significativamente menor que /bin/true
(o comando que não faz nada) em um sistema moderno (27KB no meu sistema (sem contar o vinculador dinâmico ou as bibliotecas dinâmicas com as quais ele está vinculado) contra 5KB para esse sh).
O Unix V7 no final dos anos 70 veio com o shell Bourne, o novo sh
. Muito mais avançado, mas ainda não quebrou completamente a compatibilidade com o seu antecessor. O mesmo para o csh que ocorreu mais ou menos na mesma hora no BSD. É provavelmente a principal razão para esse comportamento desajeitado da shell Bourne: a expansão dos parâmetros ainda sofre algumas formas de expansão, como a divisão em palavras e também globbing (mas não tanto quanto a reinterpretação da linguagem sh shish como no início do Unix shells como isso seria loucura), por compatibilidade retroativa com as conchas Thomson.
Alguns shells como rc
, fish
ou zsh
(aquele que é um shell parecido com o Bourne) acabaram sendo corrigidos posteriormente, mas muitos incluindo bash
mantiveram compatibilidade retroativa com o shell Bourne.
Agora, se você quiser interpretar uma string como código shell, é para isso que serve o comando eval
:
eval "$var"
(observe as aspas para evitar o split + globbing em shells parecidos com Bourne).
Mas se você quiser armazenar código em uma variável, faria muito mais sentido usar uma função como em outros idiomas:
f() { echo test && reboot; }