Grupos de comandos Expansão de strings
e nomes de arquivos
O Bash permite agrupar comandos, que desse modo
são executados como uma única unidade.
Podemos agrupar os comandos de duas formas:
1) Agrupamento por parênteses, "( )":
Comandos colocados entre parênteses e separados
por ponto-e-vírgula, ";", são agrupados e executados
serialmente invocando um subshell. Exemplo:
# (ls; du; find -name \*.txt)
2) Agrupados por chaves, "{ }":
Comandos colocados entre chaves e separados
por ponto-evírgula, ";", são agrupados e executados
serialmente no shell corrente. Exemplo:
# {ls; du; find -name \*.txt}
Você deve estar pensando: "Para que eu agruparia meus comandos? Para deixá-los mais bonitos?"
Não!
A vantagem de vários comandos agrupados pode ser vista quando precisamos desviar a saída desses comandos.
Imagine que você precise fazer um script para executar vários comandos e obter a saída de erros de cada comando colocado no script. Seria assim:
...
comando1 2>erros.txt
comando2 2>>erros.txt
comando3 2>>erros.txt
comando4 2>>erros.txt
...
Com o agrupamento isso fica mais fácil e simples:
...
(
comando1 ;
comando2 ;
comando3 ;
comando4 ;
) 2>erros.txt
...
Observe que no primeiro exemplo desviamos o erro para cada comando e no segundo exemplo o desvio foi feito para todos os comandos em uma única vez.
Se esse exemplo fosse passado para a linha de comando ficaria desta forma: exemplo 1:
# comando1 2>erros.txt; comando2 2>>erros.txt ; comando3 2>>erros.txt comando4 >>erros.txt
exemplo 2:
#(comando1;comando2;comando3;comando4;) 2> erros.txt
Você concorda que, com agrupamento, a linha de comando fica até mais legível! Agora vamos imaginar que você precise tratar a saída de vários comandos, por exemplo, alterar de minúsculo para maiúsculo. Você poderia escrever assim:
# cat /etc/hosts | tr a-z A-Z > saida.txt ;
cat /etc/resolv.conf |tr a-z A-Z >>saida.txt ;
cat /etc/sysconfig/network | tr a-z A-Z >>saida.txt
Mas o ideal seria:
# (cat /etc/hosts ; cat /etc/resolv.conf ;
cat /etc/sysconfig/network; )| tr a-z A-Z >>saida.txt
No segundo exemplo, além de ficar mais legível, o comando "tr" é executado uma única vez, já no primeiro exemplo cada um dos comandos será executado.
A diferença entre os grupos criados com parênteses, "( )", ou os criados com chaves, "{ }", pode ser vista no comando abaixo:
# VAR="ANTES";(VAR="DEPOIS";VAR="ADIANTE";); echo $VAR
ANTES
# VAR="ANTES";{VAR="DEPOIS";VAR="ADIANTE";}; echo $VAR
ADIANTE
Observe que no exemplo escrito com parênteses a variável VAR retornou ao mesmo valor que fora atribuído antes de executar o grupo de comandos. Isso se deve ao fato de que os comandos são executados em subshell. Já no exemplo escrito com chaves a variável VAR retornou ao último valor que fora atribuído, porque grupos feitos com chaves são executados no shell corrente.
Expansão de strings
Expansão de strings é um mecanismo que combina uma expressão inicial com um conjunto de caracteres. Exemplo:
#echo a{1,2,3}
a1 a2 a3
As combinações são feitas entre os elementos que estão entre as chaves separados por vírgula, da esquerda para a direita.
Podemos combinar mais que um conjunto de caracteres simultaneamente:
# echo a{1,2,3}b{1,2,3}
a1b1 a1b2 a1b3 a2b1 a2b2 a2b3 a3b1 a3b2 a3b3
Combinar dois conjuntos separadamente também é possível, basta colocar os conjuntos entre chaves e separá-los por vírgula.
# echo {a{1,2,3},b{1,2,3}}
a1 a2 a3 b1 b2 b3
A grande vantagem da expansão está na expansão de nomes de arquivos, que é utilizada em comandos que
recebem como parâmetro um ou mais nome de arquivos. Exemplo:
# ls /dev/hda
|
|> Este é o parâmetro do ls que deve ser um diretório ou um arquivo
Imagine que você queira listar todos os arquivos do diretório /dev que inicie com hda e termine com 1, 2 ou 3.
# ls /dev/hda{1,2,3}
/dev/hda1 /dev/hda2 /dev/hda3
Podemos combinar dois conjuntos também:
# ls /dev/{hda{1,2,3},hdb{1,2,3}}
/dev/hda1 /dev/hdb1
/dev/hda2 /dev/hdb2
/dev/hda3 /dev/hdb3
Adicionar metacaracteres também é possível:
# ls /dev/{hd?{1,2,3}}
/dev/hda1 /dev/hdb1 /dev/hdc1 /dev/hdd1
/dev/hda2 /dev/hdb2 /dev/hdc2 /dev/hdd2
/dev/hda3 /dev/hdb3 /dev/hdc3 /dev/hdd3
Os metacaracteres suportados em expansão são:
* Um ou mais caracteres
# ls /dev/{hd{a,b,c}*}
/dev/hda1 /dev/hdb1 /dev/hdc1
/dev/hda11 /dev/hdb11 /dev/hdc11
/dev/hda2 /dev/hdb2 /dev/hdc2
/dev/hda3 /dev/hdb3 /dev/hdc3
? Exatamente um caractere
# ls /dev/{hd{a,b,c}?}
/dev/hda1 /dev/hdb1 /dev/hdc1
/dev/hda2 /dev/hdb2 /dev/hdc2
/dev/hda3 /dev/hdb3 /dev/hdc3
[abc] Combina com a, b ou c
# ls /dev/{hd[abc]{1,2,3}}
/dev/hda1 /dev/hdb1 /dev/hdc1
/dev/hda2 /dev/hdb2 /dev/hdc2
/dev/hda3 /dev/hdb3 /dev/hdc3
[a-z] Combina o range de "a" a "z"
# ls /dev/{hd[a-c]{1,2,3}}
/dev/hda1 /dev/hdb1 /dev/hdc1
/dev/hda2 /dev/hdb2 /dev/hdc2
/dev/hda3 /dev/hdb3 /dev/hdc3
[!a-z] Não combina o range de "a" a "z"
# ls /dev/{hd[!a-c]{1,2,3}}
/dev/hdd1 /dev/hde1 /dev/hdf1
/dev/hdd2 /dev/hde2 /dev/hdf2
/dev/hdd3 /dev/hde3 /dev/hdf3
Espero ter contribuído mais um pouco para o
aprendizado dos leitores.
Até a próxima edição!