Programa��o Shell

ArticleCategory: [Es gibt verschiedene Artikel Kategorien]

UNIXBasics

AuthorImage:[Ein Bild von Dir]

[Photo of the Authors]

TranslationInfo:[Autor und Übersetzer]

original in en Katja and Guido Socher 

en to pt Patrick Carpalhoso  

AboutTheAuthor:[Eine kleine Biographie über den Autor]

Katja � a editora germ�nica de LinuxFocus. Ela gosta de Tux, filmes & fotografia e o mar. A sua p�gina pessoal encontra-se aqui.

Guido � um fan de Linux desde a muito e gosta do Linux porque ele foi criado por pessoas honestas e de mentalidade aberta. � uma das raz�es porque o chamamos open source. A sua p�gina pessoal esta no linuxfocus.org/~guido.

Abstract:[Hier sollte eine kleine Zusammenfassung stehen]

Neste artigo iremos explicar como escrever pequenos scripts shell e dar v�rios exemplos.

ArticleIllustration:[Das Titelbild des Artikels]

[Illustration]

ArticleBody:[Der eigentliche Artikel. Überschriften innerhalb des Artikels sollten h2 oder h3 sein.]

Porqu� programa��o shell ?

Apesar existirem varias interfaces graphicas dispon�veis para Linux o shell continua a ser uma ferramenta muita eficiente. O shell n�o � s� uma colec��o de comandos mas uma verdadeira boa linguagem de programa��o. Com ele voc� pode automatizar numerosas tarefas, o shell � muito bom para tarefas de administra��o de sistema, voc� pode muito rapidamente testar se as suas ideias funcionam o que torna muito �til para uma prototipagem simples e � muito �til para pequenos utilit�rios que executam tarefas relativamente simples onde a efici�ncia � menos importante que o conforto da configura��o, manuten��o e portabilidade.
Ent�o vamos ver como ele funciona:

Criar um script

Existem muitos shells diferentes dispon�veis para Linux mas habitualmente o bash (bourne again shell) � utilizado para programa��o shell porque ele � gr�tis e � f�cil de utilizar. De modo que todos os scripts que iremos escrever neste artigo utiliza o bash (mas na maior parte do tempo funciona tamb�m com a sua velha irm�, o bourne shell).
Para escrever os nossos programas shell podemos utilizar qualquer editor de texto, ex: nedit, kedit, emacs, vi... como em qualquer outra linguagem de programa��o.
O programa tem de come�ar com a linha seguinte (deve ser a primeira linha no ficheiro):
 #!/bin/sh 
   
Os caracteres #! informam o sistema que o primeiro argumento que segue na linha � o programa utilizado para executar este ficheiro. Neste caso /bin/sh � o shell que utilizamos.
Depois de escrever e gravar o script � necess�rio tornar-lo execut�vel para o poder utilizar.
Para tornar um script execut�vel escreva
chmod +x nomeficheiro
Ent�o pode executar o seu script escrevendo: ./nomeficheiro

Coment�rios

Na programa��o shell os coment�rios come�am com # e v�o at� o fim da linha. Recomendamos a utiliza��o dos coment�rios. Se t�m coment�rios e que n�o utiliza um script desde um certo tempo voc� saber� imediatamente do que se se trata e como funciona.

Vari�veis

Como nas outras linguagens de programa��o n�o se pode viver sem vari�veis. Na programa��o shell todas as vari�veis s�o de tipo string e n�o � necess�rio declara-las. Para atribuir um valor a uma vari�vel basta escrever:
vari�vel=valor 
Para ler o conte�do de uma vari�vel basta colocar um cifr�o antes da vari�vel;
#!/bin/sh
# atribui um valor:
a="hello world"
# e agora escreve o conte�do de "a":
echo "A �:" echo $a 
Escreva estas linhas no seu editor de texto e grave com o nome first. Ponha o script execut�vel escrevendo chmod +x first no shell e executa escrevendo ./first
O script ira unicamente escrever:
A �:
hello world 
Por vezes � poss�vel confundir os nomes das vari�veis com o resto do texto:
num=2 echo "isto � o $numnd" 
Isto n�o vai imprimir "isto � 2nd" mas "isto � " porque o shell vai procurar por uma vari�vel chamada numnd que n�o tem valor nenhum. Para dizer ao shell que estamos a fazer refer�ncia a vari�vel num temos que usar as chavetas:
num=2 echo "isto � o ${num}nd"
Desta forma vai escrever: isto � o 2nd

Existem vari�veis que s�o automaticamente inicializadas. Iremos discutir delas mais abaixo a primeira utiliza��o.

Se necessita de manipular express�es matem�ticas ent�o precisa de utilizar programas como expr (ver a tabela seguinte).
Apesar das vari�veis de shell normais ser unicamente validas em programas shell existem tamb�m vari�veis de ambiente. A vari�vel precedida com a palavra-chave export � uma vari�vel de ambiente. N�o iremos mais falar sobre elas sabendo que elas s�o normalmente utilizadas em scripts de login.

Comandos Shell e estruturas de control

Existem 3 categorias de comandos que podem ser utilizadas em scripts shell:

1)Comandos Unix:
Se bem que um script shell pode utilizar qualquer comando unix existem comandos que s�o utilizados mais vezes que outros. Esses comandos podem ser descritos como comandos para manipula��o de ficheiro e de texto.
Syntax de comandos Objectivo
echo "algum texto" escreve algum texto no ecr�
ls lista ficheiros
wc -l ficheiro
wc -w ficheiro
wc -c ficheiro
conta as linhas num ficheiro ou
conta o numero de palavras
conta os numeros caracteres
cp ficheiroorigem ficheirodestino copia ficheiroorigem para ficheirodestino
mv nomeantigo novonome renomeia ou move um ficheiro
rm ficheiro apaga um ficheiro
grep 'qualquercoisa' ficheiro procura por strings num ficheiro
Exemplo: grep 'qualquercoisa' ficheiro.txt
cut -b colnum file extrai dados de uma coluna fixa de texto
Exemplo: extrai caracteres da posi��o 5 a 9
cut -b5-9 file.txt
N�o confundir com o comando "cat" que faz uma coisa completamente diferente
cat ficheiro.txt escreve o conte�do de ficheiro.txt no stdout (seu ecr�)
file ficheiroqualquer descreve qual � o tipo do ficheiro ficheiroqualquer
read var pede ao utilizador para escrever e coloca numa vari�vel (var)
sort ficheiro.txt ordena as linhas no ficheiro.txt
uniq remove as linhas duplicadas, utilizado em combina��o com sort visto uniq remover unicamente linhas duplicadas consecutivas
Exemplo: sort ficheiro.txt | uniq
expr faz matem�tica no shell
Exemplo: adiciona 2 e 3
expr 2 "+" 3
find procura ficheiros
Exemplo: procura por nome:
find . -name ficheiro -print
Este comando tem muitas possibilidades e op��es. � demasiado para ser explicado ao pormenor neste artigo.
tee escreve os dados para stdout (seu ecr�) e para um ficheiro
Normalmente utilizado da seguinte forma:
umcomando | tee ficheiroout
Ele escreve o output de umcomando para o ecr� e para o ficheiro ficheiroout
basename ficheiro devolve o nome do ficheiro de um determinado ficheiro e remove o caminho
Exemplo: basename /bin/tux
devolve unicamente tux
dirname ficheiro devolve unicamente o nome da directoria de um determinado nome e remove o nome do ficheiro
Exemplo: dirname /bin/tux
devolve unicamente /bin
head ficheiro escreve umas linhas desde o inicio do ficheiro
tail file escreve umas linhas desde o fim do ficheiro
sed sed � basicamente um programa de pesquisa e substitui��o. Ele l� texto de um input standard (ex desde um pipe) e escreve o resultado para stdout (normalmente o ecr�). O padr�o de pesquisa � uma express�o regular (ver refer�ncias). Esse padr�o de pesquisa n�o deve ser confundido com a syntax da wildcard do shell. Para substituir a string linuxfocus por LinuxFocus num ficheiro texto fa�a:
cat ficheiro.txt | sed 's/linuxfocus/LinuxFocus/' > novoficheiro.txt
Isto substitui a primeira ocur�ncia da string linuxfocus em cada linha com LinuxFocus. Se existirem linhas onde linuxfocus apare�a varias vezes e que queira substituir todos fa�a:
cat ficheiro.txt | sed 's/linuxfocus/LinuxFocus/g' > novoficheiro.txt
awk A maior parte das vezes awk � utilizado para extrair campos de uma linha de texto. O separador por defeito � espa�o. Para definir um outro utiliza a op��o -F.
 cat ficheiro.txt | awk -F, '{print $1 "," $3 }'
 
Neste caso estamos a utilizar a virgula (,) como separador de campos e escrevemos a primeira e terceira coluna ($1 $3). Se ficheiro.txt tiver linhas como:
 Adam Bor, 34, India Kerry Miller, 22, USA 
ira dar como resultado:
 Adam Bor, India Kerry Miller, USA
 
O awk permite fazer muita mais coisas mas esta � a utiliza��o mais frequente.


2) Conceitos: Pipes, redirecionamento e backtick
N�o s�o realmente comandos mas s�o conceitos muito importantes.

pipes (|) envia o output (stdout) de um programa para o input (stdin) de um outro programa.
 grep "hello"
 ficheiro.txt | wc -l 
procura as linhas com a string hello no ficheiro.txt e conta as linhas.
O output do comando grep � utilizado como input para o comando wc. Dessa forma voc� pode concatenar os comandos que queira (nos limites do razo�vel).

redirecionamento: escreve o output de um comando para um ficheiro ou adiciona dados a um ficheiro
> escreve o output para um ficheiro e sobrep�em o ficheiro antigo caso ele existir
>> adiciona dados a um ficheiro (ou cria um novo se ele ainda n�o existir mas nunca sobrep�em nada).

Backtick
O output de um comando pode ser utilizado como argumento da linha de comandos (n�o stdin como anteriormente, os argumentos da linha de comandos s�o qualquer string que voc� pode especificar depois de um comando como nomes de ficheiros e op��es) para um outro comando. Voc� pode tamb�m utiliza-lo para definir o output de um comando para uma vari�vel.
O comando
 find . -mtime -1 -type f -print 
procura todos os ficheiros que foram modificados nas ultimas 24 horas (-mtime -2 significaria 48 horas). Se voc� quer armazenar esse ficheiros num arquivo tar (ficheiro.tar) a syntax para o tar teria de ser:
 tar xvf ficheiro.tar ficheiro1 ficheiro2 ...
Em vez de escrever isso tudo, voc� pode combinar 2 comandos (find e tar) utilizando backticks. Tar ira aquivar todos os ficheiro que find escreveu:
 #!/bin/sh
# Backticks (`)  n�o plicas ('):
tar -zcvf ultimamod.tar.gz `find . -mtime -1 -type f -print` 

3) Estruturas de control
A instru��o "if" testa se uma condi��o � verdadeira (c�digo retorno � 0, verdadeiro). Se for isso a parte "then" � executado:
if ....; then
   .... 
elif ....; then
   ....
else 
   .... 
fi 
A maior parte das vezes um comando especial chamado test � utilizado dentro de uma instru��o if. Ele pode ser utilizado para comparar strings ou testar se um ficheiro existe, se ele pode ser lido etc...
O comando "test" � escrito com as par�nteses rectos " [ ] ". Nota que o espa�o � significativo aqui: Certifica-se que tenha sempre um espa�o entre as par�nteses rectos. Exemplos:
[ -f "umficheiro" ] : Testa se umficheiro � um ficheiro. 
[ -x "/bin/ls" ]    : Testa se /bin/ls exista e se � um execut�vel.
[ -n "$var" ]       : Testa se a the vari�vel $var tem algo.
[ "$a" = "$b" ]     : Testa se as vari�veis "$a" e "$b" s�o iguais 
Executa o comando "man test" para obter uma longa lista de todos os tipos de operadores de teste para compara��es e ficheiros.
Utilizar isso num script shell � facil:
#!/bin/sh
if [ "$SHELL" = "/bin/bash" ]; then
  echo "o seu shell de login � o bash (bourne again shell)"
else
  echo "o seu shell de login n�o � bash mas sim $SHELL"
fi
A vari�vel $SHELL cont�m o nome do shell de login e ela � testada que � comparada com a string "/bin/bash"

Atalhos de operadores
As pessoas familiarizadas com C reconhecer�o a express�o seguinte:
[ -f "/etc/shadow" ] && echo "Este computador utiliza shadow passwords"
Os && podem ser utilizados como uma curta instru��o if. A parte direita � executada se a parte esquerda for verdadeira. Pode ler isso como AND. Desta maneira o exemplo �: "O ficheiro /etc/shadow exista AND o comando � executada". O operador OR (||) esta tamb�m dispon�vel. Segue um exemplo:
#!/bin/sh
mailfolder=/var/spool/mail/james
[ -r "$mailfolder" ] || { echo "N�o pode ler $mailfolder" ; exit 1; }
echo "$mailfolder t�m um mail de:"
grep "^From " $mailfolder 
O script testa em primeiro se ele pode ler o mailfolder. Se for o caso ele escreve a linha "t�m um mail" no folder, Se ele n�o conseguir ler o ficheiro $mailfolder ent�o o operador OR entra em ac��o. Em Ingl�s voc� pode ler "Mailfolder readable or exit program". O problema aqui � que t�m de ter exactamente um comando atras do OR mas precisamos de 2:
- escrever uma mensagem de erro
- sair do programa
Para trata-los como um �nico comando podemos junta-los numa fun��o an�nima utilizando chavetas. As fun��es em geral ser� explicado mais abaixo.
Pode-se fazer tudo sem ANDs e ORs utilizando unicamente instru��es if mas as vezes os atalhos AND e OR s�o mais pr�ticos.

A instru��o case pode ser utilizado para combinar (utilizando os wildcards do shell como * e ?) uma determinada string em compara��o a numerosas possibilidades.
case ... in
 ...) faz qualquer coisa aqui;; esac 
Vejamos um exemplo. O comando file pode testar qual � o tipo de um ficheiro:
file lf.gz

devolve:

lf.gz: gzip compressed data, deflated, original filename, last modified: Mon
Aug 27 23:09:18 2001, os: Unix 
Vamos utilizar isto agora para escrever um script chamado smartzip que pode descomprimir bzip2, gzip and zip ficheiros comprimidos automaticamente:
#!/bin/sh
ftype=`file "$1"`
case "$ftype" in
"$1: Zip archive"*) 
    unzip "$1" ;;
"$1: gzip compressed"*)
    gunzip "$1" ;;
"$1: bzip2 compressed"*)
    bunzip2 "$1" ;;
*) error "Ficheiro $1 no pode ser descomprimido com smartzip";;
esac 

Aqui pode reparar que estamos a utilizar uma vari�vel especial chamada $1. Essa vari�vel cont�m o primeiro argumento dado ao programa. Por exemplo executamos
smartzip articles.zip
ent�o $1 ira conter a the string articles.zip

A instru��o select � uma extens�o especifica do bash e � muita boa para utiliza��o interactiva. O utilizador por seleccionar uma op��o entre uma lista de diferentes valores:
select var in ... ; do
   break
done
....  agora $var pode ser utilizado ....  
Vejamos este exemplo:
#!/bin/sh
echo "Qual � o seu SO preferido ?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do
   break
done 
echo "Voc� seleccionou $var" 
O que script faz:
Qual � o seu SO preferido ?  
1) Linux 
2) Gnu Hurd 
3) Free BSD
4) Other 
#? 1 
Voc� seleccionou Linux 
No shell voc� t�m as seguintes instru��es de ciclos dispon�veis:
while ...; do
 ....
done 
O ciclo-while sera executada at� que a express�o que estamos a testar ser verdadeira. A palavra-chave "break" pode ser utiliza para abandonar um ciclo a qualquer altura. Com a palavra-chave "continue" o ciclo continua com a proxima itera��o e ignora o resto do corpo do ciclo.

O ciclo-for pega numa lista de strings (srings separadas por espa�o) e atribuas a vari�veis:
for var in ....; do
 ....  
done 
O que sega ira imprimir as letras de A a C no ecr�:
#!/bin/sh for var in A B C ; do
  echo "var � $var" 
done 
Um outro exemplo de script util, chamado showrpm, print uma lista do conte�do de um pacote RPM:
 #!/bin/sh
# lista um resumo do conte�do de um pacote RPM 
UTILIZA��O: showrpm rpmficheiro1 # rpmficheiro2 ...  
EXEMPLO: showrpm /cdrom/RedHat/RPMS/*.rpm
for rpmpackage in $*; do
  if [ -r "$rpmpackage" ];then
     echo "=============== $rpmpackage ==============" 
     rpm -qi -p $rpmpackage
  else 
     echo "ERRO: n�o consegue ler o ficheiro file $rpmpackage"
  fi 
done 
Como pode ver a vari�vel especial, $* que cont�m todos os argumentos da linha de comando. Se voc� executar
showrpm openssh.rpm w3m.rpm webgrep.rpm
ent�o $* ira conter as 3 strings openssh.rpm, w3m.rpm e webgrep.rpm.

O bash GNU conhece tamb�m os ciclos-until mas geralmente os ciclos while e for s�o suficientes.

Utilizando aspas
Antes de passar qualquer argumento a um programa o shell tenta desenvolver as wildcards e vari�veis. Desenvolver significa que as wildcards (ex: *) s�o substitu�das pelo o nome do ficheiro apropriado ou que essa vari�vel � substitu�da pelo o seu conte�do. Para mudar esse comportamento voc� pode utilizar aspas: Digamos que temos um numero de ficheiros numa directoria. 2 deles s�o ficheiros jpg, mail.jpg e tux.jpg.
#!/bin/sh echo *.jpg 
Isto ira escrever "mail.jpg tux.jpg".
Aspas e plicas ira impedir essa expans�o de wildcard:
#!/bin/sh echo "*.jpg" 
echo '*.jpg' 
Isto ira escrever "*.jpg" 2 vezes.
As plicas s�o mais restritivas. Elas evitam igualmente a expans�o de vari�veis. Aspas evitam a expans�o de wildcard mas permita a expans�o de vari�veis:
 
#!/bin/sh 
echo $SHELL 
echo "$SHELL" 
echo '$SHELL' 
Isto ira escrever:
/bin/bash 
/bin/bash 
$SHELL 
E por fim exista a possibilidade de aceitar significado especial de qualquer caracter fazendo preceder da backslash:
echo \*.jpg 
echo \$SHELL 
Isto ira escrever:
 *.jpg $SHELL 
Aqui documentos
Aqui documentos � uma maneira engra�ada de enviar varias linhas de texto para um comando. � �til para escrever um texto de help num script sem ter a necessidade de colocar echo a frente de cada linha. Um aqui documento come�a com << seguindo de qualquer string que pode tamb�m aparecer no fim de um aqui documento. Aqui esta um script de exemplo, chamado ren, que renomeia multiplos ficheiros e utiliza um aqui documento para o texto de ajuda:
#!/bin/sh
# Temos menos de 3 argumentos. Escreve o texto do help:
if [ $# -lt 3 ] ; then
  cat <<HELP 
ren -- renomeia um numero de ficheiros utilizando express�es do sed

UTILIZA��O: ren 'regexp' 'replacement' files...

EXEMPLO: renomeia todos os ficheiros *.HTM para *.html: ren 'HTM$' 'html' *.HTM

HELP exit 0 fi OLD="$1" NEW="$2"
# O comando shift remove um argumento da lista dos argumentos da linha de
# comandos.
shift shift
# $* agora tem todos os ficheiros:
for file in $*; do
   if [ -f "$file" ] ; then 
      newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"`
      if [ -f "$newfile" ]; then
         echo "ERROR: $newfile j� existe" 
      else
         echo "renomeando $file para $newfile ..."
         mv "$file" "$newfile" 
      fi
   fi
done 
� o script mais complexo que vimos at� aqui. Vamos falar um pouco sobre ele. A primeira instru��o if testa se temos ao menos 3 par�metros na linha de comandos. (A vari�vel especial $# cont�m o numero de argumentos). Se n�o for o caso, o texto do help � enviado ao comando cat que por sua vez mande-o para o ecr�. Depois de escrever o texto do help sa�mos do programa. Se tivermos 3 ou mais argumentos vamos atribuir o primeiro argumento a vari�vel OLD e o segundo a vari�vel NEW. A seguir deslocamos os par�metros da linha de comandos 2 vezes para colocar o terceiro argumento na primeira posi��o de $*. Com $* entramos no ciclo for. Cada um dos argumentos em $* � atribu�do um a um a vari�vel $file. A� vamos em primeiro testar se o ficheiro existe realmente e vamos construir um novo nome de ficheiro utilizando find e sed. Os backticks s�o utilizados para atribuir o resultado para a vari�vel newfile. Agora temos tudo o precisamos: O antigo e novo nome do ficheiro. � o que utilizamos com o comando mv para renomear os ficheiros.

Fun��es
Logo que voc� tenha um programa mais complexo voc� ira achar pratico utilizar o mesmo c�digo em v�rios s�tios e dar-lhe alguma estrutura. Uma fun��o tem um aspecto semelhante a este:
nomedafuncao()
{
 # Dentro do corpo $1 esta o primeiro argumento dado a fun��o No $2 esta o
 # segundo ...
 corpo
} 
Tem de "declarar" as fun��es no inicio do script antes de poder utiliza-las.

Aqui esta um script chamado xtitlebar com o qual voc� pode mudar o nome de uma janela de um terminal. Se voc� tiver varias janelas abertas � uma maneira f�cil de as identificar. O script envia uma sequ�ncia de escape que � interpretada pelo terminal e que provoca uma mudan�a de nome da barra da janela. O script utiliza a fun��o chamada help. Como pode ver a fun��o � definida uma vez e utilizada 2 vezes:
#!/bin/sh
# vim: set sw=4 ts=4 et:

help() 
{ 
    cat <<HELP 
xtitlebar -- muda o nome de um xterm, gnome-terminal ou kde konsole

UTILIZA��O: xtitlebar [-h] "texto_para_a_janela"

OP��ES: -h help text

EXEMPLO: xtitlebar "cvs"

HELP 
    exit 0 
}

# Se ocorrer algum erro ou se for introduzido -h chamamos a fun��o help:
[ -z "$1" ] && help 
[ "$1" = "-h" ] && help

# Envia a sequ�ncia de escape que muda o titulo da barra xtem:
echo -e "\033]0;$1\007"
#
� um bom habito de ter sempre uma ajuda extensiva dentro dos scripts. Isto torna poss�vel para os outros (e voc�) utilizar e perceber o script.

Argumentos da linha de comandos
Vimos que S* e $1, $2 ... $9 cont�m os argumentos que o utilizador pode especificar na linha de comandos (as strings escritas a seguir o nome do programa). At� aqui tivemos uma syntax de linha de comandos bastante simples ou com poucos argumentos (alguns argumentos obrigat�rios e a op��o -h para ajuda). Mas rapidamente voc� ira descobrir que necessita de um tipo de analisador para programas mais complexos onde voc� define as suas pr�prias op��es. Por conven��o todos esses par�metros opcionais s�o precedidos por um sinal - e t�m de ser introduzidos antes qualquer outro argumento (como por exemplo os nome dos ficheiros).

Existem varias possibilidades para implementar um analisador. O seguinte ciclo while combinado com uma instru��o case � uma solu��o muita boa para um analizador gen�rico:
 
#!/bin/sh
help() 
{
  cat <<HELP
Isto � uma demo de um analisador de comando.  
EXEMPLO DE UTILIZA��O: cmdparser -l hello -f -- -ficheiro1 ficheiro2 
HELP
  exit 0
}

while [ -n "$1" ]; do 
case $1 in
    -h) help;shift 1;; # a fun��o help � chamada
    -f) opt_f=1;shift 1;; # a vari�vel opt_f � inicializada
    -l) opt_l=$2;shift 2;; # -l pega num argumento -> shift por 2
    --) shift;break;; # fim das op��es
    -*) echo "erro: n�o exista a op��o $1. -h para help";exit 1;;
    *)  break;;
esac
done

echo "opt_f � $opt_f" 
echo "opt_l � $opt_l" 
echo "primeiro argumento � $1" 
echo "2nd argumento � $2" 
Experimenta ! POde excuta-lo com por exemplo:
cmdparser -l hello -f -- -umficheiro1 umficheiro2 
Ele ira produzir
opt_f � 1
opt_l � hello
primeiro argumento � -umficheiro1 
2nd argumento � umficheiro2 
Como � que ele funciona ? Basicamente ele faz um loop pelo todos os argumentos e verifica se coincidam com a instru��o case. Se ele encontrar algum que coincida ele carrega a vari�vel e shift a linha de comando por um. A conven��o unix � que as op��es (coisas come�ando com o menos) t�m de vir em primeiro. Voc� deve de indicar no fim da op��o escrevendo dois sinais menos (--). Voc� precisa, com grep por exemplo, pesquisar uma string que come�a com o sinal menos:
Procura de -xx- no ficheiro f.txt: 
grep -- -xx- f.txt 
O nosso option parser pode manipular os -- tamb�m como pode ver na listagem acima.

Exemplos

Um esqueleto para uma utiliza��o gen�rica

Agora j� falamos sobre praticamente todos os componentes que necessita para escrever um script. Todos os bons scripts t�m de ter uma ajuda e pode tamb�m ter uma op��o por defeito mesmo tendo uma unica op��o. Por isso � uma boa ideia ter um script modelo, chamado framework.sh, o qual voc� pode utilizar como estrutura para outros scripts. Se deseja escrever um novo script basta fazer uma copia:
cp framework.sh myscript 
e inserir a actual funcionalidade dentro de "myscript".

Vamos agora ver mais dois exemplos:

Convers�o de um numero bin�rio para decimal

O script b2d converte um numero bin�rio (ex: 1101) para o seu equivalente em decimal. � um exemplo que mostra que pode fazer opera��es matem�ticas simples com expr:
 
#!/bin/sh
# vim: set sw=4 ts=4 et:
help()
{
 cat <<HELP b2h
-- converte bin�rio para decimal

UTILIZA��O: b2h [-h] binarynum

OP��ES: -h help text

EXEMPLO: b2h 111010 retorna 58 HELP exit 0 }

error() 
{
    # escreve um erro e sai
    echo "$1" exit 1 
}

lastchar() 
{
    # devolve o ultimo caracter de uma string no $rval
    if [ -z "$1" ]; then
        # string vazia
        rval="" return fi
    # wc coloca algum espa�o atras o output � por caso disso que necessitamos
    # de sed:
    numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `
    # agora corta o ultimo caractero
    rval=`echo -n "$1" | cut -b $numofchar` 
}

chop() 
{
    # remove o ultimo caracter numa string and retorno dentro de $rval
    if [ -z "$1" ]; then
        # string vazia
        rval="" return fi
    # wc coloca algum espa�o atras o output � por caso disso que necessitamos
    # de sed:
    numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `
    if [ "$numofchar" = "1" ]; then
        # unicamente um caracter na string
        rval="" 
        return 
    fi 
    numofcharminus1=`expr $numofchar "-" 1` 
    # agora corta tudo mas o ultimo caractero:
    rval=`echo -n "$1" | cut -b 0-${numofcharminus1}` 
}
    

while [ -n "$1" ]; do 
case $1 in 
    -h) help;shift 1;; # fun��o help � chamada 
    --) shift;break;; # fim das op��es
    -*) error "erro: no such option $1. -h for help";;
     *)  break;;
esac
done

# O programa principal
sum=0 
weight=1
# T�m de ser dado um argumento:
[ -z "$1" ] && help 
binnum="$1" 
binnumorig="$1"

while [ -n "$binnum" ]; do
   lastchar "$binnum"
   if [ "$rval" = "1" ]; then
       sum=`expr "$weight" "+" "$sum"`
   fi
   # remove a ultima posi��o no $binnum
   chop "$binnum" 
   binnum="$rval" 
   weight=`expr "$weight" "*" 2` 
done

echo "bin�rio $binnumorig � decimal $sum"
# 
O algoritmo utilizado neste script pega no peso decimal (1,2,4,8,16,..) de cada numero come�ando com o numero mais a direita e adiciona-o a soma se o n�mero � um 1. Desta maneira "10" �:
0 * 1 + 1 * 2 = 2
Para obter os n�meros da string utilizamos a fun��o lastchar. Esta ultima utiliza wc -c para contar o numero de caracteres da string e vai cortando o �ltimo caracter. A fun��o chop t�m a mesma l�gica mas remove o �ltimo caracter, isto � que ele remove tudo desde o inicio at� o pen�ltimo caracter.

Um programa de rota��o de ficheiros
Provavelmente voc� � um desses que grava todo o mail que sai para um ficheiro. Depois de algum meses esse ficheiro se torna bastante grande e torna o acesso lente se voc� o l� dentro do seu programa de mail. O script seguinte rotatefile pode ajudar. Ele renomeia o mailfolder, vamos chama-lo outmail, para outmail.1 se j� existir um ent�o ele torna-se outmail.2 etc...
 
#!/bin/sh
# vim: set sw=4 ts=4 et: 
ver="0.1"
help() 
{
    cat <<HELP 
rotatefile -- roda de ficheiro 

UTILIZA��O: rotatefile [-h] ficheiro

OP��ES: -h help text

EXEMPLO: rotatefile out Isto ira renomeiar por exemplo out.2 para out.3, out.1
para out.2, out para out.1 e criar um ficheiro out vazio

O numero m�ximo � 10

version $ver
HELP
   exit 0 
}

error() 
{
    echo "$1" 
    exit 1 
}
while [ -n "$1" ]; do 
case $1 in 
    -h) help;shift 1;;
    --) break;;
    -*) echo "erro: no such option $1. -h for help";exit 1;;
     *) break;;
esac 
done

# verifica��o de input:
if [ -z "$1" ] ; then
 error "ERRO: t�m de especificar um ficheiro, utiliza -h para ajuda" 
fi
filen="$1"

# renomeia qualquer ficheiro .1 , .2 etc:
for n in  9 8 7 6 5 4 3 2 1; do 
   if [ -f "$filen.$n" ]; then 
      p=`expr $n + 1`
      echo "mv $filen.$n $filen.$p" 
      mv $filen.$n $filen.$p
   fi
done

# renomeia o ficheiro original:
if [ -f "$filen" ]; then 
   echo "mv $filen $filen.1" 
   mv $filen $filen.1
fi 
echo touch $filen 
touch $filen 
Como funciona o programa ? Ap�s verifica��o que o utilizador introduziu um ficheiro vamos para um ciclo que vai de 9 a 1. O ficheiro 9 � agora renomeado para 10, ficheiro 8 para 9 e assim por diante. Depois do ciclo renomeamos o ficheiro original para 1 e criamos um ficheiro vazio com o nome do ficheiro original.

Debugging

A ajuda mais simples para debugging � obviamente o comando echo. Voc� pode utiliza-lo para ver o conte�do de uma vari�vel especifica perto do sitio onde voc� suspeita estar o erro. � provavelmente o que os programadores em shell fazem 80% das vezes para procurar um erro. A avantajem do shell scripting � que n�o � necess�rio recompilar e inserir um comando "echo" � feito muito rapidamente.

O shell tem tamb�m um verdadeiro modo debug. Se existir um erro no seu script "strangescript" ent�o pode fazer debug desta forma:
sh -x strangescript 
Isto ira executar o script e mostrar todos as instru��es que s�o executadas com as vari�veis e wildcards j� expandido.

O shell t�m tamb�m um modo para verificar a sintaxe sem realmente executar o programa. Para utilizar fazer:
sh -n o_script 
Se n�o retornar nada ent�o o seu programa esta sem erros de sintaxe.

Esperamos que agora ira come�ar a escrever os seus pr�prios scripts. Divirta-se !

Refer�ncias