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:

  1. Ao carregar um shell de login o Linux carrega inicialmente o arquivo /etc/bashrc ou /etc/profile.
  2. Depois o sistema procura executar o script $HOME/.bash_profile.
  3. Se ele n�o existir o sistema ir� procurar pelo $HOME/.bash_login.
  4. Se ele n�o existir o sistema ir� procurar pelo $HOME/.profile.
  5. O sistema executa $HOME/.bashrc se houver.
  6. O shell est� pronto para interagir com o usu�rio e apresenta o prompt.
  7. 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