Uma shell é um programa que te permite introduzir comandos para interagir com o sistema operativo. As shells podem ser gráficas; o ambiente de trabalho que usas com o rato ou o trackpad é uma shell. Vamos concentrar-nos nas interfaces de linha de comandos, onde escreves comandos e os resultados são (normalmente) impressos no teu terminal.
Há muitas shells de linha de comandos diferentes. O Windows oferece o cmd e o powershell. O Mac e o Linux oferecem «shells Unix», das quais há muitas. A shell predefinida do Mac é o zsh. O csh é uma shell antiga com uma sintaxe semelhante à do C. O bash é uma das shells mais populares, sobretudo no Linux.
Uma das primeiras shells de sempre é a Bourne shell, o sh. Com o tempo, esta shell foi normalizada para ser disponibilizada em todos os sistemas do tipo Unix. Muitas shells baseiam-se na Bourne shell e acrescentam frequentemente funcionalidades interativas ou de programação.
O Bash baseia-se na Bourne shell. É por isso que se chama «Bourne-again shell». É a shell predefinida em muitos sistemas Linux. O Bash oferece muitas funcionalidades que o tornam uma shell interativa agradável, incluindo:
Este percurso não é sobre como usar o bash como shell interativa. É sobre aprender a escrever programas com o bash. A ênfase vai estar em usar apenas o bash para resolver problemas, embora não haja restrições quanto a recorrer a ferramentas externas.
O Bash lê comandos a partir da sua entrada (que é normalmente um terminal ou um ficheiro). Cada linha de entrada que lê é tratada como um comando: uma instrução a executar. (Há alguns casos avançados, como comandos que ocupam várias linhas, que abordaremos mais adiante.)
O Bash divide cada linha em palavras delimitadas por um espaço em branco (espaços e tabulações). A primeira palavra da linha é o nome do comando a executar. Todas as restantes palavras tornam-se argumentos desse comando (opções, nomes de ficheiros, etc.).
Nos exemplos abaixo, vamos mostrar-te alguns comandos interativos que podes executar.
# inicia um comentário.
Qualquer palavra que se siga ao comentário é ignorada pela shell; o comentário serve apenas para leitura.$ representa o prompt da shell.
Se estiveres a copiar o comando, não incluas o prompt.# Note: It is always a good idea to run `ls` before `rm`
# to make sure you know what you are deleting.
$ rm * # Remove all files in the current directory.
# Make sure you run this in a temp directory or you can lose files!
$ ls # List files in the current directory (no output, no files).
$ touch a b c # Create files 'a', 'b' and 'c'.
$ ls # List files again; this time the outputs: 'a', 'b' and 'c'.
a b c
O rm é uma aplicação que remove os ficheiros que lhe são indicados.
O * é um caractere glob, um padrão «wildcard» que corresponde a todos os ficheiros do diretório atual.
Falaremos mais sobre globs adiante.
Reparaste que há vários espaços entre a e b, e apenas um entre b e c?
A quantidade de espaços em branco entre argumentos não importa!
Por exemplo:
$ echo This is a test.
This is a test.
$ echo This is a test.
This is a test.
O echo é um comando que imprime os seus argumentos na saída padrão (que, no nosso caso, é o terminal).
O Bash divide o comando em cinco palavras e invoca o comando echo com quatro argumentos.
Os espaços a mais no segundo exemplo não fazem diferença.
O echo não sabe como estavam originalmente os espaços entre as palavras.
Se quisermos manter os espaços a mais, temos de passar a frase como um único argumento. Podemos fazê-lo com aspas:
$ echo "This is a test."
This is a test.
As aspas agrupam tudo o que está dentro delas num único argumento.
O argumento é This is a test., incluindo esses espaços específicos.
Repara que as aspas não fazem parte do argumento: o bash remove-as antes de entregar esse único argumento ao echo.
Os argumentos são separados do nome do comando e uns dos outros por um espaço em branco. É importante lembrar-te disto. Por exemplo, o seguinte está errado:
$ [-f file]
bash: [-f: command not found
A intenção é testar se existe um ficheiro chamado file.
É razoável assumir que os espaços à volta de [ e ] não importam, mas o [ é, na verdade, um comando e exige que o seu último argumento seja ].
(Abordaremos o comando [ com mais detalhe adiante.)
Por isso, temos de separar o [ do -f e o ] do file.
Caso contrário, o bash vai pensar que estamos a tentar executar um comando chamado [-f com um único argumento, file].
O comando correto separa todos os argumentos com espaços em branco:
$ [ -f file ]
(Vemos muita gente confusa com este comportamento; pensam que [...] é «apenas sintaxe» e que podem omitir os espaços; o [ é um comando e os espaços são obrigatórios.)
Se o nome do ficheiro contiver espaços ou outros caracteres especiais, também deve ser colocado entre aspas:
$ [ -f "my file" ]
O Bash reconhece vários tipos diferentes de comandos: aliases, funções, builtins, palavras-chave e executáveis. Todos eles são comandos e comportam-se de forma muito semelhante.
Por predefinição, os aliases só estão ativos em shells interativas, não em scripts de shell. Não os vamos abordar aqui.
Uma função contém comandos de shell e comporta-se de forma muito semelhante a um pequeno script; pode até receber argumentos e criar variáveis locais. Quando uma função é chamada, os comandos que contém são executados. As funções serão abordadas em profundidade mais adiante.
O Bash tem alguns comandos básicos incorporados, como o cd (mudar de diretório), o echo (escrever saída), e por aí fora.
Podem ser vistos como funções já disponibilizadas.
As palavras-chave são como os builtins, com a diferença principal de que as palavras-chave são realmente sintaxe do bash e podem ser analisadas com regras especiais.
Por exemplo, o [ é um builtin do bash, enquanto o [[ é uma palavra-chave do bash; ambos servem para testar várias condições.
Mas a palavra-chave [[ tem algumas funcionalidades extra.
Por exemplo, fornece o operador =~, que faz correspondência com expressões regulares.
$ [[ "hello world" =~ e.*o ]] # returns a "success" status
$ [ "hello world" =~ e.*o ]
bash: [: =~: binary operator expected
Os operadores de comparação de strings < e > são outro exemplo de como as palavras-chave analisam o código de forma diferente.
Dentro de [[...]], estes operadores não precisam de cuidados especiais.
$ if [[ "$USER" < "n" ]]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
Mas dentro de [...] tens de os colocar entre aspas ou escapar para impedir que a shell os interprete como operadores de redirecionamento.
$ if [ "$USER" < "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => bash: n: No such file or directory
$ if [ "$USER" \< "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
O último tipo de comando que o bash pode executar é um executável.
São ficheiros executáveis (programas) no sistema de ficheiros do teu computador que podes executar.
(Os executáveis também podem ser chamados comandos externos ou aplicações.)
Os executáveis são normalmente invocados escrevendo apenas o seu nome; o caminho completo para o ficheiro normalmente não é necessário.
Isto é possível porque o bash usa uma variável predefinida que o ajuda a encontrar a localização dos ficheiros executáveis habituais.
Essa variável chama-se PATH.
É um conjunto de nomes de diretórios separados por dois pontos (por exemplo, /usr/bin:/bin).
Quando é indicado um comando (por exemplo, myprogram ou ls) sem um caminho de ficheiro (e que não é um alias, função, builtin ou palavra-chave), o bash procura nos diretórios do PATH.
Os diretórios são pesquisados por ordem, da esquerda para a direita, para ver se contêm um executável com o nome indicado.
Se o executável estiver fora de um caminho conhecido, será necessário indicar o caminho do ficheiro do executável.
Para o executável myprogram no diretório atual, usa ./myprogram; se estiver no diretório /opt/somedirectory, usa /opt/somedirectory/myprogram.
Dica: podes usar o comando type para obter detalhes sobre o comando:
$ type rm
rm is hashed (/bin/rm)
# or it might return: `rm is /bin/rm`
$ type cd
cd is a shell builtin
Um script é uma sequência de comandos guardada num ficheiro. O Bash lê o ficheiro e processa os comandos por ordem. Só passa ao comando seguinte quando o atual termina. A exceção é quando um comando foi indicado para ser executado de forma assíncrona (em segundo plano). Não te preocupes demasiado com este caso por agora; vamos aprender como isso funciona mais adiante.
Qualquer exemplo de linha de comandos neste guia também pode ser usado num script.
Criar um script é fácil. Começa por criar um ficheiro novo e coloca isto na primeira linha:
#!/bin/bash
Esta linha chama-se diretiva de intérprete (também lhe chamam hashbang ou shebang).
Especifica que /bin/bash deve ser usado como intérprete quando o ficheiro é usado como executável num comando.
Quando o kernel do sistema operativo executa um ficheiro não binário, lê a primeira linha do ficheiro.
Se a linha começar por #!, o kernel usa essa linha para determinar o intérprete a quem entregar o ficheiro.
(Há outras formas válidas de o fazer, vê mais abaixo.)
O #! tem de estar no início absoluto do ficheiro, sem espaços nem linhas em branco antes.
Os comandos do nosso script aparecem em linhas separadas, a seguir a esta.
Dica: em vez de #!/bin/bash , podes usar
#!/usr/bin/env bash
O env procura no $PATH o executável indicado pelo seu primeiro argumento (neste caso, «bash»).
Para uma explicação mais detalhada desta técnica e de como difere do simples #!/bin/bash, vê esta pergunta no StackOverflow.
Não te deixes enganar por scripts ou exemplos na Internet que usam /bin/sh como intérprete.
O sh não é o bash!
O próprio Bash é uma shell «compatível com sh». Isso significa que consegue executar a maioria dos scripts sh e partilha grande parte da mesma sintaxe.
No entanto, o contrário não é verdade: algumas funcionalidades do bash falham ou provocam comportamentos inesperados no sh.
Na verdade, não existe uma única shell sh.
Há muitas implementações de shell que suportam a sintaxe POSIX-sh.
Consoante o teu sistema operativo, quando executas sh podes estar, na realidade, a executar o bash, o ash, o dash ou outra shell qualquer.
Depois de criares o ficheiro do script, podes executá-lo assim:
$ bash myscript
Neste exemplo, executamos o comando bash e dizemos-lhe para ler o script que indicámos como argumento.
Quando o fazemos assim, a linha #! é apenas um comentário.
Em alternativa, podemos dar permissões de execução aos nossos scripts. Com este método, podemos executar o script como qualquer outro executável, em vez de chamarmos o bash manualmente:
$ chmod +x myscript # Mark the file as executable.
$ ./myscript # Now, myscript can be executed directly
# instead of having to specify bash.
Quando executamos o comando desta forma, o sistema operativo (SO) usa a linha #! para determinar que intérprete usar para executar o teu script.
O conteúdo desta lição foi retirado de Commands and Arguments, do Bash Guide.