Este documento foi escrito para aqueles que querem aprender Bash sem mergulhar muito profundamente.
Dica: Dê uma olhada em learnyoubash — são exercícios interativos baseados nesse documento!
Você pode instalar esse documento usando npm
. Execute:
$ npm install -g bash-handbook
Você será capaz de executar bash-handbook
na sua linha de comando. Isso irá abrir o manual no $PAGER
que você configurou. De outra forma, você pode continuar lendo aqui.
O código fonte está disponível aqui: https://github.com/denysdovhan/bash-handbook
- Introdução
- Estilos do shell
- Comentários
- Variáveis
- Expansões do shell
- Arrays
- Streams, pipes e listas
- Operadores condicionais
- Loops
- Funções
- Depurando
- Posfácio
- Licença
Se você é um desenvolvedor, então você sabe o valor que o tempo tem. Otimizar seu processo de trabalho é um dos mais importantes aspectos do seu dia-a-dia.
E, se entrarmos no caminho em direção à eficiência e produtividade, sempre esbarramos em ações que serão repetidas uma vez ou outra, como:
- tirar um screenshot e fazer o upload para um servidor
- processar texto em vários formatos
- converter arquivos entre diferentes formatos
- analisar o resultado da execução de um programa
Entra em cena, o Bash, nosso salvador.
Bash é um shell Unix escrito por Brian Fox no formato de software livre para o projeto GNU, com a intenção de substituir o Bourne shell. Ele foi lançado em 1989 e tem sido distribuído como shell padrão no Linux e macOS a um longo tempo.
E porque nós precisamos aprender algo que foi escrito a mais de 30 anos? A resposta é simples: essa coisa, hoje em dia, é uma das mais poderosas e portáveis ferramentas para escrever scripts para todos os sitemas baseados em Unix. E isso é a razão pela qual você deve aprender bash. Ponto.
Nesse manual, eu vou descrever os conceitos mais importantes do bash através de exemplos. Eu espero que seja útil para você e que você possa aprender algo através deles.
O usuário do shell bash pode trabalhar em dois modos - interativo e não-interativo.
Se você estiver trabalhando no Ubuntu, você tem sete terminais virtuais disponíveis para você. O ambiente de trabalho se posiciona no sétimo terminal virtual. Você pode voltar para uma GUI mais amigável usando o atalho Ctrl-Alt-F7
.
Você pode abrir o shell usando o atalho Ctrl-Alt-F1
. Depois disso, a GUI que você acostuma utilizar irá desaparecer e um dos terminais virtuais será mostrado.
Se você ver algo parecido com isso, então, você está trabalhando no modo interativo:
user@host:~$
Aqui você pode digitar uma variedade de comandos Unix, como ls
, grep
, cd
, mkdir
, rm
e ver o resultado das suas execuções.
Chamamos isso de shell interativo porque ele interage diretamente com o usuário.
Usar um terminal virtual, nem sempre é conveniente. Por exemplo, se você quiser editar um documento e executar um comando ao mesmo tempo, é melhor você usar um emulador de terminais virtuais, como:
No modo não-interativo, o shell recebe comandos de um arquivo ou um pipe e executa eles. Quando o interpretador chega no final do arquivo, a sessão de processamento do shell é terminada e o processo anterior é retornado.
Use os seguintes comandos para executar o shell em modo não-interativo:
. /path/to/script.sh
bash /path/to/script.sh
No exemplo acima, script.sh
é apenas um arquivo de texto comum, contendo comandos, que o interpretador shell pode executar. sh
ou bash
são interpretadores utilizados pelo shell. Você pode criar um script.sh
usando seu editor de texto preferido (e.g. vim, nano, Sublime Text, Atom, etc).
Você também pode simplificar a invocação do script transformando o arquivo em um executável usando o comando chmod
:
chmod +x /path/to/script.sh
Além disso, a primeira linha do script deve indicar qual programa deve ser usado para executar o arquivo, como:
#!/bin/bash
echo "Hello, world!"
Ou, se você preferir usar sh
ao invés do bash
, mude #!/bin/bash
para #!/bin/sh
. Essa sequência de carácteres #!
, é conhecida como shebang. Agora você pode executar scripts da seguinte maneira:
/path/to/script.sh
Um truque útil que usamos acima, é usar o comando echo
para imprimir o texto na tela do terminal.
Uma outra maneira de usar o shebang é:
#!/usr/bin/env bash
echo "Hello, world!"
A vantagem desse modo de uso do shebang é que ele irá utilizar o programa (nesse caso o bash
) baseado no caminho PATH
do seu ambiente. Esse modo é, muitas vezes, preferido, ao invés de usar o primeiro método mostrado acima, onde a localização do programa no seu ambiente, pode não ser a mesmo. Isso também é útil se a variável PATH
, em um sistema, estiver configurada para uma versão diferente do programa. Um exemplo, seria a instalação de uma nova versão do bash
, enquanto preservamos a versão original e inserimos a localização da nova versão na variável PATH
do sistema. O uso do #!/bin/bash
pode resultar no uso da versão original do bash
, enquanto, #!/usr/bin/env bash
, fará uso da nova versão.
Todo comando retorna um código de sáida (retornando o estado ou o estado de saída). Um comando executado com sucesso, sempre retorna 0
(código-zero), e um comando executado com falha, sempre retorna um valor não-zero (código de erro). Códigos de falhas devem conter um número inteiro positivo entre 1 e 255.
Outro comando útil que nós podemos usar quando escrevemos scripts é o exit
. Esse omando é usado para finalizar a execução atual e retornar um código de saída para o shell. Executando o exit
, sem nenhum argumento, irá terminar o script que está em processamento e retornar o código de saída do último comando executado antes do exit
.
Quando um programa é finalizado, o shell atribui ao seu código de saída há variável $?
. A variável $?
, é o que normalmente usamos para testar se um script foi executado com sucesso ou não.
Do mesmo modo que podemos usar exit
para terminar um script, nós podemos usar o comando return
para sair de uma função e retornar o código de saída para quem invocou essa função. Você também pode usar exit
dentro de uma função, isso irá resultar na saída da função e na finalização do programa.
Scripts podem conter comentários. Comentários são declarações especiais ignoradas pelo interpretador do shell
. O início de um comentário deve conter o símbolo #
e continuar até o final da linha.
Por exemplo:
#!/bin/bash
# Esse script irá imprimir seu nome de usuário.
whoami
Dica: Use comentários para explicar o que seu script faz e porque.
Como na maioria das linguages de programação, você pode criar variáveis no bash.
Bash não conhece nenhum tipo de dados. Variáveis podem conter apenas números ou strings. Existem três tipos de variáveis que você pode criar: variáveis locais, variáveis de ambiente e variáveis de parâmetros posicionados.
Variáveis locais são variáveis que existem apenas no contido script. Elas são inacessíveis para outros programas ou scripts.
Uma variável local pode ser declarada usando o sinal =
(como regra, não deve conter nenhum espaço entre o nome da variável, =
e o seu valor) e seu valor pode ser acessado usando o sinal $
. Por exemplo:
username="oieduardorabelo" # declarando a variável
echo $username # imprimindo seu valor
unset username # deletando a variável
Nós podemos declarar uma variável local para uma única função usando a declaração local
. Com isso, a variável será automaticamente deletada quando a função terminar de ser executada.
local local_var="Sou uma variável local"
Variáveis de ambiente são variáveis que podem ser acessadas por qualquer programa ou script sendo executado na sessão atual do shell. Elas são criadas como variáveis locais, mas usando a declaração export
no início delas.
export GLOBAL_VAR="Sou uma variável global"
Existem muitas variáveis globais no bash. Você vai conhecer elas no decorrer do seu dia-a-dia, mas aqui você encontra uma tabela com as mais utilizadas:
Variáveis | Descrição |
---|---|
$HOME |
O diretório inicial do usuário atual. |
$PATH |
Uma lista separada por dois pontos [:] dos diretários que o shell irá procurar por comandos. |
$PWD |
O diretório atual. |
$RANDOM |
Número inteiro randômico entre 0 e 32767. |
$UID |
Versão numérica do ID do usuário atual. |
$PS1 |
Sequência primária do seu prompt de comando. |
$PS2 |
Sequência secundária do seu prompt de comando. |
Entre nesse link para ver uma lista extendida de variáveis de ambiente do Bash.
Parâmetros de posição são variáveis alocadas aos parâmetros de uma função quando ela é executada. A seguinte tabela mostra os parâmetros de posição e outras variáveis especiais e quais os seus significados dentro da função.
Parâmetro | Descrição |
---|---|
$0 |
Nome do script. |
$1 … $9 |
Os parâmetros passados de 1 há 9. |
${10} … ${N} |
Os parâmetros passados de 10 há N. |
$* or $@ |
Todos os parâmetros passados, exceto $0 . |
$# |
A soma da quantidade de parâmetros foi passada, não contando $0 . |
$FUNCNAME |
O nome da função (retornada como valor, apenas dentro da função). |
No exemplo abaixo, os parâmetros posicinais serão $0='./script.sh'
, $1='foo'
e $2='bar'
:
./script.sh foo bar
Variáveis também podem ter um valor padrão. Nós podemos definir isso usando a sintaxe:
# se a variável estiver vazia, atribua o valor padrão
: ${VAR:='default'}
: ${$1:='first'}
# ou
FOO=${FOO:-'default'}
Expansões são realizadas na linha de comando após ela ser separada em símbolos. Em outras palavras, expansões são mecânismos para calcular operações aritméticas, salvar resultados de execuções de comandos e assim por diante.
Se você estiver interessado, você pode ler mais sobre expansões do shell.
Expansões de suporte nos permite criar strings arbitrárias. É parecido com expansão de nomes de arquivos. Por exemplo:
echo bat{i,a,u}ta # batita batata batuta
Expansões também podem ser usadas para criar extensões numéricas, que podem ser iterados em um loop.
echo {0..5} # 0 1 2 3 4 5
echo {00..8..2} # 00 02 04 06 08
Substituição de comandos nos permite avaliar um comando e substituir seus valores em outro comando ou atribuição de variável. Substituição de comandos é realizado quando um comando é anexado por ``
ou $()
. Por exemplo, podemos usar isso da seguinte maneira:
now=`date +%T` # horário atual
# ou
now=$(date +%T) # horário atual
echo $now # 19:08:26
No bash, somos livres para fazer qualquer operação aritmética. Mas, expressões devem ser anexadas por $(( ))
. O formato da operação aritmética é:
result=$(( ((10 + 5*3) - 7) / 2 ))
echo $result # 9
Dentro de expressões aritméticas, variáveis geralmente deverm ser usadas sem o prefixo $
:
x=4
y=7
echo $(( x + y )) # 11
echo $(( ++x + y++ )) # 12
echo $(( x + y )) # 13
Existe uma importante diferença entre aspas simples e duplas. Dentro das aspas duplas, variáveis ou comandos podem ser expandidos. Dentro de aspas simples não. Por exemplo:
echo "Seu diretório inicial: $HOME" # Seu diretório inicial: /Users/<username>
echo 'Seu diretório inicial: $HOME' # Seu diretório inicial: $HOME
Tome cuidado ao expandir variáveis locais ou de ambiente dentro de aspas se eles contiverem espaços em branco. Um exemplo disso, considere o uso do echo
para imprimir algo:
INPUT="Uma frase com estranhos espaços em branco."
echo $INPUT # Uma frase com estranhos espaços em branco.
echo "$INPUT" # Uma frase com estranhos espaços em branco.
O primeiro echo
será invocado com 7 argumentos separados - $INPUT é separado em cada palavra, echo
imprimi um único espaço em branco entre cada palavra. No segundo caso, echo
é invocado com um único argumento (todo o valor do $INPUT, includingo seus espaços em branco).
Agora, considere um exemplo mais sério:
FILE="Minhas coisas favoritas.txt"
cat $FILE # tentará imprimir 3 arquivos: `Minhas`, `coisas` e `favoritas.txt`
cat "$FILE" # imprimi 1 arquivo: `Minhas coisas favoritas.txt`
Enquanto o problema desse exemplo pode ser resolvido apenas renomeando FILE para Minhas-coisas-favoritas.txt
, considere a entrada do nome vindo de uma variável de ambiente, um parâmetro posicional ou o resultado de outro comando (find
, cat
, etc). Se a entrada puder conter espaços em branco, tome o cuidado de envolver a expansão em aspas.
Como em qualquer outra linguagem de programação, um array no bash é uma variável que permite o armazenamento de múltiplos valores. No bash, arrays também são de base zero, ou seja, o primeiro elemento do array tem o íncide 0.
Ao lidar com arrays, nós devemos tomar um cuidado especial com as variáveis de ambiente IFS
. IFS, que significa Input Field Separator, em português, algo como, Separador dos campos de entrada, são os carácteres que separam os elementos dentro de um array. O valor padrão desses campos é um espaço em braco, IFS=' '
.
Para criar um array no bash, você pode simplesmente atribuir o valor ao index da variável do array:
frutas[0]=Maça
frutas[1]=Pera
frutas[2]=Banana
As variáveis de arrays também podem ser criadas a partir de uma atribuição composta, como:
frutas=(Maça Pera Banana)
Elementos individuais do array, são igualmente expansíveis como qualquer outra variável:
echo ${frutas[1]} # Pera
Todo o array pode ser expansível usando *
ou @
no lugar do índice numérico:
echo ${frutas[*]} # Maça Pera Banana
echo ${frutas[@]} # Maça Pera Banana
Tem uma importante (e súbita) diferença entre as duas linhas acima: considere que um elemento do array tenha espaços em branco:
fruta[0]=Maça
fruta[1]="Mamão papaia"
fruta[2]=Banana
Nós queremos imprimir cada elemento do array separadamente em uma nova linha, então, vamos tentar usar a função nativa printf
:
printf "+ %s\n" ${frutas[*]}
# + Maça
# + Mamão
# + papaia
# + Banana
Porque o Mamão
e papaia
foram imprimidos em linhas separadas? Vamos tentar usando aspas:
printf "+ %s\n" "${frutas[*]}"
# + Maça Mamão papaia Banana
Agora, está tudo em uma linha só - isso não exatamente o que queremos! É aí que ${frutas[@]}
entra no jogo:
printf "+ %s\n" "${frutas[@]}"
# + Maça
# + Mamão papaia
# + Banana
Dentro das aspas duplas, ${frutas[@]}
é expandido separadamente para cada elemento do array, com seus espaços em branco preservados.
Além disso, você pode extrair um pedaço do array usando os operadores:
echo ${frutas[@]:0:2} # Maça Mamão papaia
No exemplo acima, ${frutas[@]}
é expandido com todo o conteúdo do seu array, e :0:2
, extraí o pedaço de tamanho 2, começando no índice 0.
Adicionar elementos no array é bem simples. Atribuições compostas são extremamente úteis nesse caso. Você pode fazer uso dessa maneira:
frutas=(Laranja "${frutas[@]}" Melão Ameixa)
echo ${frutas[@]} # Laranja Maça Mamão papaia Banana Melão Ameixa
No exemplo acima, ${frutas[@]}
é expandido com todo o conteúdo do seu array e é atribuido ao novo valor dentro do array frutas
, sendo assim, mutando seu valor original.
Para deletar um elemento de um array, use o comando unset
:
unset frutas[0] # Deleta o item Laranja
echo ${frutas[@]} # Maça Mamão papaia Banana Melão Ameixa
Bash tem uma poderosa ferramente para trabalhar com outros programas e seus resultados. Usando streams nós podemos enviar o resultado de um programa para outro programa ou arquivo, e assim, gravar logs ou fazer qualquer coisa que quisermos.
Pipes te dá a oportunidade de transportar e controlar a execução de comandos.
É fundamental o entendimento de como usar essa poderosa e sofisticada ferramenta do Bash.
Ao executar qualquer comando no Bash, ele recebe esses dados como parâmetros e envia uma sequência ou streams de caracteres. Esses streams podem ser redirecionados em arquivos ou em outro stream.
Existem três tipos de saídas de dados, conhecidos como descritores:
Código | Descritor | Descrição |
---|---|---|
0 |
stdin |
O padrão de entrada de dados. |
1 |
stdout |
O padrão de saída de dados. |
2 |
stderr |
O padrão de saída de erros. |
Redirecionamento torna possível o controle de onde a saída do comando vai parar, e, de onde a entrada de dados veem. Para redirecionar streams, você pode usar esses operadores:
Operadores | Descrição |
---|---|
> |
Redireciona a saída de dados |
&> |
Redireciona a saída de dados e de erros |
&>> |
Anexa o redirecionamento de saída e erros |
< |
Redireciona a entrada de dados |
<< |
Sintaxe do comando "Here documents" |
<<< |
Sintaxe do comando "Here strings" |
Veja aqui alguns exemplos de redirecionamento:
# a saída do comando `ls` será escrita no arquivo lista.txt
ls -l > lista.txt
# adiciona a saída do comando no final do arquivo lista.txt
ls -a >> lista.txt
# todos os erros serão escritos no arquivo erros.txt
grep da * 2> erros.txt
# lê o arquivo erros.txt
less < errors.txt
Nós podemos redirecionar os streams padrões não apenas para arquivos, mas também, para outros programas. Pipes nos permite usar a saída de um programa, como entrada de outro.
No exemplo abaixo, comando1
envia sua saída para comando2
, que então passa sua saída como entrada para comando3
:
comando1 | comando2 | comando3
Construções como essa, são chamadas de pipelines.
Na prática, isso pode ser usado para processar dados através de vários programas. Por exemplo, no exemplo a seguir, a saída do ls -l
é enviada para o comando grep
, que então imprimi apenas os arquivos que tenham a extensão .md
, e sua saída, é finalmente enviada para o comando less
:
ls -l | grep .md$ | less
Uma lista de comandos é uma sequência de um ou mais pipelines separados pelos operadores ;
, &
, &&
ou ||
.
Se um comando termina com um operador &
, o shell executará o comando asíncronamente através de um subshell. Em outras palavras, esse comando será executado em segundo plano (ou background).
Comandos separados por ;
serão executados em sequência: um após o outro. O shell esperada a finalização de cada comando para executar o próximo.
# comando1 será executado após a finalização do comando1
comando1 ; comando2
# que é o mesmo que
command1
command2
Uma lista separada por &&
e ||
são conhecidos também como listas AND e OR,
Uma lista AND é parecida com isso:
# comando2 será executado se, e apenas se, o comando1 finalize seu processo com sucesso (retorando um estado de saída 0)
comando1 && comando2
Uma lista OR é parecida com isso:
# comando2 será executado se, e apenas se, o comando1 não finalize seu processo com sucesso (retornando um estado de saída não-zero)
comando1 || comando2
O código retornado pelas listas AND ou OR, são o estado do último comando executado.
Como em qualquer outra linguagem, as condicionais no Bash nos permitem decidir qual ação realizar. O resultado é determinado pela análise da expressão, que deverá ser conter [[ ]]
em volta dela.
Expressões condicionais podem conter os operadores &&
e ||
, como vimos, AND e OR. Além disso, existem várias outras expressões que podem ser utilizadas.
Existem duas condicionais diferentes: a condicional if
, e a condicional case
.
Expressões dentro do [[ ]]
(ou [ ]
para sh
), são chamados de comandos de teste ou primários. Essas expressões ajudam a indicar o resultado de uma operação condicional. Nas tabelas abaixos, estamos usando [ ]
, porque ele também funciona para sh
. Para saber mais, veja aqui a diferença entre aspas simples e aspas duplas dentro dos colchetes no Bash..
Trabalhando com o sistema de arquivos:
Primários | Quer dizer |
---|---|
[ -e FILE ] |
true se FILE existir, do inglês exists. |
[ -f FILE ] |
true se FILE existir e for um arquivo normal, do inglês file. |
[ -d FILE ] |
true se FILE existir e for executável, do inglês directory. |
[ -s FILE ] |
true se FILE existir e não for vazio, seu tamanho é maior que 0, do inglês size. |
[ -r FILE ] |
true se FILE existir e for possível a leitura, do inglês readable. |
[ -w FILE ] |
true se FILE existir e for possível a escrita, do inglês writable. |
[ -x FILE ] |
true se FILE existir e for possíve executá-lo, do inglês executable. |
[ -L FILE ] |
true se FILE existir e for um link simbólico, do inglês symbolic link. |
[ FILE1 -nt FILE2 ] |
FILE1 é mais novo que FILE2, do inglês newer than. |
[ FILE1 -ot FILE2 ] |
FILE1 é mais velho que FILE2, do inglês older than. |
Trabalhando com strings:
Primários | Quer dizer |
---|---|
[ -z STR ] |
STR é vazio, seu tamanho é zero, do inglês zero. |
[ -n STR ] |
STR não é vazio, seu tamanho não é zero, do inglês non-zero. |
[ STR1 == STR2 ] |
STR1 e STR2 são iguais. |
[ STR1 != STR2 ] |
STR1 e STR2 não são iguais. |
Operadores aritiméticos binários:
Primários | Quer dizer |
---|---|
[ ARG1 -eq ARG2 ] |
ARG1 é igual ao ARG2 , do inglês equal. |
[ ARG1 -ne ARG2 ] |
ARG1 não é igual ao ARG2 , do inglês not equal. |
[ ARG1 -lt ARG2 ] |
ARG1 é menor que ARG2 , do inglês less than. |
[ ARG1 -le ARG2 ] |
ARG1 é menor ou igual que ARG2 , do inglês less than or equal. |
[ ARG1 -gt ARG2 ] |
ARG1 é maior que ARG2 , do inglês greater than. |
[ ARG1 -ge ARG2 ] |
ARG1 é maior ou igual que ARG2 greater than or equal. |
Condicionais podem ser combinadas usando as expressões de combinação:
Expressão | Efeito |
---|---|
[ ! EXPR ] |
true se EXPR é falso. |
[ (EXPR) ] |
Retorna o valor da EXPR . |
[ EXPR1 -a EXPR2 ] |
Operador lógico AND. true se EXPR1 e EXPR2 são verdadeiros, do inglês and. |
[ EXPR1 -o EXPR2 ] |
Operador lógico OR. true se EXPR1 ou EXPR2 são verdadeiros, do inglês or. |
Com certeza existem muitos outros comandos e expressões úteis para seu caso, você fácilmente encontra-los na página de manual do Bash.
Declarações if
funcionam da mesma maneira como em outras linguagens de programação. Se a expressão dentro dos colchetes for verdadeira, o codógio dentro do bloco then
e até o fi
será executado. fi
indica o final de uma condicional a ser executada.
# única linha
if [[ 1 -eq 1 ]]; then echo "true"; fi
# múltipla linha
if [[ 1 -eq 1 ]]; then
echo "true"
fi
Da mesma forma, podemos usar uma declaração if..else
, como:
# única linha
if [[ 2 -ne 1 ]]; then echo "true"; else echo "false"; fi
# múltipla linha
if [[ 2 -ne 1 ]]; then
echo "true"
else
echo "false"
fi
As vezes, condicionais if..else
não são suficientes para o que queremos fazer. Nesse caso, não devemos esquecer da existência da condicional if..elif..else
, que sempre vêm a calhar.
Veja o exemplo abaixo:
if [[ `uname` == "Adão" ]]; then
echo "Não coma a maça!"
elif [[ `uname` == "Eva" ]]; then
echo "Não pegue a maça!"
else
echo "Maças são deliciosas!"
fi
Se você estiver analisando várias possibilidades diferentes para ter ações diferentes, usar a condicional case
pode ser mais útil do que várias condicionais if
aninhadas. Veja abaixo um exemplo complexo de usando a condicional case
:
case "$ext" in
"jpg"|"jpeg")
echo "É uma imagem com extensão jpg"
;;
"png")
echo "É uma imagem com extensão png"
;;
"gif")
echo "É uma imagem com extensão gif"
;;
*)
echo "Oops! Não é uma imagem!"
;;
esac
A condicional case
verifica a expressão que corresponde a um padrão. O sinal |
é usado para separar múltiplos padrões e o operador )
finaliza a lista de padrões. A expressão *
é o padrão para todo o restante que não corresponder a nenhum item das suas listas. Cada bloco de comandos deve ser separado pelo operador ;;
.
Aqui não teremos nenhuma surpresa. Assim como qualquer linguagem de programação, um loop no bash é um bloco de código que se repete enquanto a condição em controle for verdadeira.
Existem quatro tipos de loops no Bash: for
, while
, until
e select
.
O for
é bem similar ao seu irmão em C. Ele se parece com:
for arg in elem1 elem2 ... elemN
do
# código
done
Durante cada etapa do loop, arg
assume os valores de elem1
até elemN
. Valores também podem ser espaços reservados ou expansões de suporte.
E também podemos escrever o loop for
em apenas uma linha, mas nesse caso, é preciso colocar um ponto e vírgula antes do do
, como no exemplo:
for i in {1..5}; do echo $i; done
A propósito, se for..in..do
parece um pouco estranho para você, você também pode escrever o for
em estilo C, como a seguir:
for (( i = 0; i < 10; i++ )); do
echo $i
done
for
é útil quando nós queremos fazer a mesma operação em cada arquivo em um diretório. Por exemplo, se precisamos mover todos os arquivos .bash
dentro da pasta script
e dar aos arquivos permissões de execução, nosso script será parecido com isso:
#!/bin/bash
for FILE in $HOME/*.bash; do
mv "$FILE" "${HOME}/scripts"
chmod +x "${HOME}/scripts/${FILE}"
done
O loop while
testa uma condição e executa a sequência de comandos desde que a condição seja verdadeira. A condição não é nada mais que uma expressão primária usada também em if..then
. Então, um loop while
se parece com:
while [[ condition ]]
do
# código
done
Tal como no caso do loop for
, se quisermos escrever uma condição do
na mesma linha, temos que usar um ponto e vírgula antes.
Um exemplo prático seria:
#!/bin/bash
# Retorna o quadrado dos números de 0 à 9
x=0
while [[ $x -lt 10 ]]; do # valor de x é menor que 10
echo $(( x * x ))
x=$(( x + 1 )) # aumenta o x
done
O loop until
é exatamente o oposto do loop while
. Assim como o while
, ele recebe uma condição teste, mas, só continua executando enquanto a condição for falsa:
until [[ cond ]]; do
# código
done
O loop select
nos ajuda a organizar um menu para o usuário. Ele tem quase a mesma sintaxe que o loop for
:
select respostas in elem1 elem2 ... elemN
do
# código
done
O select
imprimi todos os elem1..elemN
na tela, junto de suas sequências numéricas, e depois disso, pergunta ao usuário. Normalmente, isso se parece com $?
(a variável PS3
). A resposta será salva em respotas
. Se respostas
for um número entre 1..N
, então o código será executado e select
vai para a próxima iteração - isso porquê nós devemos usar a declaração break
.
Um exemplo prático se parece com esse:
#!/bin/bash
PS3="Escolha uma gerenciador de pacotes: "
select ITEM in bower npm gem pip
do
echo -n "Digite o nome de um de pacote: " && read PACKAGE
case $ITEM in
bower) bower install $PACKAGE ;;
npm) npm install $PACKAGE ;;
gem) gem install $PACKAGE ;;
pip) pip install $PACKAGE ;;
esac
break # evita loops infinitos
done
Esse example pergunta ao usuário qual gerenciador de pacote ele deseja usar. E em seguida, quais pacotes gostaríamos de instalar e finalmente, executa o processo de instalação.
Se rodarmos isso, teremos:
$ ./my_script
1) bower
2) npm
3) gem
4) pip
Escolha uma gerenciador de pacotes: 2
Digite o nome de um de pacote: bash-handbook
<installing bash-handbook>
Existem situações onde precisamos parar o loop antes da sua finalização normal ou pular uma iteração. Nesses casos, nós podemos usar as declarações break
e continue
, que são nativas do shell. Ambos funcionam com qualquer tipo de loop.
There are situations when we need to stop a loop before its normal ending or step over an iteration. In these cases, we can use the shell built-in break
and continue
statements. Both of these work with every kind of loop.
A declaração break
é usada para sair do loop atual antes da sua finalização. Nós já o conhecemos.
The break
statement is used to exit the current loop before its ending. We have already met with it.
A delcaração continue
pula uma iteração. Podemos usa-la desse modo:
The continue
statement steps over one iteration. We can use it as such:
for (( i = 0; i < 10; i++ )); do
if [[ $(( i % 2 )) -eq 0 ]]; then continue; fi
echo $i
done
Se você rodar o exemplo acima, ele vai imprimir os números ímpares de 0 à 9. If we run the example above, it will print all odd numbers from 0 through 9.
Em scripts, nós temos a habilidade de definir e chamar funções. Assim como em qualquer linguagem de programação, funções no bash são pedaços de códigos, mas elas são tratadas um pouquinho diferentes.
No bash, funções são sequências de comandos agrupados sob um mesmo nome, e esse nome, é o nome da função. Chamar uma função é o mesmo que chamar qualquer outro programa, você escreve o nome da função e ela será invocada.
Podemos declarar funções dessa maneira:
my_func () {
# código
}
my_func # invoca função
Devemos declarar a função antes de invoca-la.
Funções podem receber argumentos e retornar um resultado - o código de saída. Argumentos, em funções, são tratados da mesma maneira que os argumentos dados ao script no modo não-interativo - usando os parâmetros de posição. O resultado pode ser retornado usando o comando return
.
Abaixo é uma função que recebe um nome e retorna 0
, indicando que foi executado com sucesso.
# function with params
bemVindo () {
if [[ -n $1 ]]; then
echo "Bem-vindo, $1!"
else
echo "Bem-vindo, desconhecido!"
fi
return 0
}
bemVindo Eduardo # Hello, Eduardo!
bemVindo # Hello, desconhecido!
Nós já falamos sobre códigos de saída. O comando return
sem argumentos retorna o código de saída do último comando executado. Acima, return 0
vai retornar o código bem sucedido, 0
.
O shell nós dá ferramentas para depurar nossos scripts. Se você quer rodar um script em modo de depuração, nós usamos um modo especial em nosso shebang:
#!/bin/bash options
Esse options
é a configuração que muda o comportamento do shell. A tabela abaixo mostra uma lista de opções que podem ser úteis para você:
Atalho | Nome | Descrição |
---|---|---|
-f |
noglob | Desativa expanção de nome de arquivos, em inglês, globbing. |
-i |
interactive | Script roda no modo interativo. |
-n |
noexec | Lê comandos, mas não os executa (verifica a sintaxe). |
-t |
— | Saí da execução depois do primeiro comando. |
-v |
verbose | Imprimi cada comando no stderr antes de executa-los. |
-x |
xtrace | Imprimi cada comando e expande seus argumentos e envia para o stderr antes de executa-los. |
Por exemplo, podemos ter scripts com -x
como opção, assim como:
#!/bin/bash -x
for (( i = 0; i < 3; i++ )); do
echo $i
done
Isso vai imprimir o valor das variáveis para o stdout
junto de outras informações úteis:
$ ./my_script
+ (( i = 0 ))
+ (( i < 3 ))
+ echo 0
0
+ (( i++ ))
+ (( i < 3 ))
+ echo 1
1
+ (( i++ ))
+ (( i < 3 ))
+ echo 2
2
+ (( i++ ))
+ (( i < 3 ))
As vezes nós precisamos depurar uma parte do script. Nesse caso, usar o comando set
é mais conveniente. Esse comando habilita e desabilita opções. Opções são desabilitadas usando -
e habilitadas usando +
:
#!/bin/bash
echo "xtrace está desabilitado"
set -x
echo "xtrace está habilitado"
set +x
echo "xtrace foi desabilitado novamente"
Eu espero que esse pequeno guia tenha sido interação e tenha te ajudado a entender um pouco mais sobre o Bash. Para ser honesto, eu escrevi esse guia para mim mesmo, para assim, não esquecer o básico do bash. Eu tentei escrever de uma maneira concisa, mas significativamente útil e eu espero que você tenha gostado.
Esse guia narra minha própria experiência com o Bash. Ele não tem foco de abranger toda as funcionalidades, e, se você quiser saber mais, pode começar através do man bash
.
Contribuições são absolutamente bem-vindas, e eu ficarei grato por qualquer correção ou perguntas que você vier a ter e me enviar. Para isso, crie uma nova questão aqui.
Obrigado por ler esse guia de bolso!
Aqui tem uma lista de outros materiais, todos em inglês, sobre o Bash:
- Bash man page. In many environments that you can run Bash, the help system
man
can display information about Bash, by running the commandman bash
. For more information on theman
command, see the web page "The man Command" hosted at The Linux Information Project. - "Bourne-Again SHell manual" in many formats, including HTML, Info, TeX, PDF, and Texinfo. Hosted at https://www.gnu.org/. As of 2016/01, this covers version 4.3, last updated 2015/02/02.
- Bash 3.2 Man page hosted at Apple's Mac Developer Library site. As of 2016/01, this covers version 3.2, last updated 2006/09/28.