Estruturas de repetição em shell script, como em demais linguagens aqui teremos o for, while e o until e irei aproveitar o engajado para passar arrays em Shell Script que ficara perfeito com estrutura de repetição.
FOR
Estrutura bem parecida com a de outras linguagens porém com varias maneiras de implementar, podemos na própria estrutura For definir o numero de elementos “1 2 3 4 5”, ou uma sequencia “seq 10” e ele irá repetir até o fim desta condição.
for [ condição ]; do #Seu codigo done
Exemplo:
#!/bin/bash #Exemplo 1 echo "Contando ate 5" for i in 1 2 3 4 5; do echo $i; done #Exemplo 2 echo "Contando ate 10" for i in $(seq 10); do echo $i; done #Exemplo 3 echo "Contando ate 5 la estilo C" for ((i=0; i<=5; i++)) do echo "Executando o $i" done
WHILE
Muito parecido com o For, pode praticamente fazer a mesma coisa porém uma definição mais claro entre os dois é que o For irá iniciar em um valor e irá parar quando chegar em outro definido, o while também pode trabalhar assim além de continuar no laço enquanto a condição for verdadeira.
while [ condição ]; do #Seu codigo done
Exemplo:
#!/bin/bash #Exemplo 1 BLOG="www.cleitonbueno.wordpress.com" while [ "$BLOG" = "www.cleitonbueno.wordpress.com" ]; do echo "Esse blog eh show!!!" done #Exemplo2 var_control=0 while true; do if [ $var_control -gt 10 ]; then break fi echo Valor $var_control # Incrementando var_control, funciona apenas em Bash (( var_control++ )) done
UNTIL
Imagine o oposto do While, pois é esse é o until, no while enquanto é executado o laço enquanto a condição é verdadeira no until é executado enquanto for falsa.
until [ condição ]; do #Seu codigo done
Exemplo:
#!/bin/bash response=yes count=0 until [ "$response" = "no" ]; do (( count++ )) echo "Laco $count" #if [ $count -eq 5 ]; if [ $count -ge 5 ]; then response=no fi sleep 1 done
Então em Shell Script temos esses três modos para repetição, os mais utilizados são for e while, os exemplos acima são apenas uma pequena amostra de como implementar porém começa a ficar interessante com o uso de array.
Array
Vetores ou arrays em qualquer linguagem de programação você irá utilizar ou se não irá implementar um modo de manipular e no Shell Script não é diferente, array pode ser declarado da seguinte maneira:
num[0]=”10″
num[1]=”20″
num[2]=”30″
num[3]=”40″
num[4]=”50″
ou
nomes=(“Cleiton” “Manoel” “Pedro” “Paulo”)
Vamos ao exemplo com o arquivo forEx1.sh:
#!/bin/bash num[0]="10" num[1]="20" num[2]="30" num[3]="40" num[4]="50" nomes=("Cleiton" "Manoel" "Pedro" "Paulo") # O que diferencia de uma variavel comun é o ${ } e dentro o array com a posição echo "Numero 2 : ${num[1]}" echo "Numero 4 : ${num[3]}" echo "Nome 1 : ${nomes[0]}" echo "Nome 4 : ${nomes[3]}"
Saída:
cleiton@linuxVM:~/projetos/shell script$ ./forEx1.sh Numero 2 : 20 Numero 4 : 40 Nome 1 : Cleiton Nome 4 : Paulo bueno@ti-cleiton ~/projetos/shell_script $
Agora um exemplo mais do dia-a-dia, vamos implementar um Shell Script para bloquear algumas portas com iptables, exemplo forEx2.sh:
#!/bin/bash PORT=("137" "138" "139" "445" "22" "80" "443") echo -e "nExemplo de regras para firewall" for porta in ${PORT[@]} do echo "Bloqueando a porta $porta" iptables -A INPUT -p udp --dport $porta -j DROP iptables -A INPUT -p tcp --dport $porta -j DROP done
Vamos entender o exemplo acima, temos o nosso array PORT=(“137” “138” “139” “445” “22” “80” “443”), onde eu criei um for que irá pegar o tamanho do array ${PORT[@]} e um a um vai sendo lido e armazenado no $porta onde executo minha regra de bloqueio com iptables.
Poderíamos melhoras esse script, separando as portas/serviços TCP dos UDP e colocando uma descrição, ficaria legal.
Vamos então ao exemplo forEx3.sh:
#!/bin/bash portas=("137" "138" "139" "445" "22" "80" "443") protocol_portas=("udp" "udp" "tcp" "tcp" "tcp" "tcp" "tcp") desc_portas=("NetBIOS" "NetBIOS" "NetBIOS" "SMB" "SSH" "HTTP" "HTTPS") echo -e "nExemplo de regras para firewall com descricao e identificacao de protocolo" echo -e "nSERVICOtPORTAtPROTOCOLOtSTATUS" for ((i=0; i<${#portas[@]}; i++)) do if [ "${protocol_portas[$i]}" = "tcp" ]; then iptables -A INPUT -p tcp --dport ${portas[$i]} -j DROP echo -e "${desc_portas[$i]}t${portas[$i]}tTCPttBLOQUEADO" elif [ "${protocol_portas[$i]}" = "udp" ]; then iptables -A INPUT -p udp --dport ${portas[$i]} -j DROP echo -e "${desc_portas[$i]}t${portas[$i]}tUDPttBLOQUEADO" fi done
Saída:
cleiton@linuxVM:~/projetos/shell script$ ./forEx3.sh Exemplo de regras para firewall com descricao e identificacao de protocolo SERVICO PORTA PROTOCOLO STATUS NetBIOS 137 UDP BLOQUEADO NetBIOS 138 UDP BLOQUEADO NetBIOS 139 TCP BLOQUEADO SMB 445 TCP BLOQUEADO SSH 22 TCP BLOQUEADO HTTP 80 TCP BLOQUEADO HTTPS 443 TCP BLOQUEADO
Esse ficou mais completo, foi a mesma ideia porem com mais dois arrays do protocol_portas e desc_portas, e temos uma variável $i do for que baseado no tamanho do array irá ser o identificado dos três arrays para exibir ou usar algum valor.
Agora um script útil utilizando o while, vamos ano nosso whileEx1.sh:
#!/bin/bash if [ -z $1 ] then echo -e "Parametro PID deve ser informado!" exit fi # TIME_WAIT eh em segundos entao temos (60s * VALOR) para intervalo em minutos de espera #TIME_WAIT=60 # 1 minuto #TIME_WAIT=300 # 5 minutos TIME_WAIT=3600 # 1 hora LOCK_FILE="/tmp/$RANDOM" PID=$1 echo -e "Arquivo de log criado com sucesso em $LOCK_FILE" echo -e "Iniciando monitoramento..." echo -e "Inicio do log `date +%D_%T`" > $LOCK_FILE while [ -d /proc/$PID ]; do if [ `date +%H%M` -ge 1200 ]; then echo -e "O processo $$ deve ser morto, passou 12horas de execucao" >> $LOCK_FILE # Enviar e-mail para sysadmin com sendmail e como corpo o $LOCK_FILE # COMANDO AQUI rm -Rf $LOCK_FILE exit else echo -e "Script executando. Data/Hora: `date +%D_%T`" >> $LOCK_FILE echo -e "Comando: `cat /proc/$PID/cmdline`" >> $LOCK_FILE echo -e "n" >> $LOCK_FILE fi sleep $TIME_WAIT done echo -e "Processo finalizado com sucesso." >> $LOCK_FILE echo -e "Data/hora: `date +%D_%T`" >> $LOCK_FILE # Enviar e-mail para sysadmin com sendmail e como corpo o $LOCK_FILE # COMANDO AQUI rm -Rf $LOCK_FILE
Saída:
cleiton@linuxVM:~/projetos/shell script$ ./whileEx3.sh 20648 Arquivo de log criado com sucesso em /tmp/9503 Iniciando monitoramento...
Analisando o nosso arquivo de log:
cleiton@linuxVM:~/projetos/shell script$ tail -f /tmp/9503 Inicio do log 03/03/14_15:58:44 Script executando. Data/Hora: 03/03/14_15:58:44 Comando: /usr/sbin/fcgi-pm-kstart Script executando. Data/Hora: 03/03/14_16:58:44 Comando: /usr/sbin/fcgi-pm-kstart Script executando. Data/Hora: 03/03/14_17:58:44 Comando: /usr/sbin/fcgi-pm-kstart Script executando. Data/Hora: 03/03/14_16:04:44 Comando: /usr/sbin/fcgi-pm-kstart
Vamos entender nosso script. Ele faz parte de um processo que tenho onde começa a ser executado dia sim dia não onde varias coisas são feitas, porém ocorreu de algumas vezes esse meu script travar, e como eu sei que quando bem sucedido ele leva em torno de 6 horas, eu criei um outro script que é chamado e passado o PID desse meu script principal, então meu segundo script que é algo bem próximo ao exemplo acima irá monitorar o /proc/NUM_PID_FORNECIDO se este diretório existir quer dizer que o processo esta rodando e a sacada é o if [ `date +%H%M` -ge 1200 ], se o script for maior que 1200 seria 12:00 do dia ae sei que algo seu problema e o log é enviado para o meu e-mail para eu tomar as devidas providencias, no meu caso ele já é morto com um kill.
Foi uma pequena amostra do poder do Shell Script agora unindo if, while e variáveis, e para não virar bagunça apos enviar nosso arquivo de log por e-mail ele é removido no final do script se bem sucedido ou logo apos ultrapassar as 12horas de execução.
Nosso ultimo script dessa vez utilizando o for para uma tarefa de backup, vamos ver nosso forEx4.sh:
#!/bin/bash #Parte1 SRC="/home/bueno/projetos/shell_script/" DST="/mnt/backups/vm.03/bueno/" cd $SRC LS_DIR=$(find * -maxdepth 0 -type d) for folder in $LS_DIR; do tar -czf $DST$folder.tgz $folder > /dev/null done #Parte2 USER_ID=($(cat /etc/passwd | cut -d: -f3)) USER_DIR=($(cat /etc/passwd | cut -d: -f6)) DST_HOME="/tmp/" for ((i=0; i<${#USER_ID[@]}; i++)) do if [ ${USER_ID[$i]} -ge 1001 -a ${USER_ID[$i]} -lt 65500 ]; then USER_NAME=$(cat /etc/passwd | grep ${USER_DIR[$i]} | cut -d: -f1) echo -e "UID ${USER_ID[$i]} > 1000tDIR ${USER_DIR[$i]}" tar -czf $DST_HOME$USER_NAME.tgz ${USER_DIR[$i]} > /dev/null fi done
Agora foi uma promoção heim, ta lendo um e vai aprender dois *rs, como pode-se ver eu fiz dois scripts no nosso exemplo acima, vamos entender o primeiro Parte1, as variáveis DST e SRC dizem por si só que um serio o destino e outra a origem, eu acesso a origem na linha 7 e logo em seguida executo o comando find no diretório corrente e a saída ficara na variável LS_DIR, o meu find ira pesquisar tudo que for diretório -type d e estiver ali apenas -maxdepth 0 se eu coloca-se 1 seria 1 grau de dependência então avançaria até 1 diretório adiante.
Baseado no que estiver no LS_DIR sera os caminhos dos diretórios que eu irei compactar com o tar na linha12 e la esta o destino com o nome do .tgz e na frente cada diretório encontrado, então olha como se comporta essa primeira parte abaixo:
cleiton@linuxVM:~/projetos/shell script$ ls -1 estrutura_case/ estrutura_condicional/ estrutura_repeticao/ scripts/ variaveis/ cleiton@linuxVM:/mnt/backups/vm.03/bueno$ ls -1 estrutura_case.tgz estrutura_condicional.tgz estrutura_repeticao.tgz scripts.tgz variaveis.tgz
Na Parte2 tem uma sacadinha que fiz questão de mostrar e por isso dois exemplos, ele irá fazer algo importante, backup de “todos” os usuários do computador, muitos scripts na internet realizam backup de tudo que esta no /home, porém tem alguns usuários que o diretório padrão não é o home como Nagios e Zabbix e ae não se torna tão eficiente, e para este caso elaboramos o Parte2 do forEx4.sh.
As variáveis USER_ID e USER_DIR uma ira obter o UID de todos os usuários e a outra o diretório padrão, tenho DST_HOME que serão o destino do meu backup utilizei o /tmp para exemplo, e ae entro em uma estrutura de repetição de 0 até o numero total de elementos UID encontrados, onde por cada UID verifico se é maior que 1000 e menor que 65500, já que acima de 1000 são usuários comuns e abaixo de 1000 usuários do sistema, atendendo essa logica irei pegar o nome do usuário no /etc/passwd poderia usar regex no USER_DIR(preguiça rs), com isso em mãos é o mesmo tar utilizado agora apenas com uma variável de controle $i para identificar o usuário, vamos ver como se comportou a execução desta segunda etapa:
cleiton@linuxVM:~/projetos/shell script$ cat /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync www-data:x:33:33:www-data:/var/www:/bin/sh backup:x:34:34:backup:/var/backups:/bin/sh rtkit:x:110:119:RealtimeKit,,,:/proc:/bin/false hplip:x:111:7:HPLIP system user,,,:/var/run/hplip:/bin/false cleiton:x:1000:1000:Cleiton Bueno,,,:/home/cleiton:/bin/bash bueno:x:1001:1001:Bueno,,,:/home/bueno:/bin/bash projeto:x:1002:1002:Projetos,,,:/home/projeto:/bin/false sshd:x:113:65534::/var/run/sshd:/usr/sbin/nologin mysql:x:114:123:MySQL Server,,,:/nonexistent:/bin/false postgres:x:115:124:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash openfire:x:123:129:Openfire XMPP server,,,:/var/lib/openfire:/bin/false nagios:x:1003:1003:Nagios,,,:/opt/nagios:/bin/sh cleiton@linuxVM:~/projetos/shell script$ ls -l /tmp/ bueno.tgz cleiton.tgz nagios.tgz projeto.tgz
A maior sacada neste exemplo foi:
Parte1
LS_DIR=$(find * -maxdepth 0 -type d)
Parte2
USER_ID=($(cat /etc/passwd | cut -d: -f3))
Os dois executam comandos e armazenam em uma variável, a Parte1 é exatamente desta maneira já a Parte2 quando usamos VARIAVEL=($(COMANDO)), estou falando que meu resultado ira entrar em um array.
Espero ter passado a ideia de repetição e o uso da mesma em shell script foram vários exemplos e com praticas sem mais delongas que podem ser usadas no dia-a-dia, qualquer duvida insira um comentário.
Até a próxima!
Shell Script – Estrutura de repetição e arrays by Cleiton Bueno is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.