Capítulo 08: Coisas úteis de se aprender ←
Exemplos Variados
Aqui pretendo listar alguns scripts que possuem uma ou mais das seguintes características: é útil, é bem escrito, é didático.
Eu poderia ficar "enchendo linguiça" metendo vários exemplos, mas o objetivo é apenas ilustrar que vários conceitos mostrados no livro podem ser utilizados em conjunto num mesmo script. Se você quer mais exemplos olhe as Referências.
backup.sh
#!/bin/bash
# backup.sh
#
# 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.
if tar czf $FILE $* ; then
echo "--> Backup feito com sucesso"
else
echo "--> OCORREU UM ERRO &2
exit 1
fi
howto.sh
#!/bin/bash
# howto.sh
#
# *********************************************
# * Script para visualizar HOWTOs rapidamente *
# *********************************************
# http://meleu.da.ru
# meleu
#
# 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
# Variáveis que indicam as cores (pra não precisar ficar
# digitando os códigos ANSI toda hora)
BLUE="\e[1;34m"
RED="\e[1;31m"
NCOLOR="\e[m"
function Ler {
zless $1
echo -e "${RED}\nTchau!\n$NCOLOR"
exit
}
# Função que mostra a lista dos HOWTOs e sai do script.
function Lista {
ls -C $HTDIR | less
echo -e "
${BLUE}Uso:$NCOLOR `basename $0` [-p | -m] nome-do-HOWTO
Faça '`basename $0` -h' para ver a descrição das opções."
exit 1
}
# se não for passado nenhum parâmetro ele mostra a lista
[ -z "$1" ] && Lista
# --------------------
# - 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'
;;
esac # Ao fim deste case $1 tem necessariamente o nome ou a(s)
# primeira(s) letra(s) do nome do HOWTO a ser procurado.
cd $HTDIR
FILE=`ls $1*.gz 2>/dev/null`
[ `echo $FILE | wc -w` -gt 1 ] && {
PS3="Entre com o número: "
select opc in $FILE Sair ; do
[ "$opc" = "Sair" ] && exit
for HOWTO in $FILE ; do
[ "$opc" = "$HOWTO" ] && Ler $HOWTO
done
done
}
[ -e "$FILE" ] && Ler $FILE
# Isto só será executado se não for encontrado o HOWTO
echo -e "${RED}* * * HOWTO não encontrado * * *$NCOLOR"
echo "Tente '`basename $0` [-p | -m]' para ver a lista"
exit 1
# - = < E O F > = -
todo.sh
#!/bin/bash
# todo.sh
PROG=`basename $0`
EDITOR=`which vi`
FILE="$HOME/.ToDo"
USAGE="Uso: $PROG [-h|-e]"
case $1 in
-h) echo "
$USAGE
-e edita a lista de \"Para Fazer\" (To Do)
-h imprime esta mensagem e sai
Sem parâmetros $PROG irá mostrar a lista de \"To-Do\".
"
exit ;;
-e) $EDITOR $FILE
exit ;;
'') cat $FILE 2> /dev/null || {
echo "Você precisa criar o arquivo $HOME/.ToDo !"
echo "Entre \"$PROG -e\" para editar seu ~/.ToDo"
echo "Para ajuda tente \"$PROG -h\""
exit 1
} ;;
*) echo "Parâmetro \"$1\" desconhecido!"
echo "$USAGE"
echo "Entre com \"$PROG -h\" para ajuda."
exit ;;
esac
inseretxt.sh
#!/bin/bash
# inseretxt.sh
#
# 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"
[ "$1" = '-h' -o "$1" = '--help' ] && {
echo -e "
${B}Insere o conteúdo de arquivo(s) dentro de um outro.$N
$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
# --> início do arquivo original:
head -$[$Linha-1] $ArqOriginal > $Temp
# --> arquivos que serão inseridos:
ContaAcerto=0
for Arq in "$@"; do
[ -f "$Arq" ] || {
echo -e ${B}OBS.: \"$Arq\" não existe ou não é um arquivo regular$N
continue
}
cat $Arq >> $Temp
(( ContaAcerto++ ))
done
[ $ContaAcerto -eq 0 ] && {
echo -e ${B}Nenhum arquivo foi inserido em \"$ArqOriginal\"$N
ApagarSair $Temp 3
}
echo
# --> pra terminar, final do arquivo original:
sed -n "$Linha,\$p" $ArqOriginal >> $Temp
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
}
}
cat $Temp > $ArqFinal
echo -e "
${B}Operação concluída com sucesso.$N
Confira em \"$ArqFinal\"
"
ApagarSair $Temp
Mextract.sh
#!/bin/sh
# Mextract.sh
#
# ****************************
# * Meleu Extraction Utility *
# ****************************
# http://meleu.da.ru
#
# Este script é baseado no Phrack Extraction Utility, (mais informações
# ). 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
# poder que a contra-barra (backslash) tem de "escapar" os caracteres. Em
# outras palavras: a opção -r garante que quando o read receber uma
# contra-barra ela será passada para a variável sem nenhum valor especial;
#
# + O cat enviando os dados para o read do while é explicado em
# "5.5. Redirecionando loops" sob o título de "pipeando para o while";
#
# + o set é usado para fazer com que cada palavra (palavra aqui tem um
# sentido de conjunto de caracteres separados por aqueles definidos no
# IFS) vire um parâmetro posicional, conforme explicado em
# "2.4.2. set (para editar parâmetros posicionais)". A opção -- quer dizer
# "acabaram as opções, o que vier a partir daqui são os valores dos
# parâmetros de posição", esta opção serve para prevenir que alguma
# informação que comece com o caractere - seja considerado uma opção
# sendo passada para o set;
#
# + No tópico "2.5. Substituição de Variáveis" você verá a explicação
# de se usar "FILE=${FILE:-.}/$1";
#
# + Bom... acho que é isso. Leia o código, execute-o, faça testes,
# mude o código, execute-o novamente, veja o que mudou nos resultados,
# leia as manpages em caso de dúvidas... Enfim, use o método hacker de
# aprender a programar! ;-)
#
#
# Espero que curta!
# meleu
#
# P.S.: Quer um "dever de casa"? Altere o código para que ele verifique
# se já existe arquivos com o mesmo nome dos que estão prestes a
# serem extraídos. Se existir, alertar o usuário sobre isso. Tente
# também fazer meios de detecção dos possíveis erros que possam
# ocorrer...
# Ah, sei lá! Brinque com o código um pouco! =)
#
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
Capítulo 10: Referências →