paulo1205
(usa Ubuntu)
Enviado em 29/04/2018 - 10:22h
Mauriciodez escreveu:
EDIT: pra q os colchetes duplicados no if ????
Eles são uma forma mais eficiente de fazer testes, garantindo que o
shell não vai chamar um comando externo para testar o que você quer testar.
Permita-me contar uma historinha.
O
shell tradicional não tinha um mecanismo próprio de avaliar expressões relacionais nem aritméticas, e mesmo expressões booleanas eram limitadas a situações em que os valores testados eram o código de retorno resultante da execução de um comando (comandos bem sucedidos, com código de retorno igual a zero, correspondiam ao valor lógico verdadeiro, e comandos com código de retorno diferente de zero eram entendidos como tendo falhado, com valor lógico falso). O próprio comando
if se limitava apenas a executar um comando e ver se ele executava com sucesso com falha.
Para conseguir testar expressões relacionais e para avaliar expressões aritméticas, era preciso recorrer a comandos externos. O comando
test recebia uma expressão lógica ou relacional a ser testada, e devolvia código de retorno correspondente ao resultado do teste (zero se verdadeiro, diferente de zero se falso). Para expressões aritméticas, o comando externo se chamava
expr, e sua saída era textual, impressa na saída padrão, de modo que se um
script quisesse usá-la para fazer uma atribuição de valor a uma variável, ele tinha de redirecionar a saída, por meio da execução entre dois sinais de acento grave ("
`").
Sendo assim, no
shell tradicional, o primeiro
script mostrado por nosso colega oxidante teria de ser reescrito mais ou menos da seguinte maneira.
nerro=0
cp -p a1 b1 || nerro=`expr $nerro + 1`
cp -p a2 b2 || nerro=`expr $nerro + 1`
if test "$nerro" -ne 0; then
echo "Houve $nerro erro(s)."
else
echo "Cópia com sucesso."
fi
Lá pelas tantas, alguém deve ter achado que escrever "
if test expressão" era muito feio ou chato, e resolveu rebatizar o comando
test como
[, a fim de permitir escrever expressões na forma "
if [ expressão ]". De fato, isso é visualmente mais confortável, mas, no fundo, pouca coisa mudou. A sintaxe aparente ficou mais interessante, mas
[ continuou sendo um comando externo (veja que deve existir, na sua máquina, um arquivo chamado
/bin/[, que é um segundo nome do arquivo
/bin/test). Por ser um comando externo, ele continuou tendo os seguintes inconvenientes:
• custo de execução, tanto em tempo quanto em recursos, por ter de criar um processo novo, chamar um executável, aguardar sua execução, e fazer a limpeza dos recursos depois de ele ter terminado;
• por ser um comando externo, ele só seria executado se a variável de ambiente
PATH contivesse o diretório
/bin (ou
/usr/bin, dependendo do sistema; essa dependência, aliás, é outro sintoma de inconveniência);
• se, por algum acidente, o arquivo
/bin/[ tivesse sido apagado, os
scripts que o invocassem deixariam de conseguir efetuar seus testes relacionais;
• um problema de segurança podia acontecer: se o
script não tivesse cuidado, um valor indevido na variável
PATH poderia fazer com que um outro arquivo chamado
[, hospedado num outro diretório, fosse executado em lugar do
/bin/[;
• a sintaxe era mesmo apenas aparente: o fechamento dos colchetes, que ajudava nessa suposta sintaxe, não era exigido pelo
shell, mas sim pelo próprio comando, que acabava tendo de ter dois comportamentos diferentes, dependendo de se tivesse sido invocado como
[ ou como
test.
Quando se criou o Korn-Shell, com a esperança de que ele viesse a substituir o
shell tradicional, tentou-se tratar pelo menos uma parte dos problemas acima, mas de um modo tão conservador que acabou surtindo menos efeito do que o que se pretendia. O
ksh introduziu novas formas de efetuar avaliações lógicas e aritméticas sem usar comandos externos, por meio de comandos internos como
[[ ...
]] e
(( ...
)), e havia a indicação de que essas formas deveriam ser preferidas em relação ao uso dos comandos externos
test/
[ e
expr, mas não se forçou que isso fosse feito, nem alterou o comportamento do
shell quando esses comandos externos apareciam: eles continuavam sendo executados como comandos externos. Além disso, os fabricantes temiam incompatibilidades do novo
shell com
scripts antigos, e continuaram distribuindo o
shell tradicional como
/bin/sh, com o Korn-Shell tendo de ser explicitamente referenciado como
[/bin/ksh, o que atrapalhou ainda mais sua ampla adoção. E, para piorar ainda mais, a batalha legal entre a AT&T de um lado e a UCB (
University of California, Berkeley) e BSDI (
Berkeley Software Design, Inc.) de outro fez com que o mundo
open source não pudesse ter acesso ao Korn-Shell durante muitos anos, tendo de usar ou o
shell tradicional ou o C-Shell do BSD (um
shell, à época, inovador em diversos aspectos, mas com vários inconvenientes quando usado para fazer
scripts, a ponto de estar quase abandonado hoje em dia).
Com o surgimento do Bash e sua ampla adoção, o mundo
open source teve uma nova oportunidade de lidar com as questões desempenho e de compatibilidade com o
shell tradicional. O Bash adotou as novas formas introduzidas pelo Korn-Shell, mas deu um passo além, incorporando como comandos internos as formas tradicionais de
test e
[. Desse modo, esperava-se que
scripts escritos com a sintaxe tradicional pudessem ganhar desempenho quando executados pelo Bash (especialmente em sistemas que tivessem a ousadia de fazer do Bash seu
shell padrão, hospedado como
/bin/sh, como é o caso do Red Hat e afins) mas continuassem funcionando num
shell tradicional, até porque muitos sistemas continuavam e continuam usando
shells menores e supostamente mais leves como padrão em
/bin/sh (Debian e derivados, todos os BSDs, UNIXes comerciais etc.). Além do mais, mesmo quando executando sobre o Bash, as versões internas dos comandos tradicionais podem ser desabilitadas por meio do comando
enable com opção
-n.
A versão com dois colchetes, bem como os dois parênteses para operações aritméticas, são exclusivamente internas, e não podem ser desabilitadas. São, portanto, mais seguras.
Na prática, porém, problemas de compatibilidade acabam existindo também com o Bash. Mesmo que se desabilitem as versões internas dos comandos aqui discutidos, é muito tentador usar outros recursos do Bash que não existem no
shell tradicional, tais como mais e melhores operações com
strings, mais e melhores opções de expansões e de redirecionamentos,
arrays e
arrays associativos, melhor suporte a
locales e muitos outros. Pelo aspecto de segurança, desabilitar as versões internas dos comandos seria obviamente pouco interessante, mas seria relativamente difícil impedir que um eventual atacante conseguisse provocar essa desabilitação.
Assim sendo, minha recomendação pessoal é que, se o seu
script tiver que usar algum recurso do Bash, você explicite isso na primeira linha (i.e. usando “
#/bin/bash”, em vez de ”
#!/bin/sh”), e prefira usar todos os recursos que o tornam mais seguro, incluindo usar “
[[ ... ]]” em lugar de “
[ ... ]”.
Em tempo: eu estou ciente de que existe uma diferença entre os dois testadores, referente ao uso de comparadores de
strings, pois o de
test/
[ usa sempre a ordenação do ASCII, ao passo que
[[ usa a
locale corrente. Mas isso é muito fácil de contornar, bastando mudar a
locale corrente para “C”, antes de invocar a comparação com
[[.