Shell Scripts
Shell Scripts
eBook
Shell
Scripts
Adbeel Goes Filho
Versão 2004.10.25
Índice
Capitulo 1. Introdução ......................................................................................................... 8
1.O que é Shell ?............................................................................................................. 8
2.O que são os shell scripts ? ......................................................................................... 9
3.Definições .................................................................................................................. 10
4.Variáveis .................................................................................................................... 10
5.Formato dos Arquivos de Shell Script ........................................................................ 18
6.Exercicios.................................................................................................................... 28
Capitulo 2. Execução de Programas.................................................................................. 30
1.Execução em foreground e background .................................................................... 30
2.Redirecionamento....................................................................................................... 31
3.Metacaracteres e Wildcards....................................................................................... 34
4.Sequências de Escape ANSI...................................................................................... 37
5.Códigos de Escape .................................................................................................... 42
6.Conclusão .................................................................................................................. 43
7.Exercicios.................................................................................................................... 44
Capitulo 3. Comandos Condicionais.................................................................................. 45
1.Comando "test" .......................................................................................................... 47
2.Expressões Aritméticas............................................................................................... 54
3.Conclusão .................................................................................................................. 56
Capitulo 4. Comandos de Repetição................................................................................. 57
1.while............................................................................................................................ 57
2.for ............................................................................................................................... 58
3.until............................................................................................................................. 61
4.case ........................................................................................................................... 61
5.break e continue......................................................................................................... 62
6.Redirecionando loops................................................................................................. 64
7.select .......................................................................................................................... 70
8.Funções...................................................................................................................... 73
9.Tornando seu script amigável..................................................................................... 82
10.dialog........................................................................................................................ 84
Capitulo 5. Comandos Avançados..................................................................................... 89
1.cut............................................................................................................................... 89
2.wc................................................................................................................................ 93
3.tee............................................................................................................................... 93
4.sort.............................................................................................................................. 94
5.grep............................................................................................................................. 95
6.sed............................................................................................................................ 101
Capitulo 6. Bibliografia..................................................................................................... 108
Capitulo 7. Links............................................................................................................... 109
Capitulo 8. Anexos........................................................................................................... 110
1.Exemplos.................................................................................................................. 111
2.Laboratórios.............................................................................................................. 127
3.Exercicios.................................................................................................................. 134
4.Links.......................................................................................................................... 135
5.GNU Free Documentation License........................................................................... 137
Agradecimentos
Agradecimentos Especiais
A todos os outros que, pelo esquecimento, tipico da natureza humana, deixamos de citar.
Este trabalho foi editado e composto eletronicamente utilizando somente ferramentes livres:
O Autor
Abreviações Utilizadas
Capitulo 1. Introdução
1. O que é Shell ?
Linux 2.2.16.
Last login: Mon Sep 25 10:41:12 -0300 2000 on tty1.
No mail.
virtvs@mucuripe:~$
Essa última linha é o shell. Se você digitar o comando "ps", verá que um dos programas rodando é o
seu shell . O comando “ps” lista os processos em :
virtvs@mucuripe:~$ ps
PID TTY TIME CMD
164 tty2 00:00:00 bash
213 tty2 00:00:00 ps
virtvs@mucuripe:~$
Ou seja, o shell utilizado nesse console é o bash, que está rodando com PID 164.
Existem diversos tipos de shell: bash, csh, ksh, ash, etc. Por exemplo, o AIX/6000
possui 5 tipos de shell disponíveis: korn shell (ksh), bourne shell (bsh, sh), C shell (csh), trusted shell
(tsh), restricted shell (rsh). O default para este sistema é o ksh. O mais utilizado atualmente é o bash
(GNU Bourne-Again SHell). Por isso o tomaremos como referência.
Resumindo essa seção, é importante saber que para cada terminal ou console aberto,
tem-se um shell rodando. Assim, se você tem 3 xterms abertos na interface gráfica, vai ter um shell
para cada xterm.
Ao executar o shell script, o shell atual (no qual você deu o comando) abre um novo
shell para executar o script. Assim, os scripts são executados em um shell próprio (a menos que se
especifique, ao chamar o script, para executá-lo no shell atual). Isso será importante quando formos
tratar de variáveis de ambiente.
O uso de alias
Alias é um novo nome, ou apelido, para o comando. Pode ser usado para abreviar
longas linhas de comando ou fazer com que comandos comportem-se diferente da execução padrão.
Exemplo:
alias cls=clear
alias ls='ls -logt'
alias dir=ls
Shell scripts são conjuntos de comandos armazenados em um arquivo texto que são
executados seqüencialmente. Nesta primeira parte, faremos uma introdução sobre o shell, o formato
desses arquivos e variáveis de ambiente. Shell pode ser standard ou restrito, na verdade é um
comando que possibilita gerar textos com lógica de programação. É muito utilizado para montagem
de ambientes de login e abertura de novos processos que indicam nova seção.
Existe vários tipos de shells, a mais utilizada é a sh, que é um link da bash, ou seja
/bin/sh é link de /bin/bash
Detalhes importantes
Para colocar comentários assim como no PERL, utilize o # (chamado culturalmente de sustenido)
3. Definições
Branco e TAB ou espaço são a mesma coisa. Um nome é uma sequência de letras,
digitos ou underscores começando com uma letra ou underscore. Um parâmetro é um nome um
digito ou algum desses caracteres: *, #, ?, -, $, e !.
Comando : é uma sequência de não brancos separados por brancos. A primeira sequência ou
argumento é o argumento 0.
Pipeline : é uma sequência de um ou mais comandos separados por | (ou, para compatibilidade
histórica, pelo ^). A função de uma pipeline é sincronizar a entrada ou saida padrão para um arquivo
do tipo FIFO.
Uma lista : é uma sequência de pipelines separadas por;. &, &&, or ||, e opcionalmente, terminada
por ; ou &.
4. Variáveis
Uma variável é onde o shell armazena determinados valores para utilização posterior.
São áreas de memória onde podem ser armazenados dados. Estes dados podem ser números, textos,
listas de arquivos e até mesmo resultados da saida de comandos que podem ser utilizados
posteriormente. Existem duas categorias de variáveis que podemos definir no ambiente Unix:
1. Variáveis locais – disponíveis somente para o Shell corrente, não sendo acessadas
pelos subprocessos;
2. Variáveis ambientais ou globais – disponíveis tanto para o Shell corrente como para
os subprocessos que venham a usar o conteúdo das variáveis definidas. Para
transformar uma variável com escopo global utilize o comando export. Exemplo:
export <variável>
Para a visualização das variáveis locais, usa-se o comando set. Para verificar quais
variáveis estão exportadas, usa-se o comando env.
Toda variável possui um nome e um valor associado a ela, podendo ser este último
vazio. Para listar as variáveis atualmente definidas no shell digite o comando set. Para se definir uma
variável, basta utilizar a síntaxe (Sem espaço ao redor do =):
nome_da_variável=conteúdo
Por exemplo, queremos definir uma variável chamada "cor" com o valor de "azul":
virtvs@mucuripe:~$ cor=azul
Para utilizar o valor de uma variável, é só colocar um sinal de "$" seguido do nome da
variável - o shell automaticamente substitui pelo valor da variável:
Em alguns casos, é aconselhável colocar o nome da variável entre chaves {}. Por
exemplo, se quero imprimir "azul-escuro", como faria ? Simplesmente echo $cor-escuro ? Não
funcionaria, pois o bash vai procurar uma variável de nome "cor-escuro". Portanto, temos que
colocar o nome "cor" entre chaves para delimitar o nome da variável:
Algumas variáveis já são predefinidas no shell, como o PATH, que, como foi dito antes,
armazena o caminho dos programas. Por exemplo, a minha variável PATH contém:
ou seja, quando digito um comando, como "ls", o shell vai começar a procurá-lo em /
usr/local/bin, se não encontrá-lo, vai procurar em /usr/bin e assim por diante. Repare que os
diretórios são separados por um sinal de dois pontos (:).
É importante destacar que o shell possui várias variáveis pré-definidas, ou seja, que
possuem um significado especial para ele, entre elas: PATH, PWD, PS1, PS2, USER e UID.
Assim, quando iniciamos um novo shell (ao executar o nosso script), essas variáveis
especiais são "herdadas" do shell pai (o que executou o shell filho). Outras variáveis definidas pelo
usuário, como a variável "cor" não são passadas do shell pai para o filho.
Quando o script terminar, o seu shell (shell filho) simplesmente desaparece e com ele
também as suas variáveis, liberando o espaço ocupado na memória.
Para tornar uma variável imune à alteração ou deleção, deve-se usar o comando
readonly
Variáveis somente leitura não podem ser apagadas. Elas somente deixarão de existir no
momento em que for efetuado o logout da sessão de terminal, ou se o programa que criou a variável
for encerrado, exceto no caso de o Shell-script estar rodando na mesma instância de interpretador
que iniciou a execução do script-Shell.
readonly nome
Para apagar uma variável, use o comando unset:
unset nome
Variáveis Array
Também conhecidas como vetores. Este tipo de variável serve para armazenar vários
valores sob um nome e um índice. A maneira de declarar variáveis array é a seguinte:
NomeDaVariavel[Indice]=Valor
Imaginemos que Maria queira armazenar uma lista de suas frutas favoritas em uma variável array.
Para isso ela faria o seguinte:
$ FRUTA[0]=goiaba
$ FRUTA[1]=manga
$ FRUTA[2]=pera
$ FRUTA[3]=laranja
Supondo que ele colocou esta lista em ordem decrescente de gosto, para sabermos qual é a sua fruta
favorita basta digitarmos:
$ echo ${FRUTA[0]}
Se colocarmos $FRUTA[0] e shell exibirá goiaba[0] (goiaba concatenado com [0]. Desta forma
nota-se a necessidade do uso de {}
$FRUTA=goiaba
e depois quiser fazer um array com o nome FRUTA eu posso fazer assim:
$ FRUTA[1]=manga
Outra coisa interessante é que podemos declarar um array inteiro numa única linha de comando. Para
isto usamos a sintaxe:
E para vermos toda a lista de uma vez só, podemos usar o seguinte comando:
$ echo ${FRUTA[*]}
Existem várias outras especificações para arrays, aqui é só o básico. E se você precisar usar arrays
de maneira mais complexa que isso procure a documentação oficial do bash.
Ambiente do usuário
A variável PS1
Esta é a "Prompt String 1" ou "Primary Prompt String". Nada mais é do que o prompt
que nos mostra que o shell está esperando um comando. Quando você muda PS1 você muda a
aparência do prompt. Na minha máquina o padrão é '\u@\h:\w\$ ' onde \u significa o nome do
usuário, \h significa o nome do host e \w é o diretório atual, o que dá a seguinte aparência:
meleu@meleu:/usr/doc/Linux-HOWTOs$
Se você estiver afim de ter a impressão de que está no shell do root basta trocar o '$' por '#'
Para aprender a fazer um monte de gracinhas com o PS1 dê uma lida no Bash-Prompt-HOWTO
A variável PS2
Esta é a "Secondary Prompt String". É usada quando um comando usa mais de uma
linha. Por exemplo:
$ echo m\
> e\
> l\
> e\
>u
meleu
$ echo 'm
>e
>l
>e
> u'
m
e
l
e
u
Este sinal '> ' (maior-espaço) é o PS2. Você pode usar os mesmos caracteres especiais que o PS1
usa.
A variável MAIL
Nada mais é do que o arquivo onde são guardados seus emails. Aqui na minha máquina
eu uso o sendmail como servidor de email, portanto:
A variável SHLVL
Esta variável armazena quantos shells você executou a partir da primeira shell.
Imagine que você está usando o bash e executou o bash de novo, nesta situação o seu
SHLVL vale 2. Veja isto:
$ echo $SHLVL
1
$ bash # estou executando o bash a partir do bash
$ echo $SHLVL
2
$ exit # saí do segundo bash
exit
$ echo $SHLVL
1
A variável PROMPT_COMMAND
Esta é bem interessante. Ela armazena um comando que será executado toda hora que o
prompt é exibido. Veja:
$ PROMPT_COMMAND="date +%T"
19:24:13
$ cd
19:24:17
$ ls
GNUstep/ bons.txt pratica/ teste worldwritable.txt Mail/ hacking/ progs/ txts/
19:24:19
$
19:24:32
$ # isso eh uma linha sem nenhum comando
19:24:49
$
Esta variável é útil quando queremos brincar com o prompt, para aprender mais sobre isso leia o
Bash-Prompt-HOWTO (v. 10. Referências).
A variável IFS
O shell usa esta variável para dividir uma string em palavras separadas. Normalmente o
IFS é um espaço, uma tabulação (Tab) e um caractere nova linha (\n). Desta maneira:
são quatro palavras, pois IFS é um espaço e as palavras estão separadas por espaço. Agora se eu
mudar IFS para um ':' desta maneira:
IFS=':'
então a string:
isto:eh:uma:string
conterá quatro palavras. Isto é útil para casos como neste exemplo:
#!/bin/bash
IFS=':'
for item in $PATH ; do
echo $item
done
Se IFS for uma variável nula (vazia), tudo será considerado uma única palavra. Por exemplo: se o
IFS for nulo, toda essa linha será considerada uma única palavra
A variável RANDOM
Quando você exibe esta variável ("echo $RANDOM") é exibido um número aleatório
entre 0 e 32767.
#!/bin/bash
NUM="98$(echo $RANDOM)0"
CONT=$(echo -n $NUM | wc -c) # quantos digitos tem?
while [ $CONT -lt 8 ]; do # se nao tiver 8 digitos acrescenta 0's
NUM=${NUM}0
CONT=$(echo -n $NUM | wc -c)
done
echo $NUM
Outras Variáveis
Estas são as mais utilizadas, porém existem muitas outras. Para ver quais são as
variáveis definidas no momento basta entrar com o comando "set". E para ver apenas as variáveis de
ambiente use "env".
Olhe a man page do bash na seção "Shell Variables" para mais detalhes.
nome=conteúdo
Não deve haver espaço antes ou depois do sinal de igual. Isso assegura que a atribuição seja feita
corretamente, não sendo interpretada com o um comando com argumentos separados por espaço.
Exercícios
A primeira linha de todo shell-script deve começar com algo do tipo (Se não colocar é
assumido o shell padrão):
#!/bin/bash
a qual indica com qual shell deverá ser executado o script. Nesse exemplo, estamos falando para o
shell atual executar o script com o shell /bin/bash. Se quisermos que o nosso script seja executado
com o shell csh, devemos colocar nessa primeira linha:
#!/bin/csh
Como usaremos o bash como nosso shell de referência, todas as linhas dos nossos
scripts começarão com #!/bin/bash
Digamos que você executa freqüentemente o comando:
find / -name file -print
que procura na raiz (/) por um arquivo de nome "file". Só que é incômodo ficar
digitando esse comando toda vez que se quer procurar um arquivo. Então vamos criar um shell
script que contenha esse comando. Vamos chamar esse shell script de "procura". Seu conteúdo fica
assim:
#!/bin/bash
find / -name file -print
Tornemos agora o arquivo executável: chmod 755 ./procura . Porém, ao tentar executar o nosso
script, teremos um problema.
./procura
Este script irá procurar por um arquivo chamado "file". Como especificar qual arquivo
queremos procurar ? Seria ideal executarmos o nosso script seguido do nome do arquivo que
queremos procurar. Por exemplo, queremos saber onde está o arquivo "netscape":
./procura netscape
Quando o bash lê a variável "$1", ele a substitui pelo primeiro parâmetro passado na
linha de comando para o nosso script. Então, se executamos ./procura netscape , a variável "$1" será
substituída por "netscape", como a gente queria. Repare que a variável "$2" conteria o segundo
parâmetro passado para o script e assim por diante.
Sendo assim, qualquer comando colocado abaixo de find seria executado após ele.
Esse é o essencial do shell script: poder automatizar a execução de programas e comandos como se
estivessem sendo digitados diretamente no console ou terminal.
Cada variável tem um valor associado a ela. Quando o nome de uma variável for
precedido por uma sinal de $ (dólar), o Shell substituirá o parâmetro pelo conteúdo da variável. Este
procedimento é conhecido com Substituição de Variável. Uma das maneiras de exibir o conteúdo
de uma variável é usando o comando echo.
Existem dois tipos de parâmetros: posicional e o de palavra chave. Se o parâmetro é um
digito, então é posicional. Palavras chave (também conhecida como variáveis) podem ter seus
valores escritos.
${variavel:-string}
Se "variavel" não tiver sido definida ou for vazia será substituída por "string". O valor
da variável não é alterado. Veja este exemplo:
$ echo ${URL:-"http://unsekurity.virtualave.net"}
http://unsekurity.virtualave.net
$ echo $URL # observe que URL nao foi alterado
${variavel:=string}
Se "variavel" não estiver sido definida ou for vazia, receberá "string". Exemplo:
$ echo ${WWW:="http://meleu.da.ru"}
http://meleu.da.ru
$ echo $WWW
http://meleu.da.ru
${variavel:?string}
Se "variavel" não estiver sido definido ou for vazia, "string" será escrito em stderr (saída de erro
padrão). O valor da variável não é alterado. Veja um exemplo:
${variavel:+string}
Se "variavel" estiver definida, será substituída por "string" mas seu valor não será alterado. Exemplo:
Variáveis Somente-Leitura
Como sabemos as variáveis podem ter seu valor modificado pelo usuário, mas se nós
quisermos variáveis que NÃO possam ter seus valores alterados basta declararmos tal variável como
somente-leitura. Para tornar uma variável read-only podemos usar o comando "readonly" ou então
"declare -r".
Veja os exemplos a seguir, ambos possuem o mesmo resultado:
$ NOME=Fulano
bash: NOME: readonly variable
$ echo $NOME
Meleu Zord
Um bom uso deste tipo de variável é para garantir que variáveis importantes de um
determinado script não possam ser sobregravadas, evitando assim algum resultado crítico.
O comando "readonly" quando usado sem parâmetros (ou o comando "declare" apenas
com o parâmetro "-r") nos mostra todas as variáveis declaradas como somente-leitura. No exemplo a
seguir se for usado "declare -r" no lugar de "readonly" teríamos a mesma saída.
$ readonly
declare -ar BASH_VERSINFO='([0]="2" [1]="05" [2]="0" [3]="1" [4]="release"
[5]="i386-slackware-linux-gnu")'
declare -ir EUID="1005"
declare -ir PPID="1"
declare -r
SHELLOPTS="braceexpand:hashall:histexpand:monitor:ignoreeof:interactive-comments:emacs"
declare -ir UID="1005"
Existem variáveis somente-leitura que são inicializadas pelo shell, como USER, UID.
TODAS as variáveis readonly uma vez declaradas não podem ser "unsetadas" ou ter seus valores
modificados. O único meio de apagar as variáveis readonly declaradas pelo usuário é saindo do shell
(logout).
Parâmetros
Podemos passar parâmetros para o shell-script assim como na maioria dos programas.
Os parâmetros são variáveis, digamos, especiais. Para começar elas não obedecem as regras de
nomeclatura de variáveis, pois elas usam números; e também nós não podemos mudar o valor destas
variáveis pelas vias "tradicionais", para mudar o valor destas nós temos que contar com a ajuda do
shift e/ou do set (como veremos adiante).
Exemplo:
#!/bin/bash
#
# "basename" serve para eliminar o caminho do arquivo e mostrar
# somente o último nome dele. Neste caso: parametros.sh
echo "Nome do script: `basename $0`"
echo "Número total de parâmetros: $#"
echo "Primeiro parâmetro: $1"
echo "Segundo parâmetro: $2"
echo "Décimo quinto parâmetro: ${15}"
echo "Todos os parâmetros: $*"
$ ./parametros.sh a b c d e f g h i j l m n o p q r s t u v x z
Nome do script: parametros.sh
Número total de parâmetros: 23
Primeiro parâmetro: a
Segundo parâmetro: b
Décimo quinto parâmetro: p
Todos os parâmetros: a b c d e f g h i j l m n o p q r s t u v x z
Se você não entendeu direito a diferença entre o $* e o $@, então dê uma olhada no seguinte script
(se não entendê-lo tudo bem, siga em frente e quando aprender sobre o "if" e o "for" leia-o
novamente):
Exemplo:
#!/bin/bash
# Ao executar este script entre alguns parametros. Ex.:
# [prompt]$ ./testargs.sh um dois tres quatro
if [ -z "$1" ]; then
echo "Uso: `basename $0` argumento1 argumento2 etc."
exit 1
fi
echo
echo "Listando argumentos com \"\$*\":"
num=1
for arg in "$*"; do
echo "Arg #$num = $arg"
num=$[ $num + 1 ]
done
# Conclusão: $* mostra todos os argumentos como uma única string
echo
echo
shift
O bash possui um comando embutido que lida com parâmetros, o shift. Quando você
usa o shift sai o primeiro parâmetro da lista e o segundo vai para $1 o terceiro vai para $2, e assim
por diante. Você pode ainda especificar quantas "casas" você quer que os parâmetros "andem" à
esquerda através de "shift n" onde 'n' é o número de casas, mas se o número de casas que ele deve
andar for maior que o número de parâmetros o shift não é executado.
Veja este exemplo:
#!/bin/bash
echo "$#: $*"
echo -e "executando \"shift\""
shift
echo "$#: $*"
echo -e "executando \"shift 5\""
shift 5
echo "$#: $*"
echo -e "executando \"shift 7\""
shift 7
echo "$#: $*"
$ ./shift-exemplo.sh 1 2 3 4 5 6 7 8 9 0
10: 1 2 3 4 5 6 7 8 9 0
executando "shift"
9: 2 3 4 5 6 7 8 9 0
executando "shift 5"
4: 7 8 9 0
executando "shift 7"
4: 7 8 9 0
O que vou passar neste tópico não é sobre como usar "todo o poder do comando set",
e sim como usar set especificamente para editar parâmetros. Não tem nenhum segredo! Veja este
exemplo:
Isso fará com que $1 seja 'um', $2 seja 'dois', $3 seja 'tres' e só!
Não existirá $4, $5, etc. mesmo que eles tenham sido usados. Veja um exemplo de script:
#!/bin/bash
Não interessa quantos parâmetros você passar para este script, no final você só terá $1,
$2 e $3 valendo 'um', 'dois' e 'tres', respectivamente.
Quoting
· caracter deve ser cotado pela precedência com um \. O par \new-line é ignorado. Todos os
caracteres fechados entre o par de acentos (‘), exceto aspas simples são cotadas.
· Um & no final do comando indica que este irá ser executado em background. O shell mostra o
número do processo gerado.
Para que você não perca este processo ao terminar seu shell, execute seu comando assim:
Exemplos:
echo $PATH
/usr/bin:/usr/local/bin
arquivo=/home/morro.txt
more $arquivo
txt1=Casa
txt2=Mae
txt3=Joana
echo ${txt1}da$txt2$txt3
Observe que no último exemplo foram usadas as chaves para circundar o nome da
variável, senão o Shell poderia interpretar a variável como txt1da, o que seria o nome da variável
diferente de txt1, caso as chaves não fossem usadas, gerando mensagem de erro de parâmetro não
definido, pois nesse caso a variável txt1da não existiria.
Substituição de comando
$(comando) ou `comando`
Exemplo:
dir_atual=$(pwd)
cp $(ls *.txt) /home/user1/backup
Substituição do ~ (til)
Exemplo:
echo ~
ls -1F ~/file1
ls – logt ~+/poodle
ls ~-
Prompting
Input / Output
Antes de um comando ser executado, sua entrada e saida pode ser redirecionada
usando as notações interpretadas pelo shell.
Exemplo:
... 2>&1
Arquivo associado ao descritor 2 com o arquivo corrente associado com o descritor do
arquivo 1. Note que o redirecionamento de I/O é necessário se você quer sincronia com stdout e
stderr para o mesmo arquivo. Esta opção é bastante usado para se configurar impressoras ou
dispositivos seriais.
Sinais
Comandos especiais
break [ n ]
continue [ n ]
cd [ arg ]
echo [ arg ... ]
eval [ arg ... ]
exit [ n ]
export [ name ... ]
hash [ -r ][ name ... ]
newgrp [ arg ... ]
pwd
read [ name ... ]
return [ n ]
set [ --aefhkntuvx [ arg ... ] ]
shift [ n ]
test
times
trap [ arg ] [ n ]
type [ name ... ]
ulimit [ -f [ n ] ]
umask [ nnn ]
unset [ name ... ]
wait [ n ]
Chamada do sh
-c string - Se a chave -c está presente, então os comandos estão lendo de uma string.
-s - Se a chave -s está presente, então os comandos estão lendo da entrada padrão, o shell
irá de cada parametro posicional.
-i - Se a entrada do shell estiver conectada a um terminal, então este shell é interativo,
Neste caso:
sh pode retornar :
0 - Sucesso
1 - A execução do programa com erro (Veja Comandos especiais)
2 - Sintax Error (Erro de Sintaxe)
3 - Signal received that is not trapped
6. Exercicios
6. Utilizando redirecionamento, como você faria para escrever um pequeno texto no arquivo carta ?
10.É possível redirecionar mais do que um dispositivo padrão num só comando ? Caso afirmativo,
faça um exemplo redirecionando todos os dispositivos padrões.
11. A ordem com que são feitos os redirecionamentos é relevante para a ação tomada pelo
comando ? E usando associações ? Explique.
15.Crie uma variável chamado MYNAME e armazene nela o seu primeiro nome. Como se mostra o
conteúdo da variável ?
16.Como fazer para que o Shell filho “enxergue” o conteúdo da variável MYNAME ?
18.Modifique seu prompt de comando, de modo que ele fique com a aparência a seguir:
[user1]-10/10/2000 as 06:45-/home/user1 =>
21.Copie a lista detalhada dos processos em execução para o arquivo lista.txt no seu diretório home.
22.Sabendo que:
a=1
b=2
c=3
cp arquivo1 arquivo2
o shell executa esse comando, fica esperando ele terminar, para depois nos retornar a linha de
comando. Isso chama-se execução em primeiro plano, ou foreground, em inglês.Agora digamos que
o arquivo a ser copiado seja muito grande, digamos, 50Mb. Enquanto o comando cp é executado,
vamos ficar com o shell preso a ele todo esse tempo, ou seja, cerca de 1 ou 2 minutos (Dependendo
da máquina). Somente após isso vamos ter a linha de comando de volta para podermos continuar
trabalhando.
Pronto, assim que teclarmos [enter], teremos a nossa linha de comando de volta e mais
duas informações: "[1] 206".
A primeira informação ([1]) chama-se job. Ela identifica os programas que estão sendo
executados em bg (background). Por exemplo, se executarmos mais um programa em bg enquanto
este "cp" está sendo executado, o novo programa recebe o job de número 2 ([2]) e assim por diante.
Mas aí temos um pequeno problema. Digamos que você queira trazer o programa para
fg, por exemplo, se o programa executado em bg for um tocador de mp3 e você quiser mudar a
música. Como fazemos? Usamos o comando fg [job] (intuitivo, não ?) do bash. No exemplo acima,
digamos que eu execute o "cp" em bg, mas depois queira trazê-lo para fg para cancelá-lo. Seria
simples:
Assim trazemos o "cp" para primeiro plano e a linha de comando só retorna quando ele terminar.
Opcional
Quando teclamos o Ctrl + Z, o bash nos diz que o job 1 foi parado ([1]+ Stopped) e
quando executamos "bg 1" ele nos diz que o comando voltou a ser executado, mas em bg (repare o
sinal de "&" no final da linha): [1]+ cp -i arquivo1 arquivo2 &.
2. Redirecionamento
Agora se quisermos procurar quantos arquivos compactados com o gzip (extensão .gz)
tem no nosso sistema para depois analisá-los e possivelmente apagar os repetidos ou inúteis, teremos
uma lista muito grande. Aqui no meu sistema deu mais de 1000 arquivos. Então, seria útil podermos
enviar as mensagens que vão para o console, para um arquivo. Assim, poderíamos analisá-lo depois
de executar o comando. Isso é extremamente útil ao trabalharmos com shell script - enviar as saídas
dos comandos para arquivos para depois analisá-las. Exemplificando:
Notamos que nem tudo foi para o arquivo lista.txt. As mensagens de erro foram para o
console. Isso porque existem dois tipos de saída: a saída padrão e a saída de erro. A saída padrão é a
saída normal dos programas, que no caso acima, seria os arquivos encontrados. E a saída de erro,
como o próprio nome diz, são os erro encontrados pelo programa durante sua execução, que devem
ser informados ao usuário, como os erros de "permissão negada".
E aí, como selecionar uma ou outra ? É simples, apenas devemos indicar o "file
descriptor" (fd) delas. Não vamos entrar em detalhes sobre o que é "descritor de arquivos" por fugir
do nosso escopo e ser um pouco complicado, apenas temos que saber que o fd 1 corresponde a saída
padrão e o fd 2 a saída de erro. Assim, no exemplo acima, para enviar os erro para o arquivo
erros.txt e a saída padrão para o arquivo lista.txt, usaríamos:
Agora digamos que queremos ver o conteúdo do arquivo lista.txt. Podemos abrí-lo com
um editor de textos ou usar o "cat". Optando pela última opção:
Temos um problema. O arquivo tem mais de 1000 linhas e não conseguimos vê-lo
inteiro. Podemos redirecionar a saída do comando "cat" para o comando "more", que dá pausa entre
as telas:
Veja que utilizamos o caractere "|", chamado "pipe". Não poderíamos ter utilizado o
sinal de ">"? Não, o sinal de ">" é somente para enviar a saída para um ARQUIVO. Para enviar a
saída de um comando para a entrada de outro, usamos o pipe.
As vezes queremos que além da saída ir para um arquivo, ela também vá para a tela.
Temos um programinha que faz isso. Ele chama-se "tee". Veja o exemplo a seguir:
O que fizemos ? Primeiro pegamos a saída de erro e enviamos para o arquivo erros.txt.
O restante, ou seja, a saída padrão, estamos enviando para a entrada do comando tee (usando o
pipe). O tee simplesmente pega o que ele recebe (através do pipe) e joga no arquivo lista.txt e na
tela. Assim, além de gravarmos a saída num arquivo, podemos mostrar ao usuário o que está
acontecendo. Isso é muito útil quando escrevemos alguns scripts.
echo
Existem alguns momentos que você não quer que a saída do echo pule de linha
automaticamente. Para isso use o parâmetro "-n". Este parâmetro é útil quando você vai entrar com
algo após o echo. Veja este script:
Exemplo:
#!/bin/bash
echo -n "Entre com o nome do arquivo: "
read FILE
echo "Tipo do arquivo `file $FILE`"
Muita atenção deve ser tomada ao usar o echo, pois as aspas que você pode vir a deixar de usar
podem fazer um diferença (em alguns casos isso pode ser muito útil).
Exemplo:
A esta altura você já deve ter se perguntado "Como faço para imprimir caracteres nova
linha ou beep?!". Os mais malandrinhos devem ter tentado um contra-barra (backslash) '\', mas você
não pode simplesmente fazer isso. É necessário usar o parâmetro "-e" com o echo. Este parâmetro
permite que usemos sequências de escape contra-barra.
As sequências são iguais a da linguagem C, exemplo: \n para nova linha, \a para beep,
\b para backspace, etc...
Exemplo:
O -e é também usado para escrever colorido e outras coisas interessantes. Veremos isso no tópico
seguinte.
3. Metacaracteres e Wildcards
Metacaracteres são caracteres que possuem algum significado especial para o shell.
Wildcards são subconjuntos dos metacaracteres cuja característica é simplificar a linha
de comando.
Os metacaracteres e wildcards serão vistos neste trabalho de uma maneira gradativa, a
medida em que sejam necessários.Alguns deles já foram vistos no redirecionamento (> < e >> ).
Veja mais alguuns metacaracteres:
Caracteres Definição
\ Continuação de linha
; Agrupamento de comandos
| Processamento Sequenciado
Exemplo:
$cat /home/chico/public_html/index.html \
> /home/maria/public_html/index.html
Exemplo:
$ls ; pwd
Pipes
Pipes ou tubos ( | ) é uma outra forma de agrupar comandos em uma mesma linha,
porém, há relacionamento entre os comandos.
Quand utilizamos o “|” (pipe), ele informa ao shell para conectar a saida padrão do
primeiro comando com a entrada padrão do segundo comando. Por sua vez, a saida do segundo
comando é direcionada para a saida padrão.
Para um melhor entendimento imagine que a linha de comando é um tubo por onde
deve passar a informação do primeiro comando para o segundo, do segundo para o terceiro e assim
sucessivamente, até que o último comando direcione a saida para a saida padrão.
$ programa1 | programa2
O pipe é usado para você fazer da saída de um programa a entrada de outro (como
usado no exemplo amarelinho.sh já mostrado anteriormente). Apesar de simples o pipe é muito útil e
poderoso. Veja este exemplo muito simples:
$ who
meleu tty1 Nov 20 01:40
hack tty2 Nov 20 01:45
root tty3 Nov 20 02:44
$ who | cut -c-9
meleu
hack
root
xargs
Exemplo:
Como eu gosto do find não resisti e colocarei um comando interessante que usa pipe e xargs:
tee
Outro comando bom de se usar com pipe é o "tee". Ele faz com que a saída do
programa vá para a saída padrão, normalmente a tela e para um arquivo ao mesmo tempo. É como
se você fizesse "programa > arquivo" só que o saída do programa também seria escrita na saída
padrão. Experimente:
Para usar cores a sequência de escape é "\e[<NUM>m" (os sinais '<' e '>' não entram!).
Veja um exemplo (mais a frente você verá tabelas com os significados destas sequências):
Examplo:
#!/bin/bash
# imprime amarelinho no centro da linha
# A variável $COLUMNS contém o número de colunas que o terminal está
# usando, e antes de executar este script você precisa exportá-la:
# [prompt]$ export COLUMNS
[ $COLUMNS ] || {
echo Você precisa exportar a variável \"COLUMNS\":
echo "Tente \"export COLUMNS\" e tente executar novamente"
exit 1
}
echo -e "\e[${POSICAO}C\e[33;1m$*\e[0m"
Agora uma explicação ligeira: o \e diz ao echo que o que vem depois é uma sequência
de escape. Se você der a sequência '[<num>C', onde num é um número qualquer, o cursor vai andar
"num" caraceteres para a direita. Acima eu uso a variável POSICAO para mover o cursor para o
centro da linha (veja o cálculo no código).
O comando '[<num>m' muda para a cor "num". Cada cor tem um código próprio. No
exemplo acima o 33 faz ficar marrom, porém combinando com o 1 fica amarelo (isso no modo texto,
pois no xterm, por exemplo, o 1 faz o marrom ficar em negrito. veja OBSERVAÇÕES mais
adiante). Veja também uma tabela com os códigos de movimentação de cursor que eu conheço (os
caracteres '<' e '>' devem ser ignorados):
Agora uma tabelinha dos atributos e seus números (N deve estar no formato "\e
[<N>m"):
Atributo N Cor X
Desligar todos atributos 0 Preto 0
Negrito 1 Vermelho 1
Cor X para o primeiro plano 3X Verde 2
Cor X para o segundo plano 4X Marrom 3
Sublinhado 4 Azul 4
Piscando (blink) 5 Roxo 5
Video reverso 7 Ciano 6
-x- -- Branco 7
OBSERVAÇÕES:
2. read
Como você viu no script anterior para entrar com um dado usa-se "read". O read tem
alguns "macetinhos". Pra começar: você não precisa colocar um echo toda hora que for usar o read
para escrever um prompt. Basta fazer "read -p prompt variavel"
Veja esta seção de exemplos:
Examplo:
#!/bin/bash
read -p "Entre com uma string: " string
echo $string
$ ./read1.sh
Entre com uma string: klogd eh um tremendo cachacero!
#!/bin/bash
read -p "Entre com 3 strings: " s1 s2 s3
echo "s1 = $s1 s2 = $s2 s3 = $s3"
Examplo:
$ ./read-2.sh
Entre com 3 strings: j00nix eh cabecudo
s1 = j00nix s2 = eh s3 = cabecudo
$ ./read-2.sh
Entre com 3 strings: eSc2 adora ficar de copy'n'paste no IRC.
s1 = eSc2 s2 = adora s3 = ficar de copy'n'paste no IRC.
Quando o "read" vai ler apenas uma variável, toda a string vai ser atribuída a esta
variável. Quando vai ler mais de uma variável ele atribui cada string a sua respectiva variável; e
quando o número de strings excede o número de variáveis a última fica com o excedente.
O parâmetro "-s" serve para não ecoar o que for digitado. É útil para digitar uma senha
por exemplo. Tente "read -s PASS" e veja.
Você também pode determinar o número de caracteres que serão lidos com o
parâmetro "-n". Tente fazer "read -n 10 VAR". Mas cuidado: ao usar a opção -n você não poderá
usar o backspace para fazer correções.
A opção "-r" serve para que a contra-barra (backslash) não aja como um caracter de
escape.
3. Redirecionamento
Quem já sabe programar deve saber que existem três "file descriptors" abertos por
padrão (pelo menos nos sistemas operacionais que conheço):
stdin (standard input), stdout (standard output) e stderr (standard error). Para fins práticos, estes são
considerados arquivos e você pode direcionar destes "arquivos" para outros e vice-versa.
Se você usar por exemplo "find / -perm -2 > worldwritable.txt" e no diretório não tiver
um arquivo chamado "worldwritable.txt" este será criado, a saída do comando será gravada nele e a
saída de erro padrão será impressa na tela. Para não ver as mensagens de "Permission Denied" faça
isso:
Ainda temos um probleminha: este comando irá mostrar também todos os links
simbólicos e vários arquivos de dispositivo. Para eliminar os links simbólicos faça o seguinte:
Se o arquivo já existir seu conteúdo será sobregravado. Mas você pode apenas
concatenar o conteúdo no final do arquivo usando ">>". Exemplo:
Veja este script a seguir a execute ele usando redirecionamento na linha de comando
pra ver os resultados
#!/bin/bash
echo "Isto vai para a saída padrão."
echo "Isto vai para a saída de erro padrão." 1>&2
echo "Isto vai criar um arquivo e colocar esta linha nele." > ARQUIVO
echo "Esta linha vai para o final do arquivo." >> ARQUIVO
Isto quer dizer que o programa vai ler o arquivo stdin até encontrar o delimitador. Isso é muito útil
para usar programas externos através de shell scripts.
Você pode, por exemplo, usar este tipo de redirecionamento para fazer um shell script escrever um
outro arquivo usando o comando "cat". Vamos a um exemplo em que eu crio um código de
programa escrito em C através do script (note que as variáveis do script SÃO interpretadas):
#!/bin/bash
NAME=`whoami`
echo -e "\n\tFEITO!\n"
5. Códigos de Escape
Toda vez que executamos um programa em Unix, ele retorna um código de escape ao
finalizar. Esse código reflete a condição em que o programa finalizou. Se ocorreu tudo certo e o
programa terminou normalmente, ele retorna 0. Se ocorreu algum problema, o programa retorna um
código diferente de 0, geralmente variando com o problema ocorrido.
Esse código de retorno é extremamente importante em shell script, pois é assim que
testamos se uma certa ação ocorreu bem ou teve problemas. Esse código é armazenado pelo bash
numa variável chamada "?" (isso mesmo, somente o sinal de interrogação ;-)).
Por exemplo, vamos executar um "ls" em um diretório que existe e ver o código de retorno:
virtvs@mucuripe:~$ ls /boot
System.map boot.0300 boot.b boot_message.txt chain.b config map os2_d.b
virtvs@mucuripe:~$ echo $? 0
virtvs@mucuripe:~$
Ou seja, o "ls" foi executado normalmente, retornando 0. Agora vamos executá-lo num diretório que
não existe:
virtvs@mucuripe:~$ ls /diretorio_invalido
/bin/ls: /diretorio_invalido: Arquivo ou diretório não encontrado
virtvs@mucuripe:~$ echo $?
1
virtvs@mucuripe:~$
Alguns programas tem muitos códigos de retorno. Por exemplo, os códigos de retorno
do "pppd" vão até o 19. Assim é possível saber porque ele foi finalizado. Se você colocar uma senha
errada no pppd e tentar conectar, ele vai terminar com o código 19.
man pppd
...
17 The PPP negotiation failed because serial loopback was detected.
18 The init script failed (returned a non-zero exit status).
19 We failed to authenticate ourselves to the peer.
...
Como vemos, ao terminar, ele emite uma mensagem dizendo que finalizou com código 1 ([1]+ Exit
1) mas quando testamos a variável "?", o bash nos diz "0".
6. Conclusão
Como todos os programas tem que terminar com um código de retorno que tenha
algum significado, nossos shell scripts também terão que finalizar indicando o que aconteceu, se
ocorreu tudo bem ou se ouve erro. Mas isso discutiremos melhor mais pra frente.
O importante aqui é saber que todos os programas terminam com um código de
retorno, os quais usaremos nos nossos scripts para testar o término dos programas.
7. Exercicios
O famoso "if"
O bash nos oferece, entre outros, o comando “if” (ele é um comando embutido no bash
e não um programa como o "ls", o "cp" etc.). A forma mais simples de representar uma condicional
utilizando o “if”, é da seguinte forma básica:
if [condição];
then comandos1;
else comandos2;
fi;
É assim que o if verifica uma condição. Se o seu código de retorno for zero, então ela é
considerada verdadeira. Caso contrario, ela é falsa. Mas então a [condição] tem que ser um
comando, certo? Exatamente. Vamos exemplificar:
O que fizemos ?
Logo após o if, nós colocamos um comando: "ls /boot". O que o if faz ? Ele executa
esse comando (por isso que temos a segunda linha começada por "System.map", que é o conteúdo
do meu diretório /boot) e avalia o seu código de saída. Como o "ls" foi executado corretamente, ele
retorna zero, significando verdadeiro para o if , que executa o comando logo após o "then", ou seja,
o echo "O diretório existe.", mostrando essa mensagem no console.
A lógica é a mesma. Executa o "ls /dir_invalido", que retorna um código maior que
zero. O if avalia como falso e executa o comando após o else: echo "Diretório inválido".
Nós poderíamos omitir a segunda linha dos dois exemplo (a que mostra o conteúdo de /
boot no primeiro exemplo e a mensagem de erro emitida pelo ls dizendo que /dir_invalido não existe
no segundo), apenas redirecionando as saídas para /dev/null, ou seja:
Nota: Tem um macete que possui o mesmo efeito. Em vez de colocar: "1> /dev/null 2> /dev/null"
podemos colocar "2&>1", que é menor e mais simples.
1. Comando "test"
Aprendemos que o “if” avalia a código de retorno de um comando. Muitas vezes, para
não dizer a maioria, nós queremos avaliar "expressões", ou seja, verificar se uma variável é igual a
outra, se ela esta vazia etc.
Para isso, nós temos outro comando chamado "test". Ele funciona da seguinte maneira:
test [expressão]
O test pode apenas comparar strings, ou seja, verificar se uma é igual a outra, e
verificar se uma string é vazia ou não. Vamos aos exemplos para facilitar o entendimento:
Aqui comparamos a string "a" com "b". Como era de se esperar, o primeiro retornou verdadeiro
(zero), pois a = a e o segundo retornou falso. No terceiro, o símbolo "!=" significa "diferente".
Acima temos os testes de vazio. A opção "-z" verifica se é vazio, e "-n" se não é vazio.
No primeiro caso, ele testa se "neo" é uma string vazia, retornando falso. Já no segundo caso, como
"" é vazia, retorna verdadeiro. O terceiro e quarto são semelhantes aos primeiros, mas com "-n".
Os testes que podem ser feitos com arquivos são para verificar determinadas
caracteristicas, como se ele existe, se é executavel, se é um link simbólico, se é um diretório etc.
Alguns exemplos:
A opção "-e" verifica apenas se um arquivo existe e a opção "-d" verifica se o arquivo é
um diretório.
A opção "-nt" verifica se o primeiro arquivo é mais novo que o segundo (nt = newer
than) e "-ot" verifica se o primeiro é mais velho que o segundo (od = older than):
0
virtvs@mucuripe:~$ test /usr -ot /vmlinuz
virtvs@mucuripe:~$ echo $?
1
#!/bin/bash
if [ $1 = $2 ]; then
echo As strings são iguais.
fi
$ ./strcmp1.sh meleu
./strcmp.sh: [: meleu: unary operator expected
Note que o test deu um erro, e por isso retornou um não-zero para o if. Observe o seguinte:
$ cat strcmp2.sh
#!/bin/bash
if [ $1 = $2 ]; then
echo As strings são iguais.
else
echo As strings são diferentes.
fi
$ ./strcmp2.sh meleu
./strcmp.sh: [: meleu: unary operator expected
Aconteceu a mesma coisa que no primeiro exemplo, só que agora temos um else pra ser
executado caso a expressão do if retorne falso (ou não-zero). Uma solução pra que não dê este erro
no test é usar aspas duplas. Veja só:
#!/bin/bash
if [ "$1" = "$2" ]; then
echo As strings são iguais.
else
echo As strings são diferentes.
fi
Você também NÃO deve escrever tudo junto, assim: $1=$2 ou "$1"="$2"
Desta maneira o test vai retornar sempre verdadeiro, pois seria como se você estivesse passado
somente um parâmetro para ele.
#!/bin/bash
NUM1=1
NUM2=00001
if [ "$NUM1" = "$NUM2" ];
then
echo "O valor string de $NUM1 e $NUM2 são iguais."
else
echo "O valor string de $NUM1 e $NUM2 são diferentes."
fi
$ echo $[ 2 * 3 + 4 ]
10
$ echo $[ 2 * ( 3 + 4 ) ]
14
Basta colocar a expressão entre $[ ... ]. Você também pode usar $((cifrão-dois-
parênteses)) mas os colchetes são bem mais práticos.
Se você pretende usar mais coisa relacionada a matemática, então aprenda a usar o
comando "bc". Mas se vai ficar só no "papai-mamãe" das operações básicas pode ficar usando $(( ))
ou $[ ]
(OBS.: em operações de divisão os resultados não são muito satisfatórios).
Para usar os operadores lógicos basta usar "-a" para AND e "-o" para OR. Muito
simples. O exemplo a seguir usa o AND e também usa um novo tipo de comando de controle, o
"elif", que é a mesma coisa que "else if", só que se você usar "else if" vai precisar de um "fi" para
fechar. O elif não precisa disso.
Preste atenção que é fácil de entender para quem tem noção de algoritmo:
#!/bin/bash
read -p "Entre com o primeiro número: " NUM1
read -p "Entre com o segundo número: " NUM2
read -p "Entre com o terceiro número: " NUM3
MAIOR=$NUM3
else
MEIO=$NUM3
MAIOR=$NUM2
fi
elif [ $NUM2 -le $NUM3 ]; then
MENOR=$NUM2
if [ $NUM1 -le $NUM3 ]; then
MEIO=$NUM1
MAIOR=$NUM3
else
MEIO=$NUM3
MAIOR=$NUM1
fi
else
MENOR=$NUM3
if [ $NUM1 -le $NUM2 ]; then
MEIO=$NUM1
MAIOR=$NUM2
else
MEIO=$NUM2
MAIOR=$NUM1
fi
fi
echo "$MENOR < $MEIO < $MAIOR"
5. O comando let
O let é um comando embutido no bash (e isto quer dizer que se você quiser
informações sobre ele tem que ver na manpage do bash, mais especificamente no tópico
ARITHMETIC EVALUATION). Ele é bastante útil para quem está acostumado com programação
C, pois sua sintaxe é semelhante, mas só é usado para expressões aritméticas.
Com este comando você pode comparar valores numéricos com os sinais <, >, <=, >=,
==, e !=. E mais bastante coisa comum em linguagem C (como por exemplo o ++ e o --). Alguns
exemplos do que você pode fazer:
(( var++ ))
(( var-- ))
(( $num > 2 ))
Exemplo:
#!/bin/bash
if (( $# != 2 )) ; then # poderia ser: if let "$# != 2"
echo "Uso: `basename $0` N1 N2" 1>&2
exit 1
fi
if (( $1 > $2 )) ; then # poderia ser: if let "$1 > $2"
echo "$1 é maior que $2"
elif (( $1 == $2 )) ; then # poderia ser: elif let "$1 == $2"
echo "$1 é igual a $2"
else
echo "$1 é menor que $2"
fi
Esta maneira de tomar decisões é bem compacta, mas não aceita "else". Eu,
particularmente, prefiro usar esta estrutura quando vou fazer uma tomada de decisão e não preciso
de "else". A maneira de usar é:
Como você deve estar lembrado, "[ -d ~/tempdir ]" é o mesmo que "test -d ~/tempdir"
e retornará 0 se existir o diretório ~/tempdir. Caso não exista ele retornará 1, e neste caso o "mkdir
~/tempdir" será executado.
7. Listas
As listas de comandos servem para agrupar comandos. Podem ser representadas por
(parenteses) ou {chaves}. A diferença é que os (parenteses) executam os comandos num shell a
parte e as {chaves} executam no shell atual.
Execute comando a seguir e tente entendê-lo (está certo... são vários comandos, mas
inicialmente é encarado com um comando só).
Exemplo:
[ -d /usr/doc ] && {
echo "O diretorio existe"
echo "veja o seu conteudo"
cd /usr/doc
ls
}
E observe que ao final da execução você estará no diretório /usr/doc, o que comprova
que com as {chaves} os comandos são executados no shell atual, se você trocar as {chaves} por
(parênteses) observará que o seu diretório não se alterará.
2. Expressões Aritméticas
Infelizmente não podemos apenas utilizar os símbolos conhecidos para igual (=), maior
que (>), menor que (<) etc. Temos que usar operadores reconhecidos pelo "test". Assim, não
podemos fazer: "test 1 = 1", devemos utilizar o operador "-eq" (equal): "test 1 -eq
1". A seguir, temos uma lista dos operadores:
Para finalizar, vamos fazer duas considerações. A primeira é de que o comando "test" pode ser
substituido por um par de colchetes [ ]. Assim, o comando test "a" = "b" pode ser escrito como [ "a"
= "b" ] .
Essa segunda nomenclatura fica mais fácil e simples, principalmente quando estamos utilizando IF:
É importante colocarmos os valores entre aspas duplas ("), pois assim o bash pode
substituir o que se encontra dentro dessas aspas. Se colocarmos entre aspas simples ('), impedimos o
bash de alterar o que se encontra dentro delas. Se não utilizarmos as aspas duplas, vamos ter
problemas, principalmente ao trabalharmos com string.
Agora, se não usarmos aspas: [ $nome1 = $nome2 ]. O bash irá expandir isso para:
[ fulano de tal = ciclano ]. Perceba que os argumentos se misturam e o bash não vai saber o que
comparar. Por isso é importante colocarmos os argumentos entre aspas duplas.
3. Conclusão
Resumindo, aprendemos na aula de hoje como utilizar comandos condicionais. Isso será
fundamental nos próximos capitulos, onde iremos tratar os comandos de laço, como o while.
Vamos aprender agora sobre comandos de laço como o while, for, case e select. Eles
nos permitem executar alguns comandos diversas vezes, sob determinadas condições e até montar
menuzinhos para interagir com o usuário.
1. while
while [ condição ];
do
comando 1;
comando 2;
...
done;
x=0
while [ "$x" -le 10 ];
do
echo "Execução número: $x"?;
x = $((x+1));
done;
Analisando:
Preste atenção, que as linhas do while precisam de um ";" para terminar, exceto a que
contém o "do", pois ele representa o começo do bloco de comandos.
O while tem muita utilidade em programação, mas fica difícil dar exemplos usando
somente ele, pois geralmente ele está associado a execução de outros programas e rotinas mais
complexas.
Eu costumo usar ele, por exemplo, quando quero que o napster fique tentando
conectar, pois quando ele vai tentar conectar o servidor e não consegue, ele simplesmente termina.
Aí eu coloco o while testando a código de retorno dele (lembram da variável $? ) e enquanto estiver
diferente de zero (o napster terminou com erro), ele fica executando o napster:
O comando "? = 1" tenta atribuir 1 a variável ?. Isso provoca um erro, pois a variável ?
É somente de leitura. Isso é necessário para que ? Contenha algo diferente de 0, pois senão o while
não executa a primeira interação.
2. for
O for é semelhante ao while usado como um contador. É necessário fornecer uma lista
de nomes e ele executa os comandos para cada nome na lista. Parece meio confuso, mas é simples.
Veja a sintaxe:
Ou seja, primeiro ele coloca "Compra" na variável x e executa os comandos, no caso, "echo $x", e
assim por diante. Podemos facilmente implementar um contador, usando em conjunto com o for, o
programinha "seq". Ele simplesmente gera uma seqüência de números. Por exemplo, "seq 10" gera
uma seqüência de 1 até 10. Assim, podemos usá-lo no for:
for x in *; do
mv "$x" "${x}.bak";
done;
for x in *.mp3; do
if [ -e "$x" ];
then mv "$x" "${x}.bak";
fi;
done;
No local de <lista> nos colocamos "*.mp3". Isso diz ao bash para expandir
(transformar) ele na lista de arquivos terminados com ".mp3". Senão houver nenhum arquivo com
essa terminação, o bash não faz nada, ficando para o for a string "*.mp3". Por isso precisamos do if
para testar se o arquivo existe. Experimente no seu sistema. echo *.mp3 vai mostrar os arquivos no
diretório atual com terminação mp3, mas sem quebra de linha entre eles, ou seja, um nome após o
outro. Se não houver nenhum arquivo com terminação mp3, ele apenas vai mostrar "*.mp3".
Bem, voltando ao for, ele vai atribuir a "x" cada nome na lista de arquivos com
terminação ".mp3" e executar os comandos seguintes. Primeiro ele testa para ver se o arquivo existe
( if [ -e "$x" ]; ), e se existir, renomeia ele para o seu próprio nome acrescido de ".bak" (
${x}.bak ).
Resumindo, o for faz isso: você fornece uma lista de nomes para ele e ele vai atribuindo
esses nomes, em ordem e um por vez, à variável <var> e executa os comandos entre o "do" e o
"done;".
Agora para usarmos um for um pouco parecido com o das linguagens de programação
convencionais usamos um comandinho chamado "seq". Ele funciona basicamente da seguinte forma:
$ seq 1 10
1
2
3
4
5
6
7
8
9
10
$ seq 10 1
10
9
8
7
6
5
4
3
2
1
Examplo:
#!/bin/bash
if [ $# -ne 1 ]; then
echo "Uso: `basename $0` n"
echo "Onde 'n' é um número inteiro qualquer"
exit 1
fi
echo
3. until
Leia direitinho: comandos que serão executados enquanto <expressão> retornar FALSO! ;-)
Exemplo:
#!/bin/bash
CONT=10
until [ $CONT -eq 0 ]; do
echo -ne "$CONT\t"
let CONT--
done
echo
4. case
O case está mais para um comando condicional do que para comando de laço, visto que
ele não executa "loopings" como o while e o for. Ele geralmente é utilizado como substituição de
vários IFs.. Um exemplo clássico e muito utilizado é quando você precisa testar um parâmetro
fornecido na linha de comando. O case é utilizado desta forma em scripts de inicialização de serviços
do sistema. Vou mostrar a sintaxe em em seguida um script que inicialize/ reinicialize ou pare algum
serviço (como o sendmail, apache, bind, etc).
Sintaxe:
case <parâmetro> in
<opção 1>)
<comandos 1>
;;
[opção 2] )
<comandos 2>
;;
*)
< comandos se não for nenhuma das opções >
;;
esac
case "$1" in
'start' )
echo "Iniciando o serviço..."
<comandos para iniciar o serviço>
;;
'restart' )
echo "Reinicializando o serviço..."
<comandos para reinicializar o serviço>
;;
'stop' )
echo "Parando o serviço..."
<comandos para parar o serviço>
;;
*)
echo "Opção invalida!"
echo "As opções válidas são: start, stop e restart"
;;
esac
O case serve exatamente para isso, ou seja, evitar o teste de vários Ifs. No caso acima,
teríamos que utilizar 3 Ifs. Mesmo se o primeiro já fosse verdadeiro, o bash iria testar o segundo e o
terceiro, ou seja, ia perder tempo desnecessariamente. Já no case isso não acontece. Após entrar em
uma opção e executar seus comandos, ele já pula fora do case sem testar as outras opções abaixo.
5. break e continue
break
Examplo:
#!/bin/bash
# `true` sempre retorna verdadeiro
while true; do
read -p "Tente adivinhar o número: " NUM
[ "$NUM" -eq 666 ] && break
done
O que o break faz é pular a sequência de execução do script para o que vier depois do "done". Ele
também aceita argumento, da seguinte forma: "break n", onde 'n' é um número inteiro que indica
quantos "done's" pular.
continue
O continue interrompe o loop e faz novamente o teste do comando que está controlando o loop (for,
while, etc.). Veja este exemplo:
Examplo:
#!/bin/bash
done
echo "Feito!"
O que o continue faz é voltar lá para antes do "do" pra fazer o teste novamente. Ele
também aceita argumento no estilo "continue n" para indicar quantos "do's" voltar.
6. Redirecionando loops
Lendo o livro "Linux: Programação Shell" (ver Referências) eu aprendi umas formas de
usar redirecionamento para loops. Vou tentar passar os esquemas aqui através de exemplos
meramente ilustrativos. Vamos a eles, lembrando daqueles conceitos passados no tópico 3.
(principalmente sobre o read e sobre os redirecionamentos).
Vamos imaginar um arquivo onde eu tenho os nomes de alguns amigos meus e a região
onde eles moram. Como alguns amigos moram em uma mesma região eu não vou ficar repetindo a
região para cada um. Portanto o arquivo fica assim:
#############################
# lista de amigos e estados #
#############################
#
# OBS.: ISSO NÃO É UM SCRIPT!
#
# Linhas que COMEÇAM com '#' serão consideradas
# comentários pelo script "listamigos.sh".
#
# Use da seguinte forma:
# REGIAO amigo1 amigo2 amigo3 amigoN ...
#
#!/bin/bash
Mas a coisa não é tão simples assim... Se dentro do loop você quisesse usar o comando
read para ler do teclado, seria necessário pegar a entrada de "/dev/tty". Sabendo que o /dev/tty é o
terminal que você está usando. Se você tiver muitos amigos no arquivo "amigos.regiao" não vai
conseguir ver todos, pois a lista não caberá numa tela só. Neste caso, o script a seguir será melhor
que o "listamigos.sh".
#!/bin/bash
Vejamos um exemplo onde você verá que o exit funciona como o break:
#!/bin/bash
#
##################################################################
# ***********
# * ATENÇÃO *
# ***********
# Não use este script para atacar servidores remotos! Ele deixará
# arquivos de log imensos! Use-o apenas em localhost (127.0.0.1)
# e veja você mesmo os rastros deixados nos arquivos de log.
##################################################################
#
WL="$1"
cat $WL |
while read PASS
do
# 230 é o número que recebemos quando entramos com sucesso via ftp
ftp -ivn << EoF | grep "^230" &>/dev/null
open $HOST
user $USER $PASS
bye
EoF
done
#######################
# Agenda de telefones #
#######################
#
# OBS. I: ISSO NÃO É UM SCRIPT!
#
# Linhas que COMEÇAM com '#' serão consideradas
# comentários pelo script "listartel.sh".
#
# Use da seguinte forma:
# NOME PREFIXO TELEFONE
# Com os campos separados por UM ÚNICO <TAB>.
#
# Exemplo:
# lampiao 12 12345678
# mariabonita 87 87654321
#!/bin/bash
#arquivo=listartel.sh
#
TempFile=/tmp/TEMP-$$
rm $TempFile
Consulta o arquivo
OBS. I: Neste esquema também é necessário pegar os dados de /dev/tty se você quiser usar o read
dentro do loop.
OBS. II: Se você usar exit dentro do loop usando este esquema, ele REALMENTE SAIRÁ DO
SCRIPT. Não é igual ao esquema anterior onde o while recebe dados de um pipe e o exit funciona
como se fosse um break. Então repetindo: neste esquema o exit funciona normalmente!
Ficou confuso com este título? "Redirecionar a saída do loop para a tela parece ser uma
coisa inútil, pois isso acontece todas as vezes." Aí que você se engana! Vamos ao exemplo onde eu
mostrarei a utilidade de se redirecionar desta maneira...
Temos um script chamado retornatel.sh que pesquisa o telefone de um determinado
amigo (o nome é passado ao script durante sua execução). Agora queremos pegar o telefone deste
amigo e armazená-lo numa variável da seguinte maneira:
FONE=`./retornatel.sh`
Só que, como veremos no script a seguir, a saída do script não é somente o número do telefone.
Existe uma interface com o usuário perguntando qual o nome a ser pesquisado. Veja o script:
Exemplo:
#!/bin/bash
# tá bom, tá bom... eu sei que não é um exemplo muito útil...
FILE=agenda.tel
function gotoxy {
[ $# -ne 2 ] && {
echo gotoxy: Erro na passagem de parâmetros
echo Uso: gotoxy X Y
exit 1
}
echo -ne "\e[$1;$2H"
}
while true; do
clear
gotoxy 5 1
read -p "Nome a ser pesquisado ('sair' para sair): " NOME
[ "X$NOME" = Xsair ] && exit
if grep "$NOME" $FILE &>/dev/null ; then
break
else
gotoxy 10 15
echo Nenhum $NOME foi encontrado em $FILE.
read -p "Pressione <ENTER> para continuar..."
fi
done > /dev/tty
variavel=`programa`
XFTEL=`./retornatel.sh`
echo $XFTEL
7. select
O select é um comando de laço que nos permite mostrar um pequeno menu de opções
para o usuário. Cada opção possui um número e para escolher, o usuário digita o número
correspondente a opção desejada. Vejamos a sintaxe a seguir:
Como disse acima, o select vai mostrar as opções contidas em <lista de opções>, uma
por linha, com um número na frente e espera que o usuário digite a opção desejada. Ao digitar a
opção, o select atribui o nome da opção a variável <var> e executa os comandos. Para sair do
select, é necessário executar o comando "break". Vamos a um exemplo:
Ou seja, se o usuário escolher alguma opção diferente de "Sair", o script apenas escreve
a opção. Se for escolhida Sair, ele mostra a opção, entra no IF e executa o break, saindo do select. É
interessante combinar o Select com o Case. Vamos mostrar como ficaria aquele exemplo do case, de
iniciar um serviço, utilizando o select, para tornar o script interativo:
done;
1) Iniciar
2) Reiniciar
3) Parar
4) Sair
#?
Quando o usuário digitar um número de opção, o select executa o case e este compara
a variável x com suas opções. Se achar alguma, executa seus comandos, no caso um echo. Se o
usuário escolher Sair, além do echo, ele executa o "break", que interrompe o select:
virtvs@mucuripe:~/test$ ./t
1) Iniciar
2) Reiniciar
3) Parar
4) Sair
#? 1
Iniciando o serviço...
1) Iniciar
2) Reiniciar
3) Parar
4) Sair
#? 5
Opção inválida!
1) Iniciar
2) Reiniciar
3) Parar
4) Sair
#? 4
Script encerrado.
virtvs@mucuripe:~/test$
Espero que vocês tenham pego a idéia de como funciona comandos de laços. Pelos
menos sabendo a utilidade, sintaxe e como funcionam, quando surgir a necessidade, vocês já vão
saber o que usar.
#!/bin/bash
function Mecho {
echo -e "\e[1m$*\e[m"
}
8. Funções
Funções são interessantes para quando você precisa fazer uma determinada coisa várias
vezes num mesmo script. Uma função é como se fosse um script dentro do script, sendo assim ele
também usa o mesmo esquema de parâmetros de um script comum ($1 para primeiro parâmetro, $2
para segunda, e todas aquelas regras explicadas em 2.4. Parâmetros com a exceção do $0, que não
retorna o nome da função!). Uma função também é capaz de retornar um valor usando o "return
ValorDeRetorno", sendo que ValorDeRetorno ficará armazenado em "$?", vale observar também
que só é possível retornar valores inteiros.
Por exemplo, se na função tiver "return 123" depois da execução desta instrução "$?"
valerá 123.
#!/bin/bash
# "Hello World" usado para ilustrar o uso de funções.
function quit {
echo -e "\e[1;32mTCHAU!\e[m"
exit
}
function e {
echo -e "\e[1;35m$1\e[m"
}
e Hello
e World
quit
echo "Isso aqui não será impresso"
Outro exemplo:
#!/bin/bash
return $1
else
return $2
fi
}
exit 0
Informações adicionais:
- O return quando executado interrompe a execução da função e a execução passa para a instrução
imediatamente posterior a qual a função foi chamada;
- O valor mais alto que uma função pode retornar é 256 e o mais baixo 0. Bem, é um número de
módulo grande. :P Se precisar de valores positivos maiores que 256 retorne-o como negativo e
depois converta-o para positivo novamente.
- Se quer saber mais detalhes sobre funções o capítulo 23 do Advanced Bash-Scripting Guide vai
lhe ser muito útil.
#!/bin/bash
#
# Código baseado em um do Advanced Bash-Scripting Guide.
# Mais informações sobre este guia em http://www.linuxdoc.org
# Caso não entenda o código, faça um esforcinho! ;-)
LIMITE=400
function romano {
NUM=$1
FATOR=$2
ROMAN=$3
(( NUM -= $FATOR ))
(( RESTO = $NUM - $FATOR ))
done
return $NUM
}
[ "$1" ] || {
echo "Uso: `basename $0` NUMERO"
exit 1
}
NUMERO=$1
romano $NUMERO 90 XC
NUMERO=$?
romano $NUMERO 50 L
NUMERO=$?
romano $NUMERO 40 XL
NUMERO=$?
romano $NUMERO 10 X
NUMERO=$?
romano $NUMERO 9 IX
NUMERO=$?
romano $NUMERO 5 V
NUMERO=$?
romano $NUMERO 4 IV
NUMERO=$?
romano $NUMERO 1 I
NUMERO=$?
echo
exit
function heko {
echo -e "\e[5;1;32mHekodangews, para de enrolar a mina e casa logo! \e[m"
}
Agora quando você entrar com "heko" na linha de comando será impresso na tela um
recadinho para o Hekodangews numa corzinha verde e piscante super-fashion. =)
function SetPath {
PATH=${PATH:="/bin:/usr/bin"}
for DIR in "$@"; do
if [ -d "$DIR" ]; then
PATH="$PATH:$DIR"
else
echo "* * * Erro: $DIR nao eh um diretorio"
fi
done
export PATH
unset DIR
}
E se você tiver várias idéias de funções legais que queira usar sempre em suas sessões?
Vai ter que digitá-las na linha de comando toda hora? Não! Para este propósito o comando "source"
pode ser muito útil (informações detalhadas na manpage do bash).
Lembra-se quando disse "Quando executamos um shellscript ele é executado num shell
a parte (shell filho)" ?. O comando source faz com que o script seja executado no shell pai, ou seja, é
como você estivesse digitando todo o conteúdo do arquivo na linha de comando. E isso é
especialmente útil quando temos arquivos com as nossas funções que queremos usar como
comandos.
Uma coisa legal de se fazer é colocar o arquivo com as funções que você quer usar num
arquivo oculto no seu $HOME (ex.: $HOME/.MyFunctions) e no seu $HOME/.bash_profile você
coloca uma linha com o comando "source $HOME/.MyFunctions".
#!/bin/bash
#
# "INSTALAÇÃO":
# copie este arquivo para seu $HOME:
# [prompt]$ cp Mfunctions ~/.Mfunctions
#
# depois faça o seguinte:
# [prompt]$ echo ". ~/.Mfunctions" >> ~/.bash_profile
#
# Dê login novamente ou digite ". ~/.Mfunctions" e pronto.
# Agora é só digitar o nome da função.
#
# DICA: depois de corretamente instalada, use "M<tab><tab>"
# para ver as funções disponíveis.
#
# Funções disponíveis neste arquivo:
# + Mecho
# + Mcenter
# + Mclock
# + Mclock2
# + Msetpath
# + Mdica
# + Marrumanome
# + Mnocomments
# + Mcalcula
# + Mcores
#
# Aproveite!
# meleu
#
# P.S.: graças a esse tal de "open source" você pode ler
# um código e alterá-lo para que se adeque as suas
# necessidades eu que fique ao seu gosto. Pois foi
# isso que eu fiz aqui! Saí olhando as funções que
# outras pessoas fizeram e arrumei do meu jeito. E
# a minha principal fonte foi o funcoeszz do aurélio.
# veja em: http://verde666.org/zz
#
# imprime em negrito
function Mecho {
echo -e "\e[1m$*\e[m"
}
Mecho "\e[${POS}C$*"
}
PROMPT_COMMAND="
JOBS=\$(echo \$(jobs | wc -l))
if [ \$JOBS -ne 0 ]; then
HEADER=\"Jobs: \$JOBS - \$(DiaMesAno)\"
else
HEADER=\"\$(DiaMesAno)\"
fi
POS=\$[ (\$COLUMNS - \$(echo \"\$HEADER\" | wc -c) ) - 3 ]"
PS1="\[\e[s\e[1;0H\e[K\
\e[\$(echo -n \$POS)C\
$CIANO[$AMARELO \$HEADER $CIANO]$SCOR\
\e[u\e[1A\]
$PS1"
# Parecido com o Mclock mas fica tudo escrito no centro da primeira linha
# e em um formato mais longo.
function Mclock2 {
local CIANO="\e[1;36m"
local AMARELO="\e[1;33m"
local SCOR="\e[0m"
PROMPT_COMMAND="
JOBS=\$(echo \$(jobs | wc -l))
if [ \$JOBS -ne 0 ]; then
PS1="\[\e[s\e[1;0H\e[K\
\e[\$(echo -n \$POS)C\
$CIANO[$AMARELO \$HEADER $CIANO]$SCOR\
\e[u\e[1A\]
$PS1"
ls $DICASDIR
return
}
more $DICASDIR/$1
9. getopts
Este comando serve para permitir que você use parâmetros na linha de comando de
maneira mais eficiente que ficar usando as variáveis de posição de parâmetros ("$1", "$2"...).
Sua sintaxe é:
Você poderá usar os parâmetros 'a', 'b' ou 'c'. Note que 'b' é seguido de um ':', isso significa que 'b' é
um parâmetro que precisa de argumento. Este, por sua vez, é armazenado na variável OPTARG.
Mais detalhadamente, o getopts funciona assim (adaptado do "Teach Yourself Shell Programming in
24 Hours", veja Referências):
1. O getopts examina todos os argumentos da linha de comando procurando por argumentos que
comecem com o caractere '-'.
2. Quando acha um argumento começado com '-' o getopts procura em "CadeiaDeOpcoes" se tem
algum caractere que combine.
3. Se combinar com algum, "variavel" receberá este caractere, caso contrário "variavel" receberá
'?'.
4. Os passos 1-3 são repetidos até que todos os argumentos da linha de comando tenham sido
lidos.
5. Quando acaba de ler todos os argumentos da linha de comando getops retorna um valor falso
(não-zero). Isto é útil para verificações em loops (como veremos no exemplo abaixo).
<++> BashScript/getopts.sh
#!/bin/bash
h) echo -n "
Script de exemplo de uso do \"getopts\".
n) NOME=$OPTARG ;;
e) EMAIL=$OPTARG ;;
10.dialog
10.dialog
O dialog não um comando específico do bash! Estou escrevendo sobre ele porque já vi
muita gente querendo informações.
As opções de título são opcionais (por isso que elas estão entre colchetes. As de caixa
são obrigatórias. Para que você possa fazer agora uma apreciação visual do dialog tente o seguinte
comando:
Existem vários tipos de caixas, a utilizada nesse comando é uma caixa do tipo "yesno".
Bem... isso foi só pra você dar uma admirada rápida no dialog. Agora vamos organizar as coisas,
vou explicar alguns parâmetros. Vá praticando cada um desses parâmetros e vendo o resultado.
Lembre-se que as opções da caixa são obrigatórias!
* --backtitle "texto"
Este parâmetro é para por um título "lá atrás". Tente dialog --backtitle "Micro\$oft
Scandisk" --yesno bla 0 0 e veja como o título que aparece lhe trará péssimas lembranças. =)
* --title "texto"
Agora vamos as opções de caixa, mas antes uma informação: Em todos os tipos de
caixas existem os argumentos "altura" e "largura" (que para abreviar, eu chamarei de "alt" e "larg"),
que (por incrível que pareça) servem para você determinar a altura e largura da caixa de diálogo. Se
você não tiver saco pra ficar contando quantos caracteres serão usados na caixa, use 0 para altura e
largura, assim a caixa terá um tamanho de acordo com texto utilizado.
Faz uma caixa de diálogo com as opções "Yes" e "No". Se você escolher
"Yes" o dialog retorna 0, caso contrário, retorna 1.
Serve para mostrar uma mensagem e tem um botão de confirmação "OK". Quando você escolher
"OK" o dialog retornará 0, e, assim como em todos os
outros tipos de caixas, você pode cancelar via tecla ESC, quando o dialog
retornará 255.
Faz uma caixa de entrada de dados, se "string de inicio" for passada o campo de
entrada de dados será inicializado por esta string. Os botões são "OK" E "Cancel", o primeiro, se
selecionado, retorna 0 e o outro retorna 1. A string que você entrar será direcionada para a saída de
erro padrão, portanto, se você quer que isto seja gravado use redirecionamento de stderr (ver 3.3
Redirecionamento).
* --menu "texto" alt larg alt-do-menu item1 "descricao do item1" \ [ itemN "descricao do itemN" ]
Note que aquela '\' lá no final da linha significa que o comando continua na próxima
linha. Como o próprio nome diz, esse parâmetro é usado para fazer menus, como por exemplo
aquele do pppsetup (slackware) onde você escolhe qual o ttyS do seu modem. O item que for
escolhido será impresso em stderr. "OK" retorna 0 e "Cancel" retorna 1.
Veja um exemplo besta só pra ilustrar:
#!/bin/bash
FILE=/tmp/script-$$
RET_VAL=$?
[ $RET_VAL -eq 0 ] || { echo "Operação cancelada."; exit 1; }
sh $FILE
rm $FILE
Note novamente o caractere '\' fazendo o comando continuar na linha abaixo. Este tipo
de caixa é utilizado quando se quer fazer um menu onde pode-se escolher vários itens, você marca
os que deseja e dá "OK". Assim como o "--menu", o(s) item(ns) que for(em) escolhido(s) serão
impressos em stderr. O parâmetro "status" serve para você deixar um determinado item selecionado
logo de início, seus valores podem ser "on" ou "off". Veja este exemplo bem interessante (oh!
finalmente um exemplo com alguma utilidade!) que usa muitos dos conceitos já ensinados até aqui,
se não entender releia o código:
Exemplo:
#!/bin/bash
#
# "Escolhedor" de mp3z feito para ilustrar o uso do 'dialog'
# no texto "Programação em Bourne-Again Shell".
#
# Para utilizar este script é necessário ter instalado o mpg123.
#
# + OBS.: Se o nome de alguma mp3 for muito grande podem acontecer
# resultados bizarros... :-/
# + OBS.II: Este script não "detecta" nome de mp3z que contenham
# espaços. Pois se detectasse também ocorreriam resultados bizarros.
#
# Feito por: meleu <[email protected]>
# mude a variável MP3DIR e descomente-a para não
# precisar passar o diretório toda hora na linha
# de comando.
#MP3DIR="$HOME/mp3z/"
FILE="/tmp/mp3.$$"
NCOR="\e[m"
WHITE="\e[1m"
function AjudarSair {
echo "Tente \"`basename $0` -h\" para ajuda."
exit $1
}
function ApagarSair {
rm $FILE
exit $1
}
if [ -z "$MP3DIR" ]; then
echo -e "${WHITE}Você precisa indicar em qual diretório estão as mp3z.$NCOR"
AjudarSair 1
elif [ ! -d "$MP3DIR" ]; then
echo -e "$WHITE\"$MP3DIR\" não é um diretório.$NCOR"
AjudarSair 1
fi
cd "$MP3DIR"
CONT=1
RET_VAL=$?
[ $RET_VAL -ne 0 ] && { echo "Tchau!"; ApagarSair; }
RET_VAL=$?
if [ $RET_VAL -eq 0 ]; then
kill $MPGPID 2>/dev/null || {
echo -e "${WHITE}Não foi possível finalizar o mpg123.$NCOR"
echo "Pode ser que outro usuário esteja utilizando-o."
ApagarSair 1
}
else
echo "Saindo..."
ApagarSair
fi
}
ApagarSair
# EoF #
Similar ao --checklist porém aqui só se pode fazer uma escolha, quando você seleciona
um item desmarca outro.
1. cut
Faríamos o seguinte:
root:x:0:0::/root:/bin/bash
bin:x:1:1:bin:/bin:
daemon:x:2:2:daemon:/sbin:
adm:x:3:4:adm:/var/log:
lp:x:4:7:lp:/var/spool/lpd:
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/:
news:x:9:13:news:/usr/lib/news:
uucp:x:10:14:uucp:/var/spool/uucppublic:
operator:x:11:0:operator:/root:/bin/bash
games:x:12:100:games:/usr/games:
ftp:x:14:50::/home/ftp:
mysql:x:27:27:MySQL:/var/lib/mysql:/bin/bash
gdm:x:42:42:GDM:/var/state/gdm:/bin/bash
nobody:x:99:99:nobody:/:
c0mb4t:x:1000:100:c0mb4t,,,:/home/c0mb4t:/bin/bash
Mas vimos aqui muita sujeira, então juntamos dois comando e veremos sua saída:
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
news
uucp
operator
games
ftp
mysql
gdm
nobody
c0mb4t
Aqui o comando cut corta o ':' (-d: -f1) onde o 'f' vem do inglês field. Assim deixa
somente a lista de usuários. Mas ainda não estamos satisfeitos, pois queremos em ordem alfabética,
então usaremos junto mais um comando, o sort.
adm
bin
c0mb4t
daemon
ftp
games
gdm
halt
lp
mail
mysql
news
nobody
operator
root
shutdown
sync
uucp
Mas como sou chato, vou querer em ordem inversa, logo acrescentamos no sort o -r.
uucp
sync
shutdown
root
operator
nobody
news
mysql
mail
lp
halt
gdm
games
ftp
daemon
c0mb4t
bin
adm
Notamos então, que o comando cat joga sua saída para o comando cut, que por sua vez
joga a saída para o comando sort que finalmente joga a saída na tela. Poderíamos ainda colocar o |
more, caso a tela ficasse pequena, ficando:
Agora, vamos pegar essa saída, e jogá-las em letra maiúsculas, para melhor visualização
e para melhor aprendizado, certo? Neste caso usamos o comando tr.
root@c0mb4t:/# cat etc/passwd |cut -d: -f1| sort -r| tr a-z A-Z
UUCP
SYNC
SHUTDOWN
ROOT
OPERATOR
NOBODY
NEWS
MYSQL
MAIL
LP
HALT
GDM
GAMES
FTP
DAEMON
C0MB4T
BIN
ADM
O comando tr, substituí as letras de 'a' a 'z' por 'A' até 'Z'.
Pronto, aqui está a lista de usuários em ordem alfabética inversa, em letras maiúsculas.
Mas que tal jogarmos a sua saída num arquivo chamado usuários?
root@c0mb4t:/# cat etc/passwd |cut -d: -f1| sort -r| tr a-z A-Z > /usuarios
O comando > grava em arquivo. assim o > /usuarios grava o saida, que no lugar de
jogar na tela, ela joga para um arquivo que chamei de /usuarios.
Para visualizar o arquivo, digitamos conforme já visto:
UUCP
SYNC
SHUTDOWN
ROOT
OPERATOR
NOBODY
NEWS
MYSQL
MAIL
LP
HALT
GDM
GAMES
FTP
DAEMON
C0MB4T
BIN
ADM
grep. Esse comando pega a saída de um comando e imprime ta tela apenas a linha o qual contém o
conteúdo desejado.
2. wc
As opções podem ser utilizadas em conjunto. A ordem das opções vai determinar a ordem de
apresentação da saída do comando
3. tee
4. sort
00003Maria 20000015
00005Antonio 20000009
00002Pedro 20000002
00004Carlos 20000002
00001Zelia 20000011
5. grep
Agora teremos um breve tutorial sobre o Grep. Ele não é somente um dos comandos
mais úteis, mas o seu domínio abre portas para dominar outros poderosos comandos, como o sed
(que trataremos na próxima aula) , awk, perl, etc. Eu fiz uma adaptação/ modificação de um tutorial
que eu tenho, e por sinal é excelente, sobre o Grep e Expressões Regulares em
inglês. Espero que tenha ficado legal e vocês gostem.
Lista todos os arquivos que contenham palavra em seu nome. Ou seja, a entrada do grep é uma lista
de arquivos (gerada pelo ls) que será filtrada, sendo impressas somente as linhas que contenham
palavra.
Suponho que todos saibam o que são caracteres coringas. Caso contrário, coringas são
caracteres especiais que substituem outros. Geralmente o caracter "*" é um coringa que significa
"qualquer caracter em qualquer quantidade". Por isso se a gente executar "ls *", onde "*" entra no
lugar do "nome do arquivo", significando qualquer string de qualquer tamanho. Por isso ele lista
todos os arquivos.
Mas agora voltemos ao grep. Será que ele aceita coringas ? A resposta é mais do que
sim. O grep suporta algo que vai além de coringas, ele suporta Expressões Regulares. Mas vamos
começar apenas com coringas. Um dos mais usados com o grep é o "."
Vamos a um exemplo:
>cat file
>grep b.g
file
big
big
bad bug
bad bug
bigger
bigger
boogy
Note que boogy não casa, desde que "." significa "qualquer e apenas um caracter". Para
significar strings arbitrárias utilizamos "*", que funciona da seguinte maneira:
"A expressão consistindo de um caracter seguido por um * casa com qualquer número (inclusive
zero) de repetições desse caracter. Em particular, ".*" significa qualquer string."
Para compreendermos vamos a mais exemplos:
>cat file
big
bad bug
bag
bigger
boogy>grep b.*g file
big
bad bug
bag
bigger
boogy>grep b.*g. File
bigger
boogy>grep ggg* file
bigger
Os coringas são o começo, mas a idéia vai mais longe. Por exemplo, suponha que
queremos uma expressão que case com Frederic Smith ou Fred Smith, ou seja, a string "eric" é
opcional. Primeiro, introduzimos o conceito de um "caracter escapado (escaped character)".
"Um caracter escapado é um caracter precedido por uma barra invertida ( \ ). Essa barra invertida
faz o seguinte: (a) Remove qualquer significado especial do caracter. (b) acrescenta um significado
especial a um caracter que não tenha um significado especial."
Parece complicado, mas veja nos exemplo que é um tanto quanto simples:
Para procurar uma linha contento o texto "hello.gif", o comando correto seria:
Desde que "grep 'hello.gif' file" iria resultar linhas contendo: hello -gif , hello1gif ,
helloagif , etc. Ou seja, a barra invertida remove o significado especial do ".", passando esse a
significar um simples ponto ao invés de um coringa. Agora vamos passar para expressões agrupadas,
a fim de encontrar um jeito de criar uma expressão que case com Frederic ou Fred. Primeiro vamos
começar com o operador "?":
"Uma expressão consistindo de caracter seguido por uma interrogação escapada ( \? ) casa com zero
ou uma instância daquele caracter".
Exemplo:
"bugg\?y" casa com o seguinte: bugy , buggy mas não com bugggy
Agora vamos para expressões agrupadas. No nosso exemplo, queremos tornar opcional a string
"eric" após "Fred", ou seja, não apenas um caracter mas sim um conjunto de caracteres (string).
"Uma expressão dentro de parênteses escapados é tratada como um único caracter"
Exemplos:
"Fred\(eric\)\?" Smith casa com "Fred Smith" or "Frederic Smith" \(abc\)* casa com abc , abcabcabc,
etc (isto é, qualquer número de repetições da string "abc", incluindo zero).
Note que temos que tomar cuidado quando nossas expressões contém espaços em
branco. Quando eles aparecem, precisamos colocar a expressão entre aspas, para que o shell não
interprete mal nosso comando. Para o exemplo acima:
grep "Fred\(eric\)\? Smith" file
Eu aconselho fortemente a sempre usar aspas, mesmo que não usemos espaços em
brancos. Já tive muita dor de cabeça porque expressões e comandos não funcionavam simplesmente
por não estarem entre aspas. Um dos exemplo acima (o do bugg\?y) não
funciona no meu sistema se não estiver entre aspas. Veja:
Casando com um número especifico de repetições Suponha que você queira casar um
número específico de repetições de uma expressão. Um bom exemplo são números de telefones.
Você pode procurar por um número de telefone com sete dígitos,
assim:
grep "[:digit:]\{3\}[ -]\?[:digit:]\{4\}" file
Ou seja, três dígitos, opcionalmente um espaço ou um hífen e mais 4
dígitos.
Procurando por começo e fim de linha: Isso é muito interessante. Digamos que você
queira procurar por linhas que contendo espaços em brancos no começo da linha, a palavra hello e
então o fim da linha. Vamos começar com este
exemplo:
>cat file
hello
hello world
hhello
Isso não é o que nós queremos. O que está errado ? O problema é que o grep procura
por linhas contendo a string hello e todas as linhas especificadas contem ela. Para contornar isso,
introduzimos os caracteres de começo e fim de linha:
"O caracter ^ significa começo de linha e o $ significa fim da linha"
Retornando ao nosso exemplo:
Procura no meu inbox por mensagens de uma pessoa em particular (no caso, Alex).
Esse tipo de expressão regular é extremamente útil e filtros de e-mail, como o procmail, utilizam isso
para fazerem tudo.
Isso ou Aquilo: Procurando uma coisa OU outra:
"A expressão consistindo de duas expressões separadas pelo operador OU \| casa linhas contendo
uma das duas expressões"
Note que você DEVE colocar a expressão dentro de aspas simples ou duplas:
Digamos que você queira procurar strings que contenham uma substring em mais de um
lugar. Um exemplo é as tags de cabeçalhos de HTML. Suponha que eu queira procurar por
"<H1>alguma string</H1>". Isto é fácil de se fazer. Mas suponha que eu queira fazer o mesmo, mas
permita H2 H3 H4 H5 e H6 no lugar de H1. A expressão <H[1-6]>.*</H[1-6]> não é boa, desde
que casa com "<H1>alguma string</H3>" e nos queremos que a tag de abertura seja igual a de
fechamento. Para fazermos isso, usamos backreference:
"A expressão \n onde n é um número, casa com o conteúdo do n-ésimo conjunto de parênteses na
expressão".
Nossa... isso realmente precisa de um exemplo!!!!
<H\([1-6]\)>.*</H\1> faz o que nos queríamos fazer acima...
"O Senhor \(dog\|cat\) e a senhora \1 foram visitar o Senhor \(dog\|cat\) e a senhora \2"
Esse é outro exemplo bobo. Os casais tem sempre que serem iguais. Ou um casal de
cachorro ou de gato.
Alguns detalhes cruciais: caracteres especiais e aspas Caracteres Especiais:
Aqui nós descrevemos os caracteres especiais para RegExp (expressões regulares) no grep. Note
que no "egrep", que usa expressões regulares estendidas (atualmente não tem nenhuma
funcionalidade a mais que as expressões regulares normais do GNU grep), a lista de caracteres
especiais são os mesmos, diferindo somente que alguns não precisar estar "escapados". Os seguintes
caracteres são considerados especiais, e precisam estar escapados:
? \ . [ ] ^
$
Note que o sinal de $ perde seu sentido se tiver caracteres depois dele, do mesmo jeito
que o sinal ^ perde seu sentido se tiver caracteres antes dele. Os colchetes [ ] comportam-se um
pouco diferente. A baixo segue as regras para eles:
? O colchete direito ( ] ) perde seu sentido especial se colocado no começo de uma lista, por
exemplo: "[]12]" casa com ] , 1,
or 2.
? Um hífen perde seu significado especial se colocado por último. Assim, [15-] casa com 1, 5 ou -
Aspas:
Em primeiro lugar, aspas simples são mais seguras de usar porque elas protegem suas
expressões regulares de serem alteradas pelo bash (como foi visto nas aulas anteriores). Por
exemplo, grep "!" file vai produzir um erro, já que o shell pensa que "!" está se referindo ao
comando de histórico do shell, enquanto grep '!' file funciona perfeitamente.
Quando você deve usar aspas simples ? A resposta é: se você precisa usar variáveis do
shell, use aspas duplas. Caso contrário, use aspas simples. Por exemplo:
grep "$HOME" file
Procura em file pelo nome do seu diretório pessoal, enquanto grep
'$HOME' file procura pela string $HOME.
Agora vamos ver a sintaxe do egrep em contraste com a sintaxe do grep. Ironicamente,
apesar do nome "estendido", o egrep atualmente tem menos funcionalidade do que quando foi
criado, para manter a compatibilidade com o tradicional grep. A melhor maneira de fazer um grep
estendido é utilizar grep -E que usa a sintaxe de expressões regulares estendidas sem perda de
funcionalidade. Bom, espero que vocês tenham tido uma boa idéia de como funcionam as expressões
regulares. Elas são extremamente importante, principalmente para utilizar o grep e o poderosíssimo
sed, do qual trataremos na próxima aula. Não se preocupem se estiverem confuso com as
expressões, quando começarem a utilizá-las, as idéias vão clareando.
6. sed
Falaremos agora sobre o sed. Vale lembrar que este tutorial dará uma breve introdução
ao sed, ajudando os iniciantes a entender como ele funciona. Portanto muitos comandos mais
complexos serão omitidos, já que seria necessário um livro inteiro para ensinar tudo sobre o Sed.
Mas não se preocupem, ensinaremos o suficiente sobre ele para utilização em shell script.
Resumo de RegExp
Vamos a um resumo sobre as expressões regulares, explicadas na aula anterior e agora aplicadas ao
Sed:
^ casa com o começo de linha
$ casa com o fim de linha
. casa com qualquer caracter simples (apenas um)
(caracter)* casa com qualquer ocorrência, em qualquer quantidade, de (caracter)
(caracter)? casa com zero ou uma ocorrência de (caracter)
[abcdef] casa com qualquer caracter dentro dos [ ] (neste caso, a b c d e ou f),
faixas de caracteres como [a-z]
são permitidas.
[^abcdef] casa com qualquer caracter NÃO incluído em [] (neste caso, qualquer
caracter que não seja a b c d e ou f)
(caracter)\{m,n\} casa com m-n repetições de (caracter)
(caracter)\{m,\} casa com m ou mais repetições de (caracter)
(caracter)\{,n\} casa com n ou menos (também zero) repetições de (caracter)
(caracter)\{n\} casa com exatamente n repetições de (caracter)
\(expressão\) operador de grupo.
\n Backreference
- casa com o n-ésimo grupo
expressão1\|expressão2 casa com expressão1 ou expressão2. Funciona com o GNU sed, mas essa
característica pode não funcionar com outros Seds.
Caracteres Especiais
Os caracteres especiais no sed são os mesmo do grep, com uma diferença: a barra
normal / é um caracter especial no sed. A razão disso ficará clara mais para frente quando
estudarmos os comandos do sed.
O sed funciona assim: ele lê da entrada padrão, uma linha de cada vez. Para cada linha,
ele executa uma série de comandos de edição e então a linha é escrita na saída padrão. Um exemplo
que mostra como ele funciona: Nós usamos o comando "s", que significa "substitute" (substituir) ou
"search and replace" (procurar e trocar).
O formato é:
s/expressão-regular/texto-substituto/{flags}
Nós não vamos discutir todas as flags ainda. A única que usamos abaixo é a "g", que
Nós atualmente damos um nome para o texto (geralmente uma linha) que o sed leu e
está processando (editando): ele chama-se "pattern space" (uma boa tradução seria "área de
edição"). O sed lê da entrada padrão e joga na sua área de edição, executando nela uma seqüência de
comandos de edição e então ele escreve o resultado na saída padrão.
Então, o sed pode ler do arquivo ou da entrada padrão, e os comandos podem ser especificados em
um arquivo de script ou na linha de comando. Esse arquivo, chamado sedscript.sed é um arquivo que
contém todos os comandos do sed, ao invés de serem especificados na linha de comando. Esses sed's
scripts são úteis quando precisamos de um processamento de texto mais complexo e refinado.
Note o seguinte: se os comandos são lidos de um arquivo (sed script), espaços em branco podem ser
fatais. Eles podem fazer o script falhar sem explicação aparente. Eu recomendo editar os arquivos de
comandos do sed com um editor como o VIM que pode mostrar o final da linha e você pode ver se
existem espaços em branco entre os comandos e o fim da linha.
Comando de Substituição
[endereço1[,endereço2]]s/procura/substituto/[f
lags]
As flags podem ser as seguinte:
O comando de Deleção
[endereço1[,endereço2]]d
Isto deleta o conteúdo da "área de edição" (se esta casar com os endereços fornecidos). Todos os
comandos seguintes serão pulados (já que não a nada a fazer com uma área de edição em branco) e
uma nova linha será lida e jogada na área de edição e todo o processo se repete.
Exemplos:
Exemplo 1:
>cat file
O gato preto foi caçado por um cachorro marrom.
>sed -e 's/preto/branco/g' file
O gato branco foi caçado por um cachorromarrom.
Exemplo 2:
>cat file
O gato preto foi caçado por um cachorro marrom.
O gato preto não foi caçado por um cachorro marrom.
>sed -e '/não/s/preto/branco/g' file
O gato preto foi caçado por um cachorro marrom.
O gato branco não caçado por um cachorro marrom.
Neste caso, a substituição é aplicada somente a linhas que casam com a expressão regular "/não/".
Portanto, ela não é aplicada a primeira linha, pois esta não contem a palavra "não".
Exemplo 3:
>cat file
linha 1 (um)
linha 2 (dois)
linha 3 (três)
Exemplo 3a:
Exemplo 3b:
Exemplo 3c:
LINHA 1 (um)
Exemplo 4:
>cat file
olá
Este texto será cortado
olá (também será retirado)
ReTiRaDo Também!!!
tchau
(1) Este texto não foi apagado
(2) nem este ... ( tchau )
Exemplo 5:
>cat file
http://www.kernel.org/
>sed -e
's@http://www.kernel.org@http://www.metalab.un
c.edu@' file
http://www.metalab.unc.edu/
Backreference no Sed
Uma das coisas legais sobre backreference no sed é que você pode usar não apenas em
procura de textos mas também na substituição de textos.
O comando Quit
Sub-rotinas
Este exemplo faz um bom uso dos conceitos descritos acima. Para isto, nos usamos um
shell-script, desde que os comandos são muito longos e precisaríamos escrever a longa string X
várias vezes. Note que usamos aspas duplas, já que a variável $X precisa ser expandida pelo shell. A
sintaxe para rodar esse script é: "script arquivo",onde "script" é o nome dado ao script e "arquivo" é
o nome do arquivo a procurar uma palavra da lista.
#!/bin/sh
X='word1\|word2\|word3|\word4|\word5'
sed -e "
/$X/!d
/$X/{
s/\($X\).*/\1/
s/.*\($X\)/\1/
q
}" $1
s/\($X\).*/\1/
s/.*\($X\)/\1/
s/.*\($X\).*/\1/
Mas isto não funciona. Por que? Suponha que temos a linha:
Nós não temos como saber se $X vai casar com word1, word2 ou word3, então
quando nós citamos ele (\1), nós não sabemos quais dos termos está sendo citado. O que está sendo
usado para certificar-se que não há problemas na correta implementação, é isto:
"O operador * é guloso. Ou seja, quando há ambigüidade sobre qual (expressão)* pode casar, ele
tentar casar o máximo possível."
Então neste exemplo, s/\($X\).*/\1/ , .* tenta engolir o máximo da linha possível, em
particular, se a linha contém isso:
"word1 word2 word3"
Então nós podemos ter certeza que .* casa com " word2 word3" e portanto $X seria
word1. Não se preocupem se não entenderam muito bem este tópico. Ele é complicado mesmo e
pouco usado. O importante é entender como ele funciona, o uso de regexp e do comando de
substituição e deleção. Para maiores informações sobre o sed, dê uma olhada na sua man page: "man
sed" . Espero que vocês tenham gostado desse pequeno tutorial sobre sed.
Capitulo 6. Bibliografia
Capitulo 7. Links
Capitulo 8. Anexos
1. Exemplos
10. Matemática
#!/bin/sh
clear
echo "Qual o número ?"
read n
soma=0
for w_i in $(seq 1 $n ) ;
do
soma=$( expr $soma + $w_i)
done
echo "A soma dos $n primeiros números naturais é $soma"
exit 0
Fatorial
#!/bin/sh
clear
read -p "Qual o número para o cálculo do fatorial ?" n
if [ $n -le 0 ];
then
echo "Não existe fatorial de número negativo"
exit 1
fi;
fatorial=1
for w_i in $( seq 1 $n ) ;
do
fatorial=$[ $fatorial * $w_i ]
done
echo "O fatorial de $n é $fatorial"
exit 0
11. Acesso
#!/bin/sh
# vi /usr/bin/login_unico
EOF
Troque as permissões dos arquivos para :
# chmod 755 /usr/bin/envia /usr/bin/recebe
# chown root /usr/bin/envia /usr/bin/recebe
# chgrp root /usr/bin/envia /usr/bin/recebe
Sintax :
# envia machine arquivo ou
# recebe machine arquivo
#!/bin/bash
# renomeia arquivos que tenham nome em maiusculas para o equivalente em minusculas
[ $# -lt 1 ] && {
echo "*** Erro: você precisa passar os arquivos que quer renomear"
echo "Uso: Mminusculas arquivo1 [arquivoN]"
exit
}
for maiuscula
do
[ -e "$maiuscula" ] || {
echo "$maiuscula não existe, continuando com o próximo arquivo"
continue
}
minuscula=$( echo $maiuscula | tr A-Z a-z )
mv $maiuscula $minuscula
done
13. Backup
#!/bin/bash
# OBS.: Por favor melhore este script! :-)
# Se o número de parâmetros for menor que 2...
[ $# -lt 2 ] && {
echo "Uso: `basename $0` destino origem [origem2 origem3...]"
exit 1 # ... sai do script
}
echo "--> Fazendo backup"
FILE="${1}_$(/bin/date +%d-%m-%Y).tgz"
shift
# Aqui está o "segredo": o shift acima é executado para que eu possa usar "$*" no if abaixo.
#!/bin/bash
#
# *********************************************
# * Script para visualizar HOWTOs rapidamente *
# *********************************************
# http://meleu.da.ru
# meleu <[email protected]>
# Inspirado em um outro script que vi no Tips-HOWTO.
# O script do Tips-HOWTO era muito simples, fiz algumas
# modificações que são interessantes para nós que falamos
# português e de vez em quando temos uns HOWTOs traduzidos,
# e ainda fiz um "suporte" aos mini-HOWTOs. ;-)
# E mais: se você não lembra direito do nome do HOWTO, pode
# passar apenas a(s) primeira(s) letra(s) e/ou usar os curingas
# ('*', '?' e '[]'), mas aconselha-se, neste último caso, o uso de
# aspas ("quoting") para evitar que seja passado como parâmetro(s)
# o conteúdo do diretório atual. Caso ele encontre mais de um
# arquivo para a expressão você poderá escolher através da tela que
# aparecerá.
# Exemplos:
# [prompt]$ howto Net
# [prompt]$ howto "*[Bb]ash"
# [prompt]$ howto "*Prog"
#
# Se você ainda não tem e não sabe onde pegar HOWTOs traduzidos
# procure em http://ldp-br.conectiva.com.br
#
# Pré-requisitos para o script funcionar direitinho (ou seja, sem
# precisar de alterações):
# + os HOWTOs devem estar em "/usr/doc/Linux-HOWTOs";
# + os HOWTOs em português devem estar em "/usr/doc/Linux-HOWTOs.pt";
# + os mini-HOWTOs devem estar em "/usr/doc/Linux-mini-HOWTOs";
# + todos os [mini-]HOWTOs[.pt] devem estar gzipados, se os seus não
# estão assim basta entrar no diretório dos HOWTOs e digitar
# "gzip *".
#
#
# Se você testou o script, ele funcionou direitinho e você gostou,
# então digite "cp howto.sh /usr/local/bin/howto" para que todos do
# seu sistema possam utilizá-lo. ;-)
#
# Aproveite!
# Estes são os diretórios onde são instalados os [mini-]HOWTOs no
# Slackware. Se a sua distribuição usa um diretório diferente
# mude a(s) variável(is) a seguir.
HTDIR=/usr/doc/Linux-HOWTOs
miniHTDIR=/usr/doc/Linux-mini-HOWTOs
PTHTDIR=/usr/doc/Linux-HOWTOs.pt # este é onde eu coloco os traduzidos
function Ler {
zless $1
echo -e "${RED}\nTchau!\n$NCOLOR"
exit
}
# --------------------
# - TESTA PARÂMETROS -
# --------------------
case $1 in
# - mensagem de ajuda -
# ---------------------
-h) echo -e "
${RED}--[ Lista de opções ]--$NCOLOR
-p \t HOWTOs em português
-m \t mini-HOWTOs
-h \t imprime esta mensagem
"
exit # depois da mensagem de ajuda, sair
;;
# - HOWTOs em português -
# -----------------------
-p) HTDIR=$PTHTDIR
[ -z "$2" ] && Lista
shift # Lembra do 'shift'? Aqui ele faz com que o primeiro
# parâmetro deixe de ser '-p' para ser o nome-do-HOWTO
;;
# - mini-HOWTOs -
# ---------------
-m) HTDIR=$miniHTDIR
[ -z "$2" ] && Lista
shift # mesma função do shift no '-p'
;;
cd $HTDIR
15. todo.sh
#!/bin/bash
PROG=`basename $0`
EDITOR=`which vi`
FILE="$HOME/.ToDo"
USAGE="Uso: $PROG [-h|-e]"
case $1 in
-h) echo "
$USAGE
esac
16. inseretxt.sh
#!/bin/bash
#
# Muitas vezes durante a escrita do texto
# "Programação em Bourne-Again Shell" eu precisava
# inserir um código de um script numa determinada
# posição do arquivo e esta posição ficava entre muito texto antes e depois
# dessa linha.
# Para fazer isso de uma maneira mais cômoda, eu escrevi este script.
# Para informações sobre o uso tente o parâmetro '-h' ou
# '--help'.
# Se você passar como o parâmetro "linha" um número maior
# que o de linhas total do "ArqOriginal" os "arquivosN"
# serão inseridos no final do "ArqOriginal".
# Ah! Lembre-se de uma coisa: "linha" precisa ser um
# inteiro positivo. E lembre-se de mais uma coisa: 0
# não é um número positivo. ;-)
# meleu.
#
B="\e[1m"
N="\e[m"
USO="Uso: `basename $0` linha ArqOriginal arquivo1 [arquivoN ...]"
AJUDA="Tente \"`basename $0` --help\" para ajuda"
$USO
Onde:
\"linha\" é a linha onde o texto será inserido
\"ArqOriginal\" é o arquivo que receberá os textos
\"arquivoN\" são os arquivos que serão inseridos em ArqOriginal
"
exit
}
[ $# -lt 3 ] && {
echo -e ${B}Erro: erro na passagem de parâmetros$N
echo $USO
echo $AJUDA
exit -1
}
Linha=$1
# verificando se $Linha é um número inteiro positivo
[ `expr $Linha - 1 2>/dev/null` -ge 0 ] 2>/dev/null || {
echo -e ${B}Erro: O primeiro parâmetro precisa ser inteiro positivo$N
echo $AJUDA
exit 1
}
ArqOriginal=$2
[ -f $ArqOriginal ] || {
echo -e ${B}Erro: \"$ArqOriginal\" não existe ou não é um arquivo regular$N
echo $AJUDA
exit 2
}
function ApagarSair {
rm "$1"
exit $2
}
shift 2
Temp=/tmp/`basename $ArqOriginal`-$$.tmp
ArqFinal="$ArqOriginal.new"
[ -e $ArqFinal ] && {
echo -e ${B}Já existe um arquivo chamado \"$ArqFinal\".$N
read -n 1 -p "Deseja sobregravá-lo? (s/N) " SN
echo
[ "$SN" != 'S' -a "$SN" != 's' ] && {
echo -e "$B\nOperação cancelada!$N"
ApagarSair $Temp 3
}
}
echo -e "
${B}Operação concluída com sucesso.$N
Confira em \"$ArqFinal\"
"
ApagarSair $Temp
17. Mextract.sh
#!/bin/sh
#
# ****************************
# * Meleu Extraction Utility *
# ****************************
# http://meleu.da.ru
# Este script é baseado no Phrack Extraction Utility, (mais informações
# <http://www.phrack.org>). Fiz ele, primeiro para praticar, segundo para
# servir como mais um exemplo no texto "Programação em Bourne-Again Shell",
# e último para extração dos códigos do texto. =P
#
############# Se já existirem arquivos com o nome dos que serão extraídos
# !CUIDADO! # eles serão sobregravados! Portanto, se você extrair uma vez,
############# alterar o(s) código(s) extraído(s) e extrair novamente, perderá as alterações feitas!
#
#
# A seguir eu vou comentar sobre o código fazendo referência aos tópicos
# do texto "Programação em Bourne-Again Shell".
#
#
# + A função do IFS é explicada no tópico "2.2. Variáveis do Shell", neste script eu usei o IFS
com valor nulo (vazio) para que os comandos considerem espaços que vêm antes de qualquer
caractere como parte do dado. Se você fizer por exemplo "read var" e antes de entrar qualquer
# coisa colocar espaços e/ou TAB, você verá que eles serão desconsiderados se o IFS tiver seu valor
default (espaço, TAB, newline);
#
# + A opção -r no read (explicada em 3.2. read) serve para ignorar o
B="\e[1m"
N="\e[m"
[ $# -lt 1 ] && {
echo -e "${B}Erro: falta parâmetros$N"
echo "Uso: `basename $0` arquivo1 [arquivoN]"
exit 1
}
[ -w . ] || {
echo -e "${B}Erro: você não tem permissão de escrita neste diretório$N"
exit 1
}
OldIFS="$IFS"
IFS=
cat $@ |
while read -r LINHA ; do
IFS="$OldIFS"
set -- $LINHA
case "$1" in
'<++>')
TempIFS="$IFS"
IFS=/
set -- $2
IFS="$TempIFS"
while [ $# -gt 1 ]; do
FILE=${FILE:-.}/$1
[ -d $FILE ] || mkdir $FILE
shift
done
FILE="${FILE:-.}/$1"
if echo -n 2>/dev/null > $FILE ; then
echo "* Extraindo $FILE"
else
echo -e "$B--> houve um erro ao tentar extrair '$FILE'"
echo -e " este arquivo será ignorado.$N"
unset FILE
fi
;;
'<-->')
unset FILE
;;
*)
[ "$FILE" ] && {
IFS=
echo "$LINHA" >> $FILE
}
;;
esac
done
echo "--> Fim <--"
18. Arquivos
Backup Diário
#!/bin/bash
# VIRTVS Engenharia e Informática Ltda
# Assunto = Backup Diário
# Data de Inclusão = 29/09/2002
# Data de Alteração = 29/09/2002
# Parâmetros tar =
# c =Cria arquivo tar
# f =Tratar o arquivo
# r =Adicionar ao final de um arquivo tar
# p =Preserva as permissões
# z =Compactar com gzip
# v =Exibe arquivos processados
# -W –verify =Verifica arquivo após gravá-lo
# -P --absolute-names =Preserva /
# tar -tvf =Exibe conteúdo do arquivo tar com as permissões
clear
BASE="${HOSTNAME}_$(date "+%Y%m%d%H%M%S")"
DESTINO=/tmp/
ARQUIVO_DESTINO=${DESTINO}${BASE}.tar
ARQUIVO_DUMP=${DESTINO}${BASE}.pgdump
ARQUIVO_ERRO="/tmp/erro.backup"
TRACOS="--------------------------------------------------------------------------------"
echo "Gerando $ARQUIVO_DESTINO ..."
echo $TRACOS
echo "Gerando $ARQUIVO_DUMP do PostgreSQL..."
sudo -u postgres pg_dump satis > $ARQUIVO_DUMP
Backup Semanal
#!/bin/bash
# VIRTVS Engenharia e Informática Ltda
# Assunto = Backup Semanal
# Data de Inclusão = 29/09/2002
# Data de Alteração = 29/09/2002
#
# Parâmetros tar =
# c = Cria arquivo tar
# f = Tratar o arquivo
# r = Adicionar ao final de um arquivo tar
# p = Preserva as permissões
# z = Compactar com gzip
# v = Exibe arquivos processados
# -W --verify = Verifica arquivo após gravá-lo
# -P --absolute-names = Preserva /
# tar -tvf = Exibe conteúdo do arquivo tar com as
permissões
clear
BASE="${HOSTNAME}_$(date "+%Y%m%d%H%M%S")"
DESTINO="/mnt/hdc/"
ARQUIVO_DESTINO=${DESTINO}${BASE}.tar
ARQUIVO_OK="/tmp/ok.txt"
ARQUIVO_ERRO="/tmp/erro.txt"
TRACOS="--------------------------------------------------------------------------"
echo $TRACOS
echo "Fim"
exit 0
#!/bin/sh
menu ()
{
echo "Entre com uma opcao:"
echo
echo "1. Exibir Data e Hora"
echo "2. Exibir Calendario"
echo "3. Desligar o sistema"
echo "4. Sair do programa"
echo
echo -n "Escolha: "
read escolha
Funcao_Escolha
}
Funcao_Escolha ()
{
case $escolha in
1) echo ; date ; echo ; menu ;;
2) echo ; cal ; echo ; menu ;;
3) halt ;
4) echo "Até mais" ; echo ; exit ;;
esac
}
menu
sintaxe : expr 2 + 2
+ - adição
- - subtração
\* - multiplicação
/ - divisão
% - resto
Talvez vc seje vaidoso(a) e queira exibir mensagens com cor , então a sintaxe é :
#!/bin/sh
Tabela de cor :
30 - preta
31 - vermelha
32 - verde
33 - amarela
34 - azul
35 - rosa
36 - azul piscina
37 - branca
{} FIM {}
2. Laboratórios
Laboratório 1 :Diversos
1. Qual o resultado do último comando abaixo? Havendo problemas, qual será a correção ?
#a=Universidade de
#b=Fortaleza
#echo $a$b
6. Elabore um shell-script para enviar para o email [email protected] a relação das variáveis
globais e locais alocadas no presente shell.
8. Elabore um shell-script para calcular a soma dos n primeiros números inteiros positivos. Leia n
em tempo de execução.
9. Elabore um shell-script para, dado um número n, exibir a Série de Fibonnacci até ele.
Exemplo: 0 – 1 – 1 – 2 – 3 – 5 – 8 – ...
2. Receber duas strings em tempo de execução e exibir uma mensagem se elas são iguais ou não.
4. Encontrar e gerar um arquivo com a extensão teste.bak no diretório do usuário de login se ele
não existir.
7. Verificar quais arquivos em /bin podem ser lidos pelo usuário atual.
9. Verificar se um usuário está logado e enviar uma mensagem para ele pedindo desconexão.
Menu
del Remover arquivo
ren Renomear arquivo
cp Copiar arquivo
me Mandar conteúdo por email
end Sair do aplicativo
1. Exibição de números de 1 a n
#!/bin/bash
Laboratório 4 :Funções
1. Exemplo de Menu
#!/bin/bash
function opcao1 {
echo “Opção 1”
}
function opcao2 {
echo “Opção 2”
return 10
}
echo “Informe a Opção”
read op
case $op in
1) echo “Entrei na Função opcao1”
opcao1
;;
2) echo “Entrei na Função opcao2”
opcao2
;;
exit 0
Laboratório 5 :Diversos
M e n u – [Usuário]
i Incluir
a Alterar
e Excluir
c Consultar
f Fim
Variáveis
$0 é o nome do shell-script;
$1 a $9 $1 é o primeiro parâmetro, $9 o nono, e assim por diante;
${10}, ${11}... quando o número do parâmetro possui mais de um dígito é necessário o
uso das chaves;
$* todos os parâmetros em uma única string (exceto o $0);
$@ todos os parâmetros, cada um em strings separadas (exceto $0);
$# número de parâmetros (sem contar com o $0).
$? valor de retorno do último comando (explicado mais adiante);
$$ PID do script.
Comando case
case <parâmetro> in
<opção 1>) <comandos 1>
;;
[opção 2] ) <comandos 2>
;;
*)
< comandos se não for nenhuma das opções >
;;
esac
x=0
while [ "$x" -le 10 ];
do
echo "Execução número: $x"?;
x = $((x+1));
done;
#!/bin/bash
CONT=10
until [ $CONT -eq 0 ]; do
echo -ne "$CONT\t"
let CONT--
done
echo
case <parâmetro> in
<opção 1>)
<comandos 1>
;;
[opção 2] )
<comandos 2>
;;
*)
< comandos se não for nenhuma das opções >
;;
esac
3. Exercicios
Lista 20040513
1. Escreva um script chamado clean para limpar seu diretório $HOME, removendo todos os
arquivos com extensão "bak" ou "~" que não tenham sido acessados há pelo menos 3 dias. Dica:
use os comandos find e rm .
2. Escreva um script para criar diretórios com nome DirXXX, onde XXX varia de 001 a 299.
3. Escreva um conjunto de scripts para gerenciar o apagamento de arquivos. O script del deve
mover os arquivos passados como parâmetros para um diretório lixeira; o script undel deve
mover arquivos da lixeira para o diretório corrente e o script lsdel deve listar o conteúdo da
lixeira. O diretório lixeira deve ser definido através da variável de ambiente $LIXEIRA.
4. Funda os scripts do exercício anterior em um só script del, com os demais (undel e lsdel) sendo
links simbólicos para o primeiro. Como fazer para que o script saiba qual a operação desejada
quando ele for chamado, sem precisar informá-lo via parâmetros ?
5. Escreva um script para verificar quais hosts de uma determinada rede IP estão ativos. Para testar
se um host está ativo, use o comando ping. A rede deve ser informada via linha de comando, no
formato x.y.z, e o resultado deve ser enviado para um arquivo com o nome x.y.z.log. Deve ser
testada a acessibilidade dos hosts de x.y.z.1 a x.y.z.254. O arquivo /etc/hosts contem os nomes
dos hosts no formato: <IP> <host> <apelido>
4. Links
http://www.semlimites.com.br/informatica/informatica_software_sistemasoperacionais_unix_linux.sh
tm
Índice alfabético
Metacaracteres 33
Pipes 34
shell 7pp., 16p., 20, 23pp., 29, 31, 33p., 40pp., 52p., 64, 75, 96, 99pp., 104p., 108,
127p., 131
shift 20, 22, 25, 111, 113, 115, 118, 121
Modified Version:
A.Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and
from those of previous versions (which should, if there were any, be listed in the History section of
the Document). You may use the same title as a previous version if the original publisher of that
version gives permission.
B.List on the Title Page, as authors, one or more persons or entities responsible for authorship of the
modifications in the Modified Version, together with at least five of the principal authors of the
Document (all of its principal authors, if it has less than five).
C.State on the Title page the name of the publisher of the Modified Version, as the publisher.
D.Preserve all the copyright notices of the Document.
E.Add an appropriate copyright notice for your modifications adjacent to the other copyright
notices.
F.Include, immediately after the copyright notices, a license notice giving the public permission to
use the Modified Version under the terms of this License, in the form shown in the Addendum
below.
G.Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in
the Document's license notice.
H.Include an unaltered copy of this License.
I.Preserve the section entitled "History", and its title, and add to it an item stating at least the title,
year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no
section entitled "History" in the Document, create one stating the title, year, authors, and publisher
of the Document as given on its Title Page, then add an item describing the Modified Version as
stated in the previous sentence.
J.Preserve the network location, if any, given in the Document for public access to a Transparent
copy of the Document, and likewise the network locations given in the Document for previous
versions it was based on. These may be placed in the "History" section. You may omit a network
location for a work that was published at least four years before the Document itself, or if the
original publisher of the version it refers to gives permission.
K.In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and
preserve in the section all the substance and tone of each of the contributor acknowledgements
and/or dedications given therein.
L.Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles.
Section numbers or the equivalent are not considered part of the section titles.
M.Delete any section entitled "Endorsements". Such a section may not be included in the Modified
Version.
N.N. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant
Section.
O.If the Modified Version includes new front-matter sections or appendices that qualify as
Secondary Sections and contain no material copied from the Document, you may at your option
designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant
Sections in the Modified Version's license notice. These titles must be distinct from any other section
titles.
You may add a section entitled "Endorsements", provided it contains nothing but endorsements of
your Modified Version by various parties--for example, statements of peer review or that the text has
been approved by an organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words
as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one
passage of Front-Cover Text and one of Back-Cover Text may be added by (or through
arrangements made by) any one entity. If the Document already includes a cover text for the same
cover, previously added by you or by arrangement made by the same entity you are acting on behalf
of, you may not add another; but you may replace the old one, on explicit permission from the
previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission to use their
names for publicity for or to assert or imply endorsement of any Modified Version.
5. COMBINING DOCUMENTS
You may combine the Document with other documents released under this License, under the terms
defined in section 4 above for modified versions, provided that you include in the combination all of
the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant
Sections of your combined work in its license notice.
The combined work need only contain one copy of this License, and multiple identical Invariant
Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same
name but different contents, make the title of each such section unique by adding at the end of it, in
parentheses, the name of the original author or publisher of that section if known, or else a unique
number. Make the same adjustment to the section titles in the list of Invariant Sections in the license
notice of the combined work.
In the combination, you must combine any sections entitled "History" in the various original
documents, forming one section entitled "History"; likewise combine any sections entitled
"Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled
"Endorsements."
6. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other documents released under this
License, and replace the individual copies of this License in the various documents with a single copy
that is included in the collection, provided that you follow the rules of this License for verbatim
copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it individually under this
License, provided you insert a copy of this License into the extracted document, and follow this
License in all other respects regarding verbatim copying of that document.
7. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate and independent documents or
works, in or on a volume of a storage or distribution medium, does not as a whole count as a
Modified Version of the Document, provided no compilation copyright is claimed for the
compilation. Such a compilation is called an "aggregate", and this this License does not apply to the
other self-contained works thus compiled with the Document, on account of their being thus
compiled, if they are not themselves derivative works of the Document. If the Cover Text
requirement of section 3 is applicable to these copies of the Document, then if the Document is less
than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that
surround only the Document within the aggregate. Otherwise they must appear on covers around the
whole aggregate.
8. TRANSLATION
Translation is considered a kind of modification, so you may distribute translations of the Document
under the terms of section 4. Replacing Invariant Sections with translations requires especial
permission from their copyright holders, but you may include translations of some or all Invariant
Sections in addition to the original versions of these Invariant Sections. You may include a
translation of this License provided that you also include the original English version of this License.
In case of a disagreement between the translation and the original English version of this License, the
original English version will prevail.
9. TERMINATION
You may not copy, modify, sublicense, or distribute the Document except as expressly provided for
under this License. Any other attempt to copy, modify, sublicense or distribute the Document is
void, and will automatically terminate your rights under this License. However, parties who have
received copies, or rights, from you under this License will not have their licenses terminated so long
as such parties remain in full compliance.
10. FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions of the GNU Free Documentation
License from time to time. Such new versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/. each version
of the License is given a distinguishing version number. If the Document specifies that a particular
numbered version of this License "or any later version" applies to it, you have the option of
following the terms and conditions either of that specified version or of any later version that has
been published (not as a draft) by the Free Software Foundation. If the Document does not specify a
version number of this License, you may choose any version ever published (not as a draft) by the
Free Software Foundation.
How to use this License for your documents.
To use this License in a document you have written, include a copy of the License in the document
and put the following copyright and license notices just after the title page: