You are on page 1of 10

Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

Recursos avançados do bash que você não aprende na


escola

Autor: Leandro Santiago <leandrosansilva at gmail.com>


Data: 13/09/2007

Utilizando a aritmética do bash no lugar do comando test

Para quem não sabe, no bash, não é obrigatório o uso do programa test para
comparação de variáveis. Digo isso pois todo mundo aprende, nos mais
diversos tutoriais que encontramos na internet, a fazer comparações com o
uso deste programa. Qual o problema disso?

Uma sintaxe diferente da que encontramos em outras linguagens.


Como o test é um programa externo e não um built-in do bash, sua
execução é mais lenta.

O que eu pretendo com esta dica?

Mostrar uma sintaxe mais limpa para tais operações, a fim de tornar o
código muito parecido com o mesmo código em linguagens como
c/c++.
Tornar a execução do programa mais rápida (se bem que, como
estamos falando de shell-script, isso não faz muita diferença).

Primeiramente, vou começar com o seguinte código, escrito em "shell


normal":

$ i=$RANDOM
$ j=$RANDOM
$ if [ $i -eq 20 -o $j -eq 30 ]
> then
> echo I é igual a vinte ou J é igual a 30
> fi

A mesma comparação no if poderia ser escrita com dois test's:

$ if [ $i -eq 20 ] || [ $j -eq 30 ]
> (...)

1 de 10 10-12-2010 22:47
Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

Mas, como estamos utilizando dois test's, há perda de desempenho.

Agora, o mesmo código em bash puro:

$ i=$RANDOM
$ j=$RANDOM
$ if (( ( i == 20 ) || ( i == 20 ) ))
> then
> echo I é igual a vinte ou J é igual a 30
> fi

Ou a condição poderia ser dividida em duas operações:

$ if (( i == 20 )) || (( i == 20 ))
> (...)

Novamente, o problema da execução de mais de uma operação.

Algumas outras equivalências entre o test e o bash:

$a -lt $b <=> (( a < b ))


$a -le $b <=> (( a <= b ))
$a -gt $b <=> (( a > b ))
$a -ge $b <=> (( a >= b ))
$a -eq $b <=>(( a == b ))
$a -ne $b <=> (( a != b ))

Além desse tipo de comparação, podemos realizar tarefas como:

incrementar variáveis:

$ incremento=2
$ i=1
$ (( i+=incremento )) ## isso acrescenta 2 à variável i, que fica com o
valor 3

O mesmo vale para operações como subtração (/), multiplicação (*) e módulo
(%). Ou se você quiser, simplesmente, adicionar 1 à variável, use:

$ ((i++)) # pós-incremento
$ ((++i)) # pré-incremento
$ ((i--)) # pós-decremento
$ ((--i)) # pré-decremento

Também é possível utilizar uma atribuição "normal":

2 de 10 10-12-2010 22:47
Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

$ (( i = (j%20)+1 ))

$ ((i=i**3)) # Eleva i ao cubo

Com tudo isso e mais alguns conhecimentos de tratamento de variáveis no


bash, podemos substituir os comandos -z e -n do test!

$ if (( ${#i} == 0 ))
> then
> echo a variável i não está definida
> fi

Este comando equivale ao -z (string nula). Quanto ao -n, basta substituir o


'==' por um '!='.

Manipulando bases numéricas

Essa eu descobri esses dias: o bash utiliza mais de uma base numérica, que
não seja a decimal.

Quando um número começa com zero (ex: 012), ele é utilizado como tendo
base octal. Assim, 012 em octal equivale à 10 em decimal.

$ if ((012==10))
> then
> echo 'Isso é igual?!!!!'
> fi
Isso é igual?!!!!

Quando um número começa com 0x, sua base é hexadecimal. Assim, por
exemplo, 0xf é igual à 15.

$ ((i=0xf))
$ echo $i
15

Também é possível "converter" um número de uma base qualquer para a


decimal. Um exemplo:

$ echo $((2#100100111001101)) ## Base binária


18893

A sintaxe é: (([base]#valor))

3 de 10 10-12-2010 22:47
Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

Se a base for omitida, usa-se o sistema decimal.

Lembrando que não pode haver nenhum algarismo no valor que seja maior
que a base (ex: não existe o valor 3 em binário, que só aceita dois "tipos de
símbolos": 0 e 1):

$ echo $((2#3))
bash: 2#3: value too great for base (error token is "2#3")

Obs: Quando a base é maior que dez, usa-se letras para complementar. Ex:

$ echo $((20#kk))
440

Se a base é menor que 36, maiúsculo e minúsculo têm o mesmo


significado.
Se a base é maior que 36 (números e minúsculas), usa-se letras
maiúsculas.
Se for maior, usa-se mais o @.
Se maior ainda, usa-se mais o _.

Ou seja, se alguém te disse que algo custa R$ Wf@3, não chame ela de
louca. Pergunte qual base numérica está utilizando!

Laços de repetição

Tá, todo mundo sabe que existem centenas de formas de utilizar um for no
bash. O legal é que a maneira aritmética dele é bem parecida com a sintaxe
em C:

$ for ((i=0;i<10;i++))
> do
> echo $i
> done

Onde temos ((inicialização, condição para continuar, incremento)). Mas isso


pode ser expandido de uma forma que você não esperaria.

É possível haver mais de uma inicialização, mais de uma condição de


continuação, e mais de um incremento. Pode-se até omitir qualquer um
destes! (só tome cuidado com loops infinitos).

$ unset z # garantia que "zerou" a variável


$ read -p "digite: " j

4 de 10 10-12-2010 22:47
Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

$ for ((i=0; (i<=20) && (j!=5); i++,z--))


do
echo $i
read -p "digite: " j
done
echo z vale = $z

digite: 9
0
digite: 7
1
digite: 8
2
digite: 6
3
digite: 3
4
digite: 1
5
digite: 4
6
digite: 5
z vale = -7

O que esse código faz? Ele lê o valor de j, inicializa i com zero, e enquanto i
for menor ou igual a 20 E j for diferente de 5, ele lê j do usuário e
incrementa i e decrementa z. No final, exibe o valor de z. Fantárdigo!!!

Funciona assim: as inicializações são separadas por vírgula, a condição para


continuar pode ser simples ou não, envolver quantas variáveis ou operações
(lógicas ou aritméticas) forem necessárias, e os incrementos são separados
com vírgula.

Outra boa sacada do pessoal do bash, para aproxima de outras linguagens é


o operador ternário:

$ i=20
$ j=15
$ echo $(( i < j ? 1 : 2 ))
2

O que isso significa? Se i for menor que j, retorna (imprime por causa do
echo) 1, senão retorna 2. Neste caso retorna 2, pela primeira operação ser
falsa.

Outra sacada legal é uma outra sintaxe do laço for. Todo mundo aprende o

5 de 10 10-12-2010 22:47
Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

for assim:

$ for ((i=0;i<10;i++))
> do
> echo $i
> done

Mas se você gosta de uma sintaxe como a do C, pode substituir do e done


por { e }:

$ for ((i=0;i<10;i++))
>{
> echo $i
>}

Ou, numa só linha:

$ for ((i=0;i<10;i++)); { echo $i ;}

Hey! Isso nem parece bash!!! Mas é!

Podemos utilizar também essas comparações nos comandos while e until:

$ until ((i==10)); do echo 'Só vou sair se você digitar 10!!!'; read i;
done
$ while ((i==10)); do echo 'Não saio enquanto você digitar 10!!!!';
read i; done

É importante notar que já que é possível utilizar variáveis tanto na forma i


quanto $i, é possível utilizar saídas de comandos como operandos das
operações:

$ if (( $(date +%Y) > 2000 ))


> then
> echo 'Você já está no século XXI!!!!!'
> else
> echo 'Você é antigo, hein...'
> fi

Temos também uma segunda forma de representar saídas de operações


aritméticas, em vez de $(( )), que é $[ ]. Exemplo:

$ echo "O ano que vem será $[ $(date +%Y) +1 ] "


2008

6 de 10 10-12-2010 22:47
Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

Utilizando booleanos

É sabido que o bash não têm "variáveis booleanas", como as utilizadas em


linguagens mais tradicionais. Isso não é exatamente uma limitação, pois ele
nos oferece recursos para implementarmos estes tipos, como mostrarei
adiante.

Mas basicamente, o que é uma variável booleana? É algo que diz de algo deu
ou não certo. E o bash já faz isso. Nós só vamos formalizar e deixar as coisas
mais simples para o uso cotidiano.

Primeiramente devo frisar que já existe, no unix, essa "implementação". São


dois programas chamados true e false. true retorna 0 e false, 1.

Mas se, repentinamente estes dois arquivos fossem deletados, como


recriá-los? Uma maneira é reescrevermos estes programas.

Mostro aqui como fazê-lo nas linguagens c e pascal, mas não é nem
necessário dizer que você poderia fazer o mesmo em qualquer outra
linguagem.

Em c:

// Programa true:
int main()
{
return 0;
}

// Programa false:
int main()
{
return 1;
}

Em pascal (fpc):

// Programa true:
program verdadeiro;
begin
halt(0);
end.

// Programa false:
program falso;
begin

7 de 10 10-12-2010 22:47
Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

halt(1);
end.

Pronto! Basta compilá-los, jogá-los num local qualquer da variável PATH e


beleza.

Mas como o negócio aqui é evitar buscar arquivos externos ao bash, vamos
fazer a mesma implementação em bash-script:

$ true()
>{
> return 0
>}

$ false()
>{
> return 1
>}

Básico, e mais rápido, por já estar na memória durante toda a execução do


script.

Para utilizar estes recursos em nossos scripts, utilizamos atribuições


normais:

$ bool1=true
$ boll2=false

$ if $bool1 || $bool2
> then
> echo Um dos dois é verdadeiro
> fi

Aí você pode inventar à vontade, utilizando expressões mais complexas:

$ while ( ! $bool1 && ( $bool2 || $bool3 )) || ( $bool4 || ! $bool5 ) ) &&


((i>50))
> do
> echo "eita negócio esquisito..."
> done

Isso é útil também quando você faz um script com "chaves liga-desliga". Não
precisa fazer comparação alguma. Basta um if com uma variável booleana, e
tudo está feito. Simples e rápido, lembrado a linguagem humana.

Já que estamos no embalo, vamos implementar uma função que devolve o

8 de 10 10-12-2010 22:47
Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

inverso do valor lógico de uma variável:

$ Not()
>{
> $1 && echo false || echo true
>}

$ variavel=false
$ for i in `seq 1 5`
> do
> echo $variavel
> variavel=`Not $variavel`
> done
false
true
false
true
false

Você pode, por exemplo, implementar uma função "Talvez", muito útil nos
dias de hoje:

$ Talvez()
>{
> return $(( RANDOM % 2 ))
>}

É tiro e queda. ;-)

Agradecimentos e referências

Me retiro por aqui, por enquanto.

Gostaria de agradecer à um usuário daqui do VOL que, através de um


comentário, me ajudou a escrever boa parte deste texto. Ele se chama Elgio
Schlemer, e pelo jeito sabe muito de bash-script.

Quero dizer também que isso não é tudo. O bash é mais complexo e
completo do que pensamos. Mas uma boa pesquisa resolve qualquer
problema.

Referências:

A infinita fonte de conhecimento que é a página de manual online do


bash.

9 de 10 10-12-2010 22:47
Recursos avançados do bash que você não aprend... http://www.vivaolinux.com.br/artigos/impressora....

http://www.gnu.org/software/bash/manual/bashref.html
Site do Júlio Neves: http://www.julioneves.com/
Site do Aurélio: http://www.aurelio.net/
Site do Thobias: http://thobias.org/
Google: http://www.google.com/
Muitos outros que precisariam de uma lista muito grande ;-)

Até a próxima.

http://www.vivaolinux.com.br/artigo/Recursos-avancados-do-bash-que-voce-
nao-aprende-na-escola

Voltar para o site

10 de 10 10-12-2010 22:47

You might also like