Shell Script – Estrutura de repetição e arrays

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!

Share Button

CC BY-NC-SA 4.0 Shell Script – Estrutura de repetição e arrays by Cleiton Bueno is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.