O Shell e as linguagens de Script
As linguagens de script podem parecer um pouco estranhas para os programadores de linguagens mais tradicionais, como programadores C ou C++. A exemplo, eu cito, falta de tipos distintos para as vari�veis, ou mesmo o fato de estas linguagens usarem algum mecanismo obscuro para gerenciamento din�mico de mem�ria que consome mais recursos da m�quina do que os estritamente necess�rios. Por outro lado, se pensarmos que em linguagens como C e C++ cerca de 40% do tempo de desenvolvimento � gasto com t�cnicas de armazenamento e gerenciamento de mem�ria (Hans Boehm. Advantages and Disadvantages of Conservative Garbage Collection. ) essas linguagens podem ser utilizadas em projetos cujo desempenho possa ser �r�pido o bastante� e n�o �o mais r�pido poss�vel� ou mesmo em sistemas de prova de conceito.
O termo script, que prov�m de mecanismos antigos onde usavam-se arquivos de texto (estes sim scripts) para gerar entradas de programas iterativos, n�o mais faz sentido. Perceba, as linguagens de script modernas como Perl5, TCL e Python cresceram em capacidade e poder que o termo �linguagem de script� n�o passa de mera palavra t�cnica que n�o mais expressa a atual capacidade destas linguagens sendo usado ainda por que n�o temos um termo melhor para elas. Hoje elas possuem estruturas de dados complexas, como refer�ncias, hashs ou dicion�rios e aceitam at� mesmo as modernas t�cnicas de programa��o orientada a objetos al�m de frameworks complexos.
E para sermos programadores ou administradores de sistemas proficientes nos ambientes Linux o conhecimento de ao menos uma ou duas linguagens de Scripts e conhecimentos m�nimos de C ou C++ fazem-se necess�rios. Dentre as linguagens de script, minha preferida � o Perl5, por�m hoje vamos falar do Shell.
O Shell foi uma das, se n�o a primeira interface de desenvolvimento de scripts no mundo Unix. Mas com certeza foi a primeira interface capaz de produzir c�digo interpretado port�vel (Addison Wesley - The Art of Unix Programming). Apesar de ser muito utilizada como mera interface de comando, o Shell � uma linguagem de programa��o simples e natural. Por n�o possuir estruturas de dados complexas o Shell utiliza de forma muito inteligente outros programas em modo texto (como sort, sed, awk e perl -e), suprindo assim esta fraqueza.
A primeira vers�o do netnews era um shellscript com 150 linhas apenas. De acordo com Steven M. Bellovin, idealizador do programa, a vers�o em shellscript era muito lento para produ��o, mas muito eficiente para provas de conceito e testes.
O primeiro Shell dispon�vel para Unix foi o Bourne Shell (sh), hoje substitu�do em muitos sistemas pelo Bourne Again Shell (bash). Depois do Bourne Shell muitos outros shells foram desenvolvidos, como o Korn Shell (ksh) e o C Shell (csh) entre outros.
Como funciona o Shell
Scripts em Shell s�o excelentes em sistemas onde n�o podemos assumir a pr� exist�ncia de outras linguagens, como Python ou Perl ou mesmo em scripts que rodam durante o start-up dos sistemas operacionais para levantar os servi�os necess�rios e fazer as configura��es dos usu�rios. Tamb�m s�o uma �tima pedida em situa��es simples para administradores, instaladores e como ferramenta did�tica para programadores iniciantes. Sistemas Shell mais complexos podem arriscar sua portabilidade, pois existem diferen�as de implementa��o entre os shells existentes (Bash, Korn, C) al�m da possibilidade da n�o exist�ncia de um determinado comando no sistema que executar� sua aplica��o.
� muito importante saber operar o Shell no mundo Unix e Linux. No mundo Linux o shell mais popular � o Bash. No mundo Unix o Ksh ainda tem muita import�ncia. Historicamente os principais Shells s�o: Bourn Shell ou sh; C Shell ou csh; Korn Shell ou ksh; Bourn Again Shell ou bash;
Inicialmente os sistemas Unix usavam o sh como linguagem de programa��o e interpreta��o de comandos. O sh introduziu a possibilidade de declara��o de vari�veis e estruturas de decis�o em um interpretador de comandos. Posteriormente foi desenvolvido o csh, cuja a estrutura de programa��o era semelhante ao C, muito utilizado no desenvolvimento Unix at� os dias de hoje.
Exemplos de c�digo em sh e csh para compara��o:
#!/bin/sh
if [ $days -gt 365 ]
then
echo This is over a year.
fi
#!/bin/csh
if ( $days > 365 ) then
echo This is over a year.
endif
#!/bin/sh
i=2
j=1
while [ $j -le 10 ]; do
echo '2 **' $j = $i
i=`expr $i '*' 2`
j=`expr $j + 1`
done
#!/bin/csh
set i = 2
set j = 1
while ( $j <= 10 )
echo '2 **' $j = $i
@ i *= 2
@ j++
end
O ksh surgiu de uma implementa��o onde David Korn tentou unir as qualidades do csh e do sh em um �nico shell al�m de adicionar aritm�tica de ponto flutuante e estrutura de arrays associativos, tornando-se muito popular em sistemas Unix como Solaris, True64 e AIX.
O bash surgiu como uma implementa��o mais atual do Bourn Shell, com aritm�tica em diversas bases, arrays de tamanho ilimitado, fun��es e diversas outras facilidades ao programador/administrador de sistemas.
Voc� pode aprender mais sobre o bash em http://www.gnu.org/software/bash/ e sobre o ksh em http://www.kornshell.com/.
Todo shell moderno deve atender ao padr�o POSIX 1003.2 "Shell and Utilities Language Committee". As principais caracter�sticas dos modernos Shells s�o:
- Controle de Jobs;
- Fun��es;
- Alias;
- Redirecionamento;
- Hist�rico de comandos;
- Editor de comandos;
- Auto-Completar;
O Bash, como hell padr�o do projeto GNU atende a todos estes requisitos do padr�o Posix e as qualidades de um shell moderno listadas acima.
Usando o shell em sistemas Linux
Apesar do Bash ser o shell padr�o do Linux, diversos outros podem existir na instala��o. O shell padr�o de cada usu�rio � definido no arquivo /etc/passwd:
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
thiago:x:500:500::/home/thiago:/bin/bash
$
E tamb�m pode ser visto na vari�vel de ambiente SHELL:
$ echo "Minha shell eh: $SHELL"
Minha shell eh: /bin/bash
$
Se voc�, por alguma raz�o, preferir outro shell ao bash pode alterar o shell padr�o do seu usu�rio com o comando chsh:
$chsh thiago
Alterando o interpretador de comandos para o usu�rio thiago.
Senha:
Novo interpretador de comandos [/bin/bash]: /bin/ksh
Interpretador de comandos alterado.Depois saia do shell com o comando logout e log no sistema novamente. Veja o seu novo shell:
$ echo $SHELL
/bin/ksh
$
E no arquivo /etc/passwd
$ grep thiago /etc/passwd
thiago:x:500:500::/home/thiago:/bin/ksh
$
Arquivos de Configura��o do BASH:
Os arquivos de configura��o do bash definem os alias, atalhos, prompt e vari�veis de ambientes da sua sess�o dentre outras coisas. Estes arquivos s�o divididos em globais, que influenciam as sess�es de todos os usu�rios e os arquivos locais que alteram apenas o seu usu�rio. Os arquivos globais s�o /etc/profile e /etc/bashrc. Os arquivos locais s�o $HOME/.profile; $HOME/.bash_login; $HOME/.bashrc; $HOME/.bash_profile; $HOME/.bash_logout;
Exce��o feita ao arquivo local $HOME/.bash_logout, os outros arquivos de configura��o local s�o equivalentes e n�o ir�o necessariamente coexistir, bastando apenas um dos arquivos de configura��o local. O arquivo que estar� na sua pasta home depender� da distribui��o. As configura��es feitas nos arquivos locais sobrescrevem as configura��es locais. Quando voc� utilizar um shell que n�o seja de login, apenas os arquivos locais ser�o lidos para configurar este shell e o $HOME/.bahs_logout n�o � executado ao sair do shell.
Nota: $HOME e ~ s�o formas de referenciar o diret�rio home do usu�rio atual, no caso /home/thiago
Ordem de leitura dos arquivos:
- Ao carregar um shell de login o Linux carrega inicialmente o arquivo /etc/bashrc ou /etc/profile.
- Depois o sistema procura executar o script $HOME/.bash_profile.
- Se ele n�o existir o sistema ir� procurar pelo $HOME/.bash_login.
- Se ele n�o existir o sistema ir� procurar pelo $HOME/.profile.
- O sistema executa $HOME/.bashrc se houver.
- O shell est� pronto para interagir com o usu�rio e apresenta o prompt.
- Quando o usu�rio sair do shell o script $HOME/.bash_logout � executado.
Executando comandos no Bash
No bash a estrutura b�sica de um comando �:
comando [op��es] [argumentos]Exemplo:
$ ls -la $HOME
total 40
drwx------ 3 thiago thiago 4096 Mai 4 11:28 .
drwxr-xr-x 4 root root 4096 Mai 3 16:15 ..
-rw------- 1 thiago thiago 2155 Mai 4 13:02 .bash_history
-rw-r--r-- 1 thiago thiago 33 Mai 3 16:15 .bash_logout
-rw-r--r-- 1 thiago thiago 176 Mai 3 16:15 .bash_profile
-rw-r--r-- 1 thiago thiago 124 Mai 3 16:15 .bashrc
drwxr-xr-x 4 thiago thiago 4096 Mai 3 16:15 .mozilla
-rw------- 1 thiago thiago 134 Mai 4 13:02 .sh_history
-rw-r--r-- 1 thiago thiago 658 Mai 3 16:15 .zshrc
$ cd /etc
$ pwd
/etc
$
Quando voc� precisar executar uma sequ�ncia muito grande de comandos podemos utilizar o caractere \ para fazer quebra de linha e facilitar a leitura da linha ou script:
# ls -lR /etc > | wc -l
2570
#
Outra facilidade importante do Bash � sua capacidade de autocompletar comandos com a tecla <tab>. Quando voc� iniciar a digita��o de um comando e n�o tiver certeza do nome completo do comando basta digitar tab duas vezes e ver as possibilidades:
$ ca <tab><tab>
cadaver cancel captoinfo
cal cancel.cups case
caller capifax cat
callgrind_annotate capifaxrcvd catchsegv
callgrind_control capiinfo
$ca
Se houver apenas uma possibilidade de comando, ela ir� aparecer na tela ap�s o tab:
$ calle <tab>
$ caller
Caracteres Especiais do Shell
Para atender sua necessidade de ser tanto um interpretador de comandos quanto uma linguagem de programa��o, o Bash possu� diversos caracteres especiais:
| Caractere | Finalidade/Significado | Exemplo |
| ~ | Home do usu�rio atual | cd ~ ls -la ~ |
| Escape do caractere seguinte: | touch my\*file myfile my1file (cria um arquivo my*file) | |
| / | Separa��o de Diret�rios | cd /etc/apache2 ls /var/log |
| $ | Vari�vel | echo $PATH |
| ? | Existe um caractere qualquer | ls my?file |
| ' | Cota absoluta | echo 'n�o substitui: $PATH' |
| ` | Executa comando/Substitui��o | NOW=`date` ; echo $NOW |
| � | Cota dupla | echo �Este � o PATH: $PATH� |
| * | Qualquer caractere, nenhuma ou v�rias vezes | ls m*file |
| & | Envia processo para o background | xeyes& |
| && | Operador AND curto-circuito: se cmd1 executar com sucesso executa o segundo, sen�o encerra | echo 1 && echo 2 fake && echo 2 |
| | | Pipe: Redireciona a sa�da de um comando para outro comando | ls ~ | xargs wc |
| || | Operador OR curto-circuito: Se primeiro comando com sucesso n�o executa o segundo comando | echo 1 || echo 2 fake || echo 2 |
| ; | Executa os comandos em sequ�ncia | cmd1; cmd2; cmd3... echo �O path �: �; echo $PATH echo �O home �: �; echo $HOME |
| [] | Range de caracteres | $ touch file1 file2 file3 file4 $ ls file[1-4] |
| > | Redireciona sa�da | Cmd > out_file.txt |
| < | Redireciona entrada | Cmd < in_file.txt |
Valor retornado por comandos
Como as fun��es em linguagens de programa��o, os comandos do GNU/Linux retornam um valor para o Shell. O valor retornado para o Shell indica o status da execu��o. O valor de retorno zero indica que o comando foi executado com sucesso. Qualquer outro valor indica um erro na execu��o do comando. O valor retornado fica armazenado na vari�vel de ambiente $?.
exemplo:
$ echo "Usuario:home_dir:login_shell";grep thiago /etc/passwd
|cut -d: -f1,6-7
&& echo "estatus do comando: $?"
Usuario:home_dir:login_shell
thiago:/home/thiago:/bin/bash
estatus do comando: 0
$
Perceba: uma vez que a execu��o do comando anterior teve sucesso o valor de $? foi setado para zero. Caso eu utilize um comando incorreto, inexistente ou que eu n�o tenho permiss�o $? receber� um valor diferente de 0.
exemplo:
$ comando_inexistente ; echo "Erro $?"
-bash: comando_inexistente: command not found
Erro 127
$ rm / || echo "Erro. $?"
rm: cannot remove directory `/': um diret�rio
Erro. 1
$ rm -rf /root || echo "Erro: $?"
rm: cannot chdir from `.' to `/root': Permiss�o negada
Erro: 1
Executando diversos comando na mesma linha
Existem v�rias maneiras de executar mais de um comando na mesma linha. Existem as maneiras condicionais e n�o condicionais.
- ponto-e-v�rgula (;): quando colocamos diversos comandos em uma linha separados por ponto e v�rgula eles s�o executados em sequ�ncia:
$ echo "cria diversos arquivos: ";touch file1 file2 file3; ls file*
cria diversos arquivos:
file1 file2 file3 file4
$
- or curto circuito ( || ): se o o primeiro comando n�o � executado com sucesso, ent�o o shell tenta executar comando seguinte:
$ rm -f /etc/cron.d || echo "ATENCAO: Nao pude remover arquivo. Erro $?"
rm: imposs�vel remover `/etc/cron.d': Permiss�o negada
ATENCAO: Nao pude remover arquivo. Erro 1
$ rm -f /etc/cron.d || mail x8ge -s "Nao pude remover arquivo. Erro $?"
$ ls $HOME || echo "Nao foi possivel ler os arquivos: $?"
file1 file2 file3 file4
$
- and curto circu�to ( && ): se o primeiro comando � executado com sucesso, ent�o o shell tenta executar o segundo comando.
$ ls $HOME && echo "arquivos lidos em $HOME"
file1 file2 file3 file4
arquivos lidos em /home/x8ge
$ ls ~root && echo "arquivos lidos em ~root"
ls: /root: Permiss�o negada
$
- Substitui��o de Comandos: Voc� pode atribuir um comando a uma vari�vel e utilizar esta vari�vel em linhas de comando e scripts:
$ ESTRUTURA=`mkdir -p $HOME/myapp/bin $HOME/myapp/conf $HOME/myapp/help`
$ echo "Criando estrutura de arquivos"; $ESTRUTURA ||
echo "WARNING: NAO FOI POSS'IVEL CRIAR ESTRUTURA DE DIRETORIOS
PARA MYAPP $?"; ls -R myapp/
Criando estrutura de arquivos
myapp/:
bin conf help
myapp/bin:
myapp/conf:
myapp/help:
$
Neste exemplo criamos uma vari�vel, ESTRUTURA, que na verdade cont�m o comando `mkdir -p $HOME/myapp/bin $HOME/myapp/help $HOME/myapp/conf`. A substitui��o de comandos tamb�m pode ser utilizada para gerar argumentos para outro comando:
$ wc -c $(ls *.pl)
117 pingador.pl
160 usa_ponto.pl
277 total
$
Neste exemplo voc� pode ver o comando pode ser executado dentro de $( ), em $( ls *.pl ). Da mesma maneira que fosse uma vari�vel, mas o valor n�o � atribu�do a nenhuma vari�vel real.
O Hist�rico de comandos do Shell
O hist�rico do Gnu/Linux � um arquivo que cont�m os �ltimos comandos digitados pelo usu�rio. Entre suas finalidades podemos citar: Analisar as �ltimas a��es do usu�rio; Executar comandos repetitivos; Executar comando que tenham pequena varia��o na lista de argumentos. Por exemplo, eu preciso verificar os arquivos de uma estrutura de diret�rio que eu n�o estou familiarizado. Ent�o irei executar o comando:
$ ls /var
agentx cache games local log opt spool www
backups crash lib lock mail run tmp Agora que eu sei quais os subdiret�rios diretamente abaixo, basta clicar a seta para cima que o shell vai colocar o comando anterior ls /var na linha de comando e eu s� preciso digitar a pasta que quero listar, abaixo de /var. Assim eu s� vou digitar /www pois ls /var foi posto no meu prompt pelo hist�rico:
$ ls /var/www
cgi-bin htdocs kumera-0.3.tar.gz library news tools
data index.html lib media teste.asp
Outro atalho interessante � fornecido pela caracter�stica de substitui��o r�pida: ^string_anterior^nova_string. Com est� t�cnica eu substituo uma string do comando anterior por uma nova string e executo novamente o c�digo. Por exemplo eu uso o comando ls para verificar o tamanho de um arquivo qualquer, como /var/www/index.html:
# ls -l /var/www/index.html
-rw-r--r-- 1 root root 2070 2011-03-09 02:17 /var/www/index.html E decido compact�-lo. basta substituir ls -l por gzip:
# ^ls -l^gzip
gzip /var/www/index.html Isto se torna extremamente �til quando estamos lidando com comandos muito extensos que possuem diversos pipes, argumentos muito longos, etc... e iremos executar comandos diferentes com argumentos repetidos ou argumentos diferentes com o mesmo comando.
Os comandos digitados pelo usu�rio ficam armazenados no arquivo $HOME/.bahs_history
$ cat .bash_history | nl | tail
466 modprobe -lt net
467 locate modules.dep
468 cat /lib/modules/2.6.32-31-generic/modules.dep
469 lsmod
470 lsmod | sort
471 grep vboxnetadp /lib/modules/2.6.32-31-generic/modules.dep
472 grep vboxdrv /lib/modules/2.6.32-31-generic/modules.dep
473 modprob -l
474 modprobe -l
475 wc -l $(modprobe -l)
476 modprobe -l | wc -l
Configura��o do Hist�rico
As principais configura��es do hist�rico podem ser vistas e alteradas nas vari�veis de ambiente HISTSIZE, HISTFILE e HISTCMD. HISTSIZE cont�m o tamanho do buffer de comandos do history, ou seja, quantos comandos ser�o armazenados pelo HISTFILE. HISTFILE cont�m o path do arquivo que armazena o hist�rico. E HISTCMD cont�m o n�mero do pr�ximo comando do hist�rico:
$ echo $HISTSIZE
500
$ echo $HISTFILE
/home/thiago/.bash_history
$ echo $HISTCMD
511 Apesar de HISTSIZE estar configurada para 500 e HISTCMD marcar 511 o meu arquivo de hist�rico cont�m apenas 500 comandos, mas seu n�mero inicial n�o est� mais em 1. Voc� pode ver a lista de todos os comandos do hist�rico com o comando history:
$ history
14 ls -R /etc/apache2
15 ls -Rd /etc | wc -l
16 ls -d /etc
17 ls -R /etc | wc -l
18 sudo ls -R /etc | wc -l
19 sudo ls -R /etc | wc -l
20 sudo ls -Ra /etc | wc -l
21 sudo ls -Ra /etc
22 sudo ls -Rla /etc
23 echo `date`
... sa�da omitida.O buffer de comandos do hist�rico pode ser limpo com o comando history -c
$ history -c
$ history
15 history Nota: As vari�veis do hist�rico est�o definidas nos arquivos de configura��o do seu shell.
Atalhos interessantes do History.
O history nos fornece atalhos muito interessantes, veja:
!! => executa o �ltimo comando digitado
!n => executa o comando de n�mero n, exemplo:
$ history
15 history
16 grep HIST* .profile
17 ls .profile
18 cat .profile
19 cat .bashrc #vamos usar o atalho para executar este comando.
20 history
$ !19
cat .bashrc
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
...sa�da omitida
!string => executa o comando mais recente que inicia com �string�
!?string => executa o comando mais recente que cont�m �string�Para obter mais informa��es sobre o history do Linux:
comandos:
man history
man fc ou info fc
man bash