Mostrando postagens com marcador bash. Mostrar todas as postagens
Mostrando postagens com marcador bash. Mostrar todas as postagens

sábado, 2 de março de 2013

Pausa em shell script

A execução de um script no interpretador de comandos do Linux (este artigo adotando o Bash) pode ser pausada por meio de dois artifícios. Um é o comando interno "read" e o outro é o comando "sleep". O "read" pode ser utilizado para esperar o pressionamento de uma tecla e o "sleep" para que espere por determinado tempo.

A seguinte linha, em determinado ponto do script, faz uma pausa na execução, neste ponto, e deixa o script esperando o pressionamento da tecla Enter:

read -p "Pressione [Enter] para continuar ou CTRL+C para sair..."

Como CTRL+C interrompe a execução do script, pode-se aproveitar para informar esta possibilidade.

Se desejar uma pausa controlada por tempo, a seguinte linha em determinado ponto do script faz uma pausa na execução por, no caso, 5 segundos:

sleep 5

O padrão para o "sleep" é em segundos mas pode-se adicionar o sufixo m para minutos, h para horas ou d para dias.

quinta-feira, 6 de maio de 2010

Usando o histórico do bash com eficiência

O interpretador de comandos padrão no Linux é o Bash (Bourne-again shell) e ele contém uma funcionalidade muito útil que é guardar o histórico dos comandos executados no prompt. Pressionando a tecla "Seta para cima", no prompt do shell, o usuário pode rolar pelo histórico dos comandos e então pressionando o "Enter" o comando é executado novamente.

Todo o histórico de um usuário é armazenado por padrão no arquivo "/home/usuario/.bash_history" e tem seu tamanho, para a sessão atual, controlado por uma variável de ambiente declarada no arquivo "/etc/profile" ou no "/home/usuario/.bash_profile" (ou .bashrc), em uma linha semelhante a "HISTSIZE=1000". A variável "HISTFILESIZE" limita o tamanho do histórico armazenado no arquivo, arquivo este que pode ser especificado pela variável "HISTFILE".

Também há a possibilidade de filtrar a entrada dos comandos ao histórico. A variável "HISTIGNORE" serve para especificar quais comandos não serão armazenados, por exemplo HISTIGNORE="pwd:ls:ls -la:exit:". A variável "HISTCONTROL" controla o armazenamento dos comandos. Com "HISTCONTROL=ignoredups" os comandos repetidos consecutivos não são armazenados e com " HISTCONTROL=erasedups" os comandos repetidos de todo histórico são removidos. Com "HISTCONTROL=ignorespace" é possível ignorar o armazenamento de um comando se ele iniciar com um espaço. Já com "HISTCONTROL=ignoreboth" todos os valores para esta variável são definidos.

O Bash fornece o comando interno "history" para visualizar ou apagar o histórico. Executando-o sem os parâmetros, todo o histórico é mostrado na tela, em linhas numeradas. Executando-o com o parâmetro "-c" todo o histórico é apagado, executando-o com o parâmetro "-d N" apenas a linha N é removida e executando-o com "-r" é apagado somente o histórico da sessão atual do usuário. Existem outros parâmetros, explicados na página manual do Bash.

É possível procurar por uma linha no histórico. Pressionando "Ctrl+r" o prompt fica ativo para receber a palavra chave e a medida que alguma linha contenha a string, esta vai aparecendo no prompt de comando. Para executar um comando específico contido no histórico usa-se o comando "!n", sendo n o número da linha que ele se encontra.

Muito mais pode ser feito com o histórico do interpretador de comandos Bash, consulte a página manual do Bash e outras fontes pela Internet para conhecer mais.

domingo, 17 de janeiro de 2010

Automatize sequências de comandos no prompt do shell com laços

Um comando de iteração, ou laço, permite que um conjunto de instruções seja executado repetidamente até que ocorra uma certa condição. No Linux o interpretador de comandos Bash, da mesma forma que às principais linguagens de programação, também possui os comandos básicos de iteração e são os comandos for, while e until.

O comando for é o mais simples dos três, utiliza um contador com um início e fim pré-definido e o incremento do passo. Durante cada passo a variável "argumento" recebe o valor de cada membro da "lista". O conjunto de instruções que será executado repetidamente deve ficar entre as palavras reservadas do e done. Sua sintaxe básica é apresentada abaixo:

for argumento in lista
do
comando(s)
done


Como vamos executar todo o comando em uma única linha no prompt do shell, então é necessário alguns ponto-e-vírgulas:

for argumento in lista; do comando(s); done


Veja alguns exemplos de lista para compor um contador para a repetição. Nos casos abaixo todos produzem o mesmo efeito, contando de 1 à 10. Em dois exemplos foi usado o utilitário seq, que imprime uma sequência de números:

for i in 1 2 3 4 5 6 7 8 9 10; do echo -n "$i "; done
for i in $(seq 10); do echo -n "$i "; done
for i in `seq 10`; do echo -n "$i "; done
for i in {1..10}; do echo -n "$i "; done
for ((i=1; i<=10; i++)); do echo -n "$i "; done


A lista de valores pode conter palavras ao invés de números, veja estes exemplos. O segundo exemplo utiliza um arquivo que contém as palavras:

for dia in Segunda Terça Quarta Quinta Sexta; do echo $dia; done
for dia in `cat semana.txt`; do echo $dia; done


O incremento do passo pode ser mais de 1 em 1. Os exemplos abaixo produzem o mesmo efeito, contando de 1 à 16 com passo 2:

for ((i=1;i<=16;i+=2)); do echo $i; done
for i in $(seq 1 2 16); do echo $i; done
for i in `seq 1 2 16`; do echo $i; done
for i in 1 3 5 7 9 11 13 15; do echo $i; done
foi i in {1..16..2}; do echo $i; done # A partir do bash versão 4.


É possível também utilizar duas variáveis no contador, na sintaxe semelhante à linguagem C:

for ((i=1, j=1; i<=10; i++, j++)); do echo -n "$i-$j "; done


Exemplos práticos com o uso do comando for:

for i in *.*; do novo="$(echo "$i" | sed 'y/ÀÁÂÃÄÅÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÇÑàáâãäåèéêëìíîïòóôõöùúûüçñ/
AAAAAAEEEEIIIIOOOOOUUUUCNaaaaaaeeeeiiiiooooouuuucn/')"; mv "$i" "$novo"; done

for i in `ls *-6_*.pdf`; do mv $i ${i/-6_/-06_}; done

n=1; for nome in $(awk 'BEGIN{FS=":"}{print $1}' < /etc/passwd ); do echo "USUARIO #$n = $nome";
let n+=1; done

for file in *.mp3; do mkdir -p "$(mp3info -p "%a/%l" "$file")" && mv "$file" "$(mp3info -p
"%a/%l/%t.mp3" "$file")"; done

for x in $(lynx -dump http://marx.vanderlinden.com.br/sem/2009.1/fi.php | awk '{print $2}' | grep
.pdf); do wget -c $x; done

for arquivo in $( find /usr/local/bin/ -type f -name '*' | sort ); do strings -f $arquivo | grep
"GPL" | sed -e "s%/usr/local/bin/%%"; done


O comando while testa por uma condição no início do laço e mantém a iteração enquanto esta condição é verdadeira (retorna 0 quando falso). Diferentemente do comando for, o comando while é usado quando o número de repetições não é conhecido de antemão. O conjunto de instruções que será executado repetidamente deve ficar entre as palavras reservadas do e done. Sua sintaxe básica é apresentada abaixo:

while condição
do
comando(s)
done


Mais uma vez, como vamos executar todo o comando em uma única linha, no prompt do shell, então é necessário alguns ponto-e-vírgulas:

while condição; do comando(s); done


Para o comando while executar o laço a condição já deve existir. Veja este exemplo onde é inicializada a variável de controle antes do comando while (Obs.: é necessário os espaços dentro dos colchetes pois é um teste de condição):

i=1; while [ $i -le 10 ]; do echo -n "$i "; let i+=1; done


Ou, produzindo o mesmo resultado:

i=1; while ((i<=10)); do echo $i; ((i+=1)); done


Uma outra forma, por exemplo, é suprir a entrada com o conteúdo de um arquivo:

cat semana.txt | while read linha; do echo -n "$linha "; done


Ou, produzindo o mesmo resultado:

while read linha; do echo -n "$linha "; done < <(cat semana.txt)


Exemplos práticos com o uso do comando while:

find /usr/share/ -regextype posix-egrep -iregex '.*\.(jpg|jpeg|gif|png|tif|tiff|bmp|svg)' -type f
-print | while read IMAGEM; do convert -thumbnail 120x120 -compress JPEG -quality 20 -delete 1--1
"$IMAGEM" thumbs/"$(echo "${IMAGEM}" | sed 's/.*\///g')".jpg; done

while read nome; do echo "USER #$n = $nome"; let n+=1; done < <(awk 'BEGIN{FS=":"}{print $1}'
< /etc/passwd )

seq 1 100 | while read i; do echo -n "texto${i}.txt "; touch texto${i}.txt 2>&1; done

while read i; do echo -n "texto${i}.txt "; touch texto${i}.txt 2>&1; done < <(seq 1 100)

while read arquivo; do strings -f $arquivo | grep "GPL" | sed -e "s%/usr/local/bin/%%";
done < <( find /usr/local/bin/ -type f -name '*' | sort )


O comando until testa por uma condição no início do laço e mantém esta iteração enquanto a condição for falsa (diferentemente do comando similar em outras linguagens), sendo o oposto do comando while. Também o comando until é usado quando o número de repetições não é conhecido de antemão. O conjunto de instruções que será executado repetidamente deve ficar entre as palavras reservadas do e done. Sua sintaxe básica é apresentada abaixo:

until condição
do
comando(s)
done


Como sempre, vamos executar todo o comando em uma única linha, no prompt do shell, então é necessário alguns ponto-e-vírgulas:

until condição; do comando(s); done


Também como o comando while, para o until executar o laço a condição já deve existir. Neste exemplo é inicializada a variável de controle antes do comando until:

i=1; until [ $i -gt 10 ]; do echo $i; let i+=1; done


Ou, produzindo o mesmo resultado:

i=1; until ((i>10)); do echo $i; ((i+=1)); done


Da mesma forma que demonstrado no comando while, também é possível suprir a entrada com o conteúdo de um arquivo.

Exemplos práticos com o uso do until:

until [ -e teste.txt ]; do echo -e "Crie o arquivo teste.txt\n"; sleep 5; done

until [ $(ls -l eventos.log | awk '{print $5}') -gt 5000 ]; do echo "Arquivo log ainda pequeno";
sleep 1; done


Nestes três comandos de iteração é possível interromper o comando ou somente pular o passo atual e continuar no próximo. Para isso usa-se respectivamente os comandos break e continue na posição desejada entre o do e o done. Obviamente para ter este controle certamente vai ser necessário o uso de um comando condicional, como o if.

Como escolher entre os laços for, while ou until? Pode ser possível conseguir o mesmo efeito com os três comandos de laço. Veja os comandos abaixo, todos produzem o mesmo resultado, então provavelmente o melhor será aquele que visualmente for mais fácil de entender o algoritmo. Mas observe que o comando for é mais utilizado quando o número de iterações é conhecido e os comandos while e until servem para um número impreciso de iterações:

for ((i=1; i<=10; i++)); do echo "$i "; done
i=1; while ((i<=10)); do echo $i; ((i+=1)); done
i=1; until ((i>10)); do echo $i; ((i+=1)); done

sexta-feira, 13 de novembro de 2009

Definindo apelidos e funções para comandos no bashrc

O arquivo bashrc determina o comportamento do shell, neste caso o interpretador de comandos bash. Basicamente o arquivo bashrc permite criar atalhos, podendo ser apelidos (alias) ou programas interativos (funções), que são carregados na inicialização do bash. O arquivo bashrc permite criar atalhos para praticamente todos os comandos.

Por exemplo, é muito mais fácil digitar o atalho "l" do que um comando como "ls -laF --color=auto". E uma função é como um roteiro (script) para o shell colocado em um arquivo bashrc. É ideal para casos em que o atalho não será para apenas um simples comando ou necessita de uma lógica em alguma estrutura de programação.

Existem dois arquivos bashrc que o interpretador de comandos bash carrega em cada nova inicialização do shell, o /etc/bashrc é carregado para todos os usuários e o ~/.bashrc é carregado para o usuário que iniciou o shell, ou seja, cada usuário tem o seu arquivo ~/.bashrc.

Tanto para apelidos ou para funções é recomendável testar primeiro os comandos em uma execução direta no prompt antes de incluí-los no arquivo bashrc. Diferentemente de um script para o shell, a interpretação de uma função não gera um novo processo no sistema.

Alguns exemplos de apelidos definidos no arquivo bashrc:

alias l="ls -laF --color=auto"
alias cds="cd /etc/rc.d/init.d && ls"
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias qag="rpm -qa | grep -i"
alias av="avast -a -c -t=A"
alias fd='mount /dev/fd0 /mnt/floppy; cd /mnt/floppy && ls'


Uma função pode ser definida com a estrutura apresentada abaixo:

function nome_da_função()
{
comando 1;
comando 2;
comando 3;
...
}


A palavra "function" é opcional na declaração de uma função mas pode ser considerada uma boa prática. Veja um exemplo bastante interessante:

function extrair()
{
if [ -f $1 ] ; then
case $1 in
*.tar.bz2) tar xvjf $1 ;;
*.tar.gz) tar xvzf $1 ;;
*.bz2) bunzip2 $1 ;;
*.rar) unrar x $1 ;;
*.gz) gunzip $1 ;;
*.tar) tar xvf $1 ;;
*.tbz2) tar xvjf $1 ;;
*.tgz) tar xvzf $1 ;;
*.zip) unzip $1 ;;
*.Z) uncompress $1 ;;
*.7z) 7z x $1 ;;
*) echo "'$1' não pode ser extraído via >extrair<" ;;
esac
else
echo "'$1' não é um arquivo válido"
fi
}


Para ativar uma definição imediatamente após a sua inclusão no arquivo bashrc, sem a necessidade de sair do interpretador de comandos e entrar novamente, basta digitar:

$ source ~/.bashrc


Para saber quais apelidos estão ativos no interpretador bash basta executar o comando alias no prompt. O shell irá imprimir a lista dos apelidos na saída padrão, normalmente na tela do monitor. As funções ativas podem ser vistas com a execução do comando set, sem opções, onde o nome e o valor de cada variável e função do shell serão mostrados na tela.