Linux Bash Handbook
Linux Bash Handbook
Índice
Introdução
Estilos do shell
Interativo
Não-interativo
Códigos de saída
Comentários
Variáveis
Variáveis locais
Variáveis de ambiente
Parâmetros de posição
Expansões do shell
Expansões de suporte
Substituição de comandos
Expansões aritiméticas
Arrays
Declarando array
Expansões de Array
Separando Array
Streams
Pipes
Lista de comandos
Operadores condicionais
Usando a condicional if
Loops
for loop
while loop
until loop
select loop
Controlando o loop
Funções
Depurando
Posfácio
Licença
Introdução
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.
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.
Estilos do shell
O usuário do shell bash pode trabalhar em dois modos - interativo e não-interativo.
Modo 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
Ctrl-Alt-F7.
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,
ls grep,
grep cd, mkdir rm e ver
cd mkdir,
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:
GNOME Terminal
Terminator
iTerm2
ConEmu
Modo não-interativo
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.
. /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).
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!"
/path/to/script.sh
Um truque útil que usamos acima, é usar o comando echo para imprimir o texto na tela do
terminal.
A vantagem desse modo de uso do shebang é que ele irá utilizar o programa (nesse caso o bash
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,
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
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
bash,
enquanto, #!/usr/bin/env bash,
bash fará uso da nova versão.
Códigos de saída
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
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
exit
código de saída do último comando executado antes do exit
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.
Comentários
Scripts podem conter comentários. Comentários são declarações especiais ignoradas pelo
interpretador do shell
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.
Variáveis
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
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:
Nós podemos declarar uma variável local para uma única função usando a declaração local
local.
Com isso, a variável será automaticamente deletada quando a função terminar de ser executada.
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
Uma lista separada por dois pontos [:] dos diretários que o shell irá procurar por
$PATH
comandos.
Entre nesse link para ver uma lista extendida de variáveis de ambiente do Bash.
Parâmetros de posição
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.
Variáveis também podem ter um valor padrão. Nós podemos definir isso usando a sintaxe:
Expansões do shell
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
Expansões de suporte nos permite criar strings arbitrárias. É parecido com expansão de nomes de
arquivos. Por exemplo:
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
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:
Expansões aritiméticas
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 é:
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
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:
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).
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
coisas-favoritas.txt
parâmetro posicional ou o resultado de outro comando ( find cat, etc). Se a entrada puder
find, cat
conter espaços em branco, tome o cuidado de envolver a expansão em aspas.
Arrays
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.
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=' '.
'
Declarando array
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:
Expansões de Array
Elementos individuais do array, são igualmente expansíveis como qualquer outra variável:
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
Porque o Mamão e papaia foram imprimidos em linhas separadas? Vamos tentar usando aspas:
Agora, está tudo em uma linha só - isso não exatamente o que queremos! É aí que ${frutas[@]}
entra no jogo:
Dentro das aspas duplas, ${frutas[@]} é expandido separadamente para cada elemento do array,
com seus espaços em branco preservados.
Separando Array
Além disso, você pode extrair um pedaço do array usando os operadores:
No exemplo acima, ${frutas[@]} é expandido com todo o conteúdo do seu array, e :0:2
:0:2, extraí
o pedaço de tamanho 2, começando no índice 0.
Adicionando elementos no Array
Adicionar elementos no array é bem simples. Atribuições compostas são extremamente úteis
nesse caso. Você pode fazer uso dessa maneira:
No exemplo acima, ${frutas[@]} é expandido com todo o conteúdo do seu array e é atribuido
ao novo valor dentro do array frutas,
frutas sendo assim, mutando seu valor original.
Streams
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.
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
# lê o arquivo erros.txt
less < errors.txt
Pipes
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.
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
grep, que então imprimi
apenas os arquivos que tenham a extensão .md,
.md e sua saída, é finalmente enviada para o comando
less:
less
Lista de comandos
Uma lista de comandos é uma sequência de um ou mais pipelines separados pelos operadores ; ,
& , && ou ||.
||
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.
# 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.
Operadores condicionais
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.
true se FILE existir e não for vazio, seu tamanho é maior que 0, do
[ -s FILE ]
inglês size.
[ -L FILE ] true se FILE existir e for um link simbólico, do inglês symbolic link.
[ -n STR ] STR não é vazio, seu tamanho não é zero, do inglês non-zero.
[ ARG1 -ge ARG2 ] ARG1 é maior ou igual que ARG2 greater than or equal.
Expressão Efeito
[ 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.
Usando a condicional if
# múltipla linha
if [[ 1 -eq 1 ]]; then
echo "true"
fi
# ú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
if..elif..else, que sempre vêm a calhar.
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
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 ;;.
;;
Loops
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.
for loop
Durante cada etapa do loop, arg assume os valores de elem1 até elemN
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,
do como no exemplo:
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 é ú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
while loop
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:
if..then
while [[ condition ]]
do
# código
done
until loop
select loop
O loop select nos ajuda a organizar um menu para o usuário. Ele tem quase a mesma sintaxe
que o loop for:
for
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
PS3). A resposta será
salva em respotas
respotas. Se respostas for um número entre 1..N
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
break.
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.
$ ./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>
Controlando o loop
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
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:
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.
Funções
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.
my_func () {
# código
}
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
}
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 .
Depurando
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ê:
Por exemplo, podemos ter scripts com -x como opção, assim como:
#!/bin/bash -x
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
Posfácio
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.
bash
Licença
Esta apostila incrível foi escrita originalmente pelo © Denys Dovhan e está licenciada nos termos
License CC BY 4.0
(CC 4.0) O que permitiu com que nós traduzissemos, adaptássemos e
redistribuissemos esse conhecimento! Nosso eternos agradecimento a ele!