De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
Colaboração: Julio Cezar Neves
Data de Publicação: 04 de outubro de 2019
Costumo dizer que a qualidade do programador Shell é inversamente proporcional à quantidade de vezes que ele usa o comando cat
e a partir de agora, veremos que essa instrução não deveria quase nunca ser usada em scripts.
Vejo muito algumas coisas assim:
Como vejo por aí | Como deveria ser |
---|---|
cat ARQ | grep ... |
grep ... ARQ |
cat ARQ | cut ... |
cut ... ARQ |
cat ARQ | tr ... |
tr ... < ARQ |
cat ARQ | more |
more ARQ |
E a lista não acaba aí, serve pra wc
, less
, ... já existe até um apelido para isso: cat command abuse.
O pipe (|
) é um cara do qual tenho um excelente conceito porque quebra altos galhos, porém deve ser usado com moderação, pois ele sempre criará um subshell o que implica a carregar um Shell filho e copiar para ele todo o ambiente do Shell pai. Tudo que for alterado ou adicionado no filho, será perdido quando o controle voltar para o Shell pai e muitas vezes o pipe (|
) pode ser evitado com um redirecionamento da entrada padrão. Veja um exemplo banal:
Meu script precisa saber quantas palavras tem no arquivo telefones
. Eu sei que posso contar palavras com wc -w
. Então veja essa análise:
$ wc -w telefones
24 telefones
Ora não era bem o que queria, pois para ter somente a quantidade de palavras eu teria de fazer:
$ wc -w telefones | cut -f 1 -d ' '
24
Puxa lá está o pipe (|
). Mas se eu passar o nome do arquivo por pipe (|
), o cat
não saberá de quem se trata e simplesmente listará somente o que quero. Veja:
$ cat telefones | wc -w
24
Tá melhor mas ainda tem pipe (|
). Então a melhor solução é redirecionar o arquivo via entrada padrão fazendo:
$ wc -w < telefones
24
Desta forma o wc
não sabia o nome do arquivo, pois seus dados vinham de StdIn e não foi necessário criar o fatídico subshell.
Experimente fazer um loop assim:
time for ((i=1; i<200; i++)) { PONHA CADA UM DESSES CMDs AQUI >/dev/null }
Com isso veremos o tempo de 200 execuções de cada uma dessas linhas de comandos que mostramos, mas não se esqueça de trocar o nome do arquivo para um que você tenha (por exemplo /etc/passwd
). Desviei a saída para /dev/null
porque o que interessa é o tempo e não a quantidade de palavras.
Aposto o chope como o último é bem mais rápido que os outros e o segundo é o pior deles
Vou executar esse loop, mas com outros exemplos para que você entenda bem. Existem duas formas de colocar o conteúdo de um arquivo dentro de uma variável:
Var=$(cat /etc/passwd)
Var=$(</etc/passwd)
Vamos medir os tempos de cada um fazendo 200 loops:
Primeiro caso:
time for ((i=1; i<200; i++))
{
Var=$(cat /etc/passwd)
}
real 0m0.262s
user 0m0.032s
sys 0m0.040s
Segundo caso:
time for ((i=1; i<200; i++))
{
Var=$(< /etc/passwd)
}
real 0m0.149s
user 0m0.032s
sys 0m0.032s
Como vocês viram o negócio é fazer redirecionamento de entrada.
Nessa rodada de dicas sobre redirecionamento já expliquei como mandar erros para a saída de erros (StdErr) na dica de amanhã mostrarei dois macetes matadores:
|
), Here Strings (<<<
) ou Redirecionamento do StdIn (<
);
This policy contains information about your privacy. By posting, you are declaring that you understand this policy:
This policy is subject to change at any time and without notice.
These terms and conditions contain rules about posting comments. By submitting a comment, you are declaring that you agree with these rules:
Failure to comply with these rules may result in being banned from submitting further comments.
These terms and conditions are subject to change at any time and without notice.
Comentários