Universidade Federal do Rio Grande do Norte
por
Allan Robson Silva Venceslau
Helio Batista de Araujo Junior
Rafael Mederiros Teles
Natal, 2012
Sumário
1 Introdução
2
2 Breve introdução ao Linux
3
3 Arquitetura do Sistema e interação com Shell
4
4 Primeiros passos
4.1 Comandos úteis e estruturas built-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Declarando variáveis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3 Algumas variáveis especiais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
4
6
7
5 Condicionais
5.1 If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
8
9
6 Iteradores
6.1 For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2 While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
10
11
7 Recursão
12
8 Algumas ferramentas de todo shelleiro
8.1 Aurelio’s tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2 ImageMagick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
13
13
1
1
Introdução
Essa apostila tem o intuito de realizar uma breve introdução ao sistema Linux e a linguagem Shell
Script. Não há intenção do leitor tornar-se um programador profissional nesta linguagem durante este
curso, visto que a mesma é muito vasta para ser coberta em poucas horas. Pensamos no entanto que
serão dados as bases necessárias para que o mesmo possa prosseguir sozinho e atingir nı́veis cada vez
maiores de proficiência nessa linguagem extremamente poderosa que paira sobre os sistemas Unix-like.
Alguns trechos foram retirados do sı́tio www.jneves.wordpress.com/, uma das melhores referências em
ShellScript no paı́s.
2
Breve introdução ao Linux
O Linux (Linus + Unix) originalmente foi desenvolvido por Linus Torvalds usando uma implementação
de código-aberto feita por Andrew S. Tanenbaum chamada MINIX (http://www.minix3.org/) encontrada
no livro Operating Systems: Design and Implementation. Desde a criação do sistema a aceitação vem
aumentando e várias distribuições vem sendo criadas objetivando um maior contato com o usuário comum.
Embora hoje se possa usar variações do Linux como o Ubuntu (www.ubuntu.com) sem mesmo saber que
existe o bash, o conhecimento de tais ferramentas torna o convı́vio com o ambiente muito mais prático
e mais simples também, facilitando tarefas como: administração do sistema com a criação de pequenos
scripts, realização de tarefas rotineiras que normalmente necessitariam de um software auxiliar, além de
outras tarefas que em outros contextos são dispendiosas e mecânicas para o usuário.
3
Arquitetura do Sistema e interação com Shell
A Figura 1 mostra um diagrama de camadas que representa a interação desde o nı́vel de hardware até
o nı́vel de “script”. O Shell representa portanto a abordagem mais “alto nı́vel” que um sistema Linux
Figura 1: Camadas do SO até o Shell
comum possui. Quando é executado um comando que vai criar um arquivo no Bash, temos de passar por
todas essas camadas até chegar no nı́vel das unidades de disco (que corresponde a uma parte do hardware
do sistema). Essa representa uma caracterı́stica muito boa e ao mesmo tempo muito ruim do Shell. Boa,
pois permite:
• uso da sintaxe cheia de recursos,
• acesso a diversos programas que realizam tarefas simples (mas em conjunto bastante poderosas),
• interação com arquivos de maneira praticamente transparente,
• interação entre processos usando “túneis”, etc.
Ao mesmo tempo, tentar realizar tarefas que são onerosas ao processador ou ao disco (como rotinas
de computação numérica ou manipulação de arquivos muito grandes) pode ser uma tarefa frustrante,
pois o Shell está repleto de system calls 1 , além de ter seu código sempre interpretado. Portanto que fique
claro que nem toda tarefa pode ser feita com essa ferramenta, logo é preciso discernimento sobre o uso
da mesma2 .
4
Primeiros passos
Agora que sabemos a filosofia do Shell, vamos a um pouco de prática. Abrindo o terminal uma tela
como a da Figura 2 se apresenta. Os comandos Shell podem ser digitados diretamente nessa interface ou
então o usuário pode optar por usar um editor como Kate ou Gedit para elaborar os scripts. No caso do
editor, o usuário deve lembrar de colocar a linha:
#!/bin/bash
no inı́cio de cada script e dar permissão de execução ao mesmo com o comando chmod +x arquivoscript.
Comentários são iniciados por “#” na linha3 .
4.1
Comandos úteis e estruturas built-in
Vamos usar alguns comandos comuns do Linux para execução dos scripts, vamos relembrar alguns.
O comando ls é responsável por listar arquivos e diretórios. Assim caso você digite:
$ls
1 Chamadas
ao sistema operacional
“great powers have great responsabilities”
3 A única exceção é a instrução #!/bin/bash, :P
2 Remember,
Figura 2: Terminal Shell
os arquivos do diretório corrente serão listados. Pode-se tambem usar o ls para mostrar o conteúdo de
uma pasta, fazendo:
$ls folder2
O comando cat é responsável por mostrar o conteúdo de um arquivo, assim para visualizar rapidamente
um .txt basta fazer:
$cat arquivo1
Podemos usar uma boa caracterı́stica do Shell que são os caracteres coringa, assim caso queiramos mostrar
todos os arquivos podemos fazer:
$cat *
Caso queiramos mostrar todos os arquivos chamados arquivo1,arquivo2,arquivo3,arquivon, basta
fazer:
$cat arq* # Ou ent~
ao, cat arquivo?
Para mostrar somente o arquivo 1 e 2, você pode fazer:
$cat arquivo{1,2}
A contrução {a,b}{c,d} realiza o produto cartesiano, entre essas duas contruções do Shell. Lembre
sempre disto, pode facilitar sua vida, :p.
Um comando obrigatório para quem usa Shell é o grep, esse comando realiza uma análise de expressões
regulares e devolve como resposta as linhas que ele conseguiu realizar um match, iremos explorar o grep
daqui a pouco.
Aproveitemos para conhecer mais uma estrutura do Shell, o pipe: “|”. Nossa intenção é mostrar as
primeiras 6 linhas de um arquivo com o poema do Vinı́cius de Moraes “Pela luz dos olhos teus”. Fazendo
um cat poema|head -n 6 4 :
Quando a luz dos olhos meus
E a luz dos olhos teus
Resolvem se encontrar
Ai que bom que isso é meu Deus
Que frio que me dá o encontro desse olhar
Mas se a luz dos olhos teus
4O
comando head possui várias funções, para aprofundamente faça no bash man head
O que fizemos foi usar a estrutura “|” para realizar um “túnel” entre processos (um pipe 5 ). Scripts Shell
usam “túneis” a todo momento, e tal caracterı́stica dá muito poder a capacidade de linkar a saı́da de um
processo a entrada de outro. Podemos agora com o comando grep fazer uma busca pela palavra “luz”
nas primeiras 6 linhas do arquivo poema por exemplo, basta fazer:
$cat poema|head -n 6|grep luz
Se quisermos procurar no arquivo todo, as linhas que não tem a palavra luz (numerando as linhas),
podemos fazer:
$nl poema|grep -v luz
Há muito que ler sobre o grep6 , fica ai a dica de bons sites sobre ele e expressões regulares: http://guia-er.
sourceforge.net e http://www.aurelio.net/er/.
Por ultimo, suponha que queiramos pegar o resultado de um comando ou de uma sequência de pipes
e colocar em um arquivo. Para isso basta que no final da declaração você coloque > file. Suponha que
temos um arquivo cheio de tags XML e há uma série de e-mails no meio. Podemos usar o comando grep
para pegar somente os que tem o servidor smtp domain.com, fazendo:
$cat emails|grep ’@domain\.com’
<mail>[email protected]<\mail>
<mail>[email protected]<\mail>
Mas veja só, estamos com as tags em volta, tudo bem. Vamos tira-las com o comando cut, cuja sintaxe
é cut -d <separador> -f<lista de campos>, logo teremos:
$cat emails |grep -i ’@domain\.com’|cut -d ’>’ -f2-|cut -d ’<’ -f1 >emails_do_domain.com
E nosso arquivo está pronto, cheque fazendo:
$cat emails_do_domain.com
[email protected]
[email protected]
4.2
Declarando variáveis
Para declarar uma variável no Shell, faça:
$ variavel=valor
É importante que tome cuidado com espaços, pois declarações como:
$ variavel = valor
ou
$ variavel= valor
não serão interpretadas como atribuições de variável e sim como nomes de comandos e passagem de
parâmetros. Para imprimir o conteúdo de uma variável, faça:
$ echo $variavel
Podemos querer guardar o conteúdo de um comando numa variável, isso pode ser bem útil para algumas
coisas que faremos adiante. Para fazer isso basta usar a seguinte declaração:
variavel=$(comando)
5 Do
6 não
inglês, cano
esqueça de consultar man grep (afinal é a documentação oficial, :P)
lembrando que comando pode ser uma série de comandos separados por pipe, ou comandos separados por
“;”7 . Usando “;” como separador, a variável irá receber a saı́da dos dois comandos. No caso do “|”,
a variável irá receber somente a saı́da do ultimo comando. Vamos então guardar numa variável o nome
dos diretórios da pasta folder :
diretorios=$(ls)
Podemos agora mostrar fazendo:
echo $diretorio #Note uma diferença fazendo, echo "$diretorio"
Usar variáveis é importante para fazer interpolações, suponha que temos uma lista de nomes usuários
e queremos fazer um script genérico para mostrar informações sobre as contas dos mesmos. Isso nos
vai ser bem útil quando usarmos iterações. Por enquanto vamos nos satisfazer entendendo a seguinte
sequência de comandos:
numeroDeTerminais=$(who|wc -l)
echo "Há $numeroDeTerminais terminais abertos"
que vai mostrar quantos terminais estão abertos no sistema. Somente usando aspas duplas é possı́vel
fazer interpolações, ao usar aspas simples o conteúdo da declaração não é interpretado pelo shell.
4.3
Algumas variáveis especiais
O Shell possui algumas variáveis especiais, que permitem que o usuário realize alguns procedimentos
como descobrir o PID do ultimo processo em background, descobrir o retorno do último processo, entre
outras coisas, alguns exemplos estão listados abaixo:
Variável
$0
$1
...
$9
$10
...
$#
$*
$@
Variável
$$
$!
$
$?
Parâmetros Posicionais
Parâmetro número 0 (nome do comando ou função)
Parâmetro número 1 (da linha de comando ou função)
Parâmetro número N ...
Parâmetro número 9 (da linha de comando ou função)
Parâmetro número 10 (da linha de comando ou função)
Parâmetro número NN ...
Número total de parâmetros da linha de comando ou função
Todos os parâmetros, como uma string única
Todos os parâmetros, como várias strings protegidas
Miscelânia
Número PID do processo atual (do próprio script)
Número PID do último job em segundo plano
Último argumento do último comando executado
Código de retorno do último comando executado
Tabela 1: Variáveis especiais
Agora podemos fazer coisas como:
$
$
$
$
$
$
echo $$ #Pid do terminal em que o echo está sendo executado
echo $? #Deve mostrar 0, pois o echo foi executado com ^
exito
qualquer #Um nome de rotina qualquer que n~
ao existe no path
echo $? #Vai imprimir um código de erro diferente de zero
gedit & #Ou kate &, dependendo da distribuiç~
ao que voc^
e está usando
echo $! #Vai imprimir o pid do gedit (ou do kate)
Com isso podemos tomar várias decisões relativas a processos que estão rodando na nossa máquina e
customizar diversas aplicações.
7O
“;” é a forma de se fazer uma lista de comandos, ou seja por uma série de comandos numa mesma linha
5
Condicionais
Para realizarmos decisões é necessário o uso de comandos condicionais. Para isso temos o comando
if e o comando case.
5.1
If
O if tem a seguinte estrutura geral:
if listadecomandos1
then
comando1
elif listadecomandos2
then
condicao2
...
elif listadecomandosN
then
comandoN
else
comandoelse
fi
O if do jeito que está escrito não avalia diretamente condições, ele avalia se o ultimo comando foi bem
sucedido ou não. Para aqueles que programam em C/C++, lembram que no final do main tem sempre
um return 0;? Pois é, é exatamente pra ajudar tomadas de decisão como essas que esse retorno existe.
Caso o programa retorne 0 para o interpretador Shell, o if correspondente terá o seu respectivo comando
executado. Por exemplo, façamos uma aplicação simples que decide se um arquivo possui determinada
string.
#Arquivo procura-string
#!/bin/bash
#Parametro 1 - arquivo
#Parametro 2 - string a ser procurada
if nl $1|grep $2 --color=auto #Dá uma corzinha
then
echo O arquivo contem a string
else
echo String n~
ao encontrada
fi
Para testar condições mais familiares deve-se usar um outro comando para auxiliar o if, esse comando
é o test. Algumas opções possı́veis são listadas abaixo na Tabela 2:
Opção
-e arq
-s arq
-f arq
-d arq
-r arq
-w arq
-x arq
Significado
arq existe
arq existe e tem tamanho maior que zero
arq existe e é um arquivo regular
arq existe e é um diretório
arq existe e com direito de leitura
arq existe e com direito de escrita
arq existe e com direito de execução
Tabela 2: Parâmetros do comando test
Algumas opções para números na Tabela 3:
Agora podemos fazer coisas como:
Opção
n1 -eq n2
n1 -ne n2
n1 -gt n2
n1 -ge n2
n1 -lt n2
Verdadeiro se:
n1 e n2 são iguais
n1 e n2 não são iguais
n1 é maior que n2
n1 é maior ou igual a n2
n1 é menor que n2
Significado
equal
not equal
greater than
greater or equal
less than
Tabela 3: Mais alguns parâmetros do comando test
#Arquivo if-com-test
#!/bin/bash
read opc #L^
e da entrada padr~
ao
if [ $opc -eq 1 ]
then
echo "Opç~
ao 1"
elif [ $opc -eq 2 ]
then
echo "Opç~
ao 2"
elif [ $opc -eq 3 ]
then
echo "Opç~
ao 3"
elif [ $opc -eq 4 ]
then
echo "Opç~
ao 4"
else
echo Digite uma opç~
ao entre 1 e 4
fi
O Shell é sensı́vel a formatação, coloque espaços como indicado acima. Uma tabela com outros exemplos
de uso do if segue abaixo:
Exemplos de uso do if
if [ -f ”$arquivo”]; then echo ’Arquivo encontrado’; fi
if [ ! -d ”$dir”]; then echo ’Diretório não encontrado’; fi
if [ $i -gt 5 ]; then echo ’Maior que 5’; else echo ’Menor que 5’; fi
if [ $i -ge 5 -a $i -le 10 ]; then echo ’Entre 5 e 10, incluindo’; fi
if [ $i -eq 5 ]; then echo ’=5’; elif [ $i -gt 5 ]; then echo ’¿5’; else echo ’¡5’; fi
if [ ”$USER”= ’root’ ]; then echo ’Oi root’; fi
Tabela 4: Exemplos rápidos sobre if
5.2
Case
O comando case fornece outra alternativa de comando condicional, ele tem a seguinte forma:
case $var in
padrao1) cmd1
cmd2
cmdn ;;
padrao2) cmd1
cmd2
cmdn ;;
padraon) cmd1
cmd2
cmdn ;;
esac
O conteúdo de var é comparado com os padrões, cada padrão pode ser um como o da lista abaixo:
Caractere
*
?
[...]
Caracteres Para Formação de Padrões
Significado
Qualquer caractere ocorrendo zero ou mais vezes
Qualquer caractere ocorrendo uma vez
Lista de caracteres
Tabela 5: Tabela de padrões do case
Para mostrar como fica melhor, vamos repetir o exemplo anterior, só que desta vez usaremos o case
e não o if...elif ... else ... fi:
#Arquivo case
#!/bin/bash
case $1 in
1) echo Opç~
ao 1;;
2) echo Opç~
ao 2 ;;
3) echo Opç~
ao 3;;
4) exit Opç~
ao 4;;
*) echo ""Digite uma opç~
ao entre 1 e 4""
esac
6
Iteradores
Vamos agora explorar alguns comandos que possibilitam fazer iterações.
6.1
For
Se você está habituado a programar, certamente já conhece o comando for, mas o que você não sabe é
que o for, que é uma instrução intrinseca do Shell (isto significa que o código fonte do comando faz parte
do código fonte do Shell, ou seja em bom programês é um built-in), é muito mais poderoso que os seus
correlatos das outras linguagens. Há várias sintaxes para o for, uma delas é:
for var in val1 val2 ... valn
do
cmd1
cmd2
cmdn
done
Outra sintaxe possı́vel é a seguinte:
for ((var=ini; cond; incr))
do
cmd1
cmd2
cmdn
done
Essa sintaxe é muito prática para se fazer daemons 8 , por exemplo um monitor da carga do sistema:
#Arquivo for-monitor
#!/bin/bash
#Monitor de processo simples
for ((x=1;;x++))
8 Processos
de “longa vida” dentro do computador que gerenciam tarefas
do
y=$(ps aux|grep $USER|wc -l)#Conta quantos processos do usuario existem
echo "$x $y">>processos
sleep 2
done&
sleep 1
gnuplot plot #Script com um pequeno codigo gnuplot
#Arquivo plot - Sintaxe gnuplot
reset #Limpa o gráfico
set yrange [0:400] #Seta a escala no eixo y
set title "Número de processos do sistema" #Coloca tı́tulo
plot ’processos’ with lines #Plota com linhas contı́nuas
reread #Volta ao inı́cio
A ainda há uma outra forma (a minha predileta) que é a seguinte:
for var in $(cmd1|cmd2|...|cmdn)
do
outrocomando1
...
outrocomandoz
done
Ou seja, podemos pegar o resultado de uma cadeia de piples e usar numa iteração. Mas como o for decide
onde começa e onde termina cada “elemento de iteração”? Para isso existe a variável IFS (Inter-FieldSeparator), que é a entidade responsável por “sinalizar” onde começa e onde termina cada elemento que
vai ser iterado. O valor default é o do espaço em branco (ou seja “ ”), mas pode ser mudado a qualquer
instante para outro valor que você julgue mais correto. Para diversas aplicações, é melhor colocar o IFS
com o valor da “quebra de linha” já que vários programas escrevem dados nesse formato. Rode o seguinte
script na pasta folder e veja a diferença que o IFS faz:
#!/bin/bash
#Usando o IFS sem quebra de linha
for var in $(ls)
do
du -sh $var
done
#Usando o IFS com quebra de linha
BACK=$IFS
IFS=’
’
for var in $(ls)
do
du -sh $var
done
IFS=$BACK
6.2
While
O while é outro iterador e possui uma sintaxe alternativa para se fazer laços, a sua forma é a seguinte:
while comando
do
cmd1
cmd2
...
cmdn
done
Podemos com ele fazer facilmente um monitor de processos:
#!/bin/bash
# Executa e monitora um
# processo em background
$1 &
# Coloca em backgroud
while ps | grep -q $!
do
sleep 5
done
echo Fim do Processo $1
7
Recursão
Assim como em muitas linguagens, o Shell possibilita o uso de estruturas recursivas. Não há nada de
misterioso nisto, mas para executar tais coisas, temos de aprender a usar funções. Uma função em shell
é declarada da seguinte forma:
nomedafuncao(){
comandos
}
Para fazer uma função recursiva, então terı́amos de fazer algo como:
funcao(){
comandos
...
funcao
...
}
Um exemplo é uma função que imprime a sequência Fibonacci:
fibonacci(){
if [ $1 -eq 1 ]#$1 representa o primeiro argumento passado para a funç~
ao
then
echo 1
elif [ $1 -eq 2 ]
then
echo 2
else
anterior=$(fibonacci $[$1-1])# O uso de $[] é para se realizar operaç~
oes aritméticas
anterior2=$(fibonacci $[$1-2])
echo $[$anterior+$anterior2]
fi
}
O uso de recursões torna o código muito limpo, mas ao mesmo tempo bem mais lento. Experimente
pedir o termo de número 40, e veja que o cálculo demora um tempo bastante significativo. Deve-se
realizar uma boa avaliação antes de se usar recursões, mas para pequenos cálculos e rotinas simples é
uma solução bastante “enxuta”.
8
Algumas ferramentas de todo shelleiro
8.1
Aurelio’s tools
Existem muitos bons aplicativos escritos em Shell, um bastante famoso na comunidade e cheio de
funcionalidades é o funçõeszz(http://funcoeszz.net/) do Aurélio Marinho Jargas9 (um dos grandes programadores em Shell) do Brasil. Para instala-lo basta baixar o fonte e dentro do seu .bashrc, fazer:
source funcoeszz.sh
Agora você tem acesso a uma caixinha de ferramentas bastante útil, veja só:
zzajuda #Mostra ajuda sobre os comandos
zzgoogle shellscript #Pesquisa no google
zzdicbabelfish shell #Traduz do ingl^
es para portugu^
es
zztempo Brazil SBNT #Mostra condiç~
oes temporais em Natal-RN
zzipinternet #Mostra o seu ip visı́vel na internet
zzconverte cf 36 #Realiza várias convers~
oes, no caso de Celsius para Fahrenheit
zzdolar #Mostra o valor do dolar comercial, paralelo, turismo
Além de usar as funçõeszz, olhe o código-fonte, está bastante legı́vel e comentado.
Existem alguns varios aplicativos escritos em Sed (Stream EDitor, um editor de texto que trabalha
em batchmode), o Aurelio criou uma versão do jogo Sokoban que usa Sed, dê uma olhada no screenshot:
Figura 3: Jogo Sokoban escrito em Sed
O Aurélio é um dos grandes incentivadores e popularizadores do uso de ShellScript e conhecido por
explorar bem os poderes desta linguagem, vale a pena conferir e acompanhar o trabalho deste “linuxer”.
8.2
ImageMagick
A suı́te ImageMagick é um conjunto de aplicativos para manipular imagens via ShellScript. Existem
várias implementações de bibliotecas para diversas linguagens e é uma biblioteca bastante usada devido
a suas facilidades. Vamos ver alguns pequenos truques.
$convert imagens/tux.jpg -monochrome imagens/tux-mono.jpg #coloca a foto em cores preto e branco
$convert imagens/tux.jpg -flip imagens/tux-flip.jpg #Cria uma imagem espelho
$convert imagens/tux.jpg -flop imagens/tux-flop.jpg #Dá um giro de 180 graus na imagem
Pode-se criar pequenas fotos com dizeres via linha de comando rapidamente:
9 www.aurelio.net
$convert -background white -fill black -font ./Loki_Cola.ttf \
-pointsize 120 label:’Shell Script’ shell.gif
Uma pequena animação resultante da montagem de outras 3 imagens:
$convert -delay
-page
-page
-loop
100 -size 100x100 xc:SkyBlue \
+5+10 imagens/balloon.gif
-page +35+30 imagens/medical.gif
+62+50 imagens/present.gif
-page +10+55 imagens/shading.gif
0 imagens/animation.gif
\
\
:(){:|:};:
“Great powers have great responsabilities”
Referências
[blu08] Linux, Command Line and Shell Scripting. Wiley Publishing, Inc, www.wiley.com, 2008.
[Jar]
Aurélio Marinho Jargas. Portal do Shell. www.aurelio.net/shell.
[Nev]
Júlio Cezar Neves. Shell e Chope: A dupla perfeita. www.julioneves.com.
Download

Allan Robson Silva Venceslau Helio Batista de Araujo Junior