Papo de botequim
Diálogo ouvido entre um linuxer e um empurrador de mouse:
Quem é o Bash?
O Bash é o filho mais novo da família Shell.
Pô, cara! Está a fim de me deixar maluco?
Eu tinha uma dúvida e você me deixa
com duas!
Não, maluco você já é há muito tempo.
Desde que se decidiu a usar aquele sistema operacional que você tem de dar dez boots
por dia e não tem domínio algum sobre
o que está acontecendo no seu computador.
Mas deixe isso para lá, vou explicar o que é
Shell e os componentes de sua família e ao final você dirá: "Meu Deus do Shell! Por que eu não optei pelo Linux antes?"
O ambiente Linux
Para você entender o que é e como funciona o Shell, primeiro vou mostrar como funciona o ambiente em camadas do Linux. Dê uma olhada no esquema do quadro 1.
É possível ver que a camada de
hardware é a mais profunda e é formada pelos componentes físicos do seu computador. Envolvendo essa camada, vem a do kernel, que é o cerne do
Linux, seu núcleo, e é quem põe o
hardware para funcionar, fazendo seu gerenciamento e controle. Os programas e comandos que envolvem o
kernel se utilizam dele para realizar
as tarefas aplicativas para as quais foram desenvolvidos. Fechando tudo isso vem o Shell, que leva esse nome porque, em inglês, Shell significa concha, carapaça, isto é, ele fica entre o usuário e o sistema operacional, de forma que tudo que interage com o sistema operacional tem de passar pelo seu crivo.
O ambiente Shell
Bom, uma vez que para chegar ao núcleo do Linux, ao seu kernel, que é o que interessa a todo aplicativo, é necessária a filtragem do Shell, vamos entender como ele funciona e tirar o máximo proveito das inúmeras facilidades que ele oferece.
O Linux, por definição, é um sistema multiusuário não podemos nunca esquecer isso e para permitir o acesso de determinados usuários e barrar a entrada de outros, existe um arquivo chamado /etc/passwd que, além de fornecer dados para essa função de leão-de-chácara do Linux, também provê informações para o login daqueles que passaram por essa primeira barreira. O último campo de seus registros informa ao sistema qual é o Shell que a pessoa vai receber ao
se "logar": ARGH!?! (veja quadro 2).
Lembra que eu falei de Shell, família? Pois é, vamos começar a entender isso: o Shell é "a concha" que envolve o sistema operacional propriamente dito. É o nome genérico para tratar os filhos dessa idéia que ao longo dos anos de existência do sistema operacional Unix foram aparecendo. Atualmente existem diversos sabores de Shell, dentre esses destaco sh (Bourne Shell), ksh (Korn Shell), bash (Bourne Again Shell) e csh (C Shell).
Uma rapidinha nos principais sabores de Shell
Bourne Shell (sh)
Desenvolvido por Stephen Bourne, da Bell Labs (da AT&T, onde também foi desenvolvido o Unix), o sh foi durante muitos anos o Shell default do sistema operacional Unix. É também chamado de Standard Shell por ter sido durante muito tempo o único. Até hoje é o mais utilizado, até porque ele foi portado para todos os ambientes Unix e distros Linux.
Korn Shell (ksh)
Desenvolvido por David Korn, também da Bell Labs, é um superset do sh, isto é, possui todas as facilidades do sh e a elas agregou muitas outras. A compatibilidade total com o sh vem trazendo muitos usuários e programadores de Shell para esse ambiente.
Bourne Again Shell (bash)
Este é o Shell mais moderno (exceção feita ao bash 2) e cujo número de adeptos mais cresce em todo o mundo, seja por ser o Shell default do Linux, seu sistema operacional hospedeiro, seja por sua grande diversidade de
comandos, que incorporam inclusive vários comandos característicos
do C Shell.
C Shell (csh)
Desenvolvido por Bill Joy, da Berkley University, é o Shell mais utilizado em ambientes BSD e Xenix. A estruturação de seus comandos é bem similar à da linguagem C. Seu grande pecado foi ignorar a compatibilidade com o sh, partindo por um caminho próprio.
Além desses Shells existem outros, mas comento a seguir somente os três primeiros, tratando-os genericamente por Shell e assinalando as eventuais especificidades de cada um.
Quadro 2
Dica para os administradores!
Quando eu disse que o último campo do /etc/passwd informa ao sistema qual é o Shell que o usuário vai receber ao se "logar", é para ser interpretado ao pé-da-letra, isto é, se neste campo do seu registro estiver prog, a pessoa ao acessar o sistema receberá a tela de execução do programa prog e ao terminar a sua execução ganhará imediatamente um logout. Imagine quanto se pode incrementar a segurança com esse simples artifício.
|
Explicando o funcionamento
do Shell
O Shell é o primeiro programa que você ganha ao se "logar" no Linux. É ele que vai resolver muitas coisas de forma a não onerar o kernel com tarefas repetitivas, aliviando-o para tratar assuntos mais nobres. Como cada usuário possui seu próprio Shell interpondo-se entre ele e o Linux, é o Shell que interpreta os comandos que são teclados e examina suas sintaxes, passando-os esmiuçados para execução.
Êpa! Isso de interpretar comando não tem nada que ver com interpretador, não é?
Tem sim, na verdade o Shell é um interpretador (ou será intérprete) que traz consigo uma poderosa linguagem com comandos de alto nível, que permitem a construção de loops, de tomadas de decisão e de armazenamento
de valores em variáveis.
Vou explicar as principais tarefas que o Shell cumpre em sua ordem de execução. Preste atenção nesta ordem porque ela é fundamental para o entendimento do resto de nosso bate-papo.
Exame da linha de comandos
O Shell identifica primeiro os caracteres especiais (reservados) que têm significado para interpretação da linha, logo em seguida verifica se a linha é um comando ou uma atribuição.
Comando
Quando uma linha é digitada no prompt do Linux, ela é dividida em pedaços separados por espaços em branco: o primeiro pedaço é o nome do programa que terá sua existência verificada; identificam-se em seguida, nesta ordem, opções/parâmetros,
redirecionamentos e variáveis.
Quando o programa identificado existe, o Shell verifica as permissões dos arquivos envolvidos (inclusive o próprio programa), informando um erro caso você não esteja credenciado a executar essa tarefa.
Atribuição
Se o Shell encontra dois campos separados por um sinal de igual (=) sem espaços em branco entre eles, identifica essa seqüência como uma atribuição.
$ ls linux
Linux
Nesse exemplo o Shell identificou
o ls como um programa e o Linux como um parâmetro passado para o programa ls.
$ valor=1000
Nesse caso, por não haver espaços em branco (já dá para notar que o branco é um dos caracteres reservados), o Shell identificou uma atribuição e colocou 1000 na variável valor. Atenção! não faça:
$ valor = 1000
bash: valor: not found
Nesse caso, o Bash achou a palavra valor isolada por brancos e interpretou que você estivesse mandando executar um programa chamado valor, para o qual estaria passando dois parâmetros: = e 1000.
Resolução de redirecionamentos
Após identificar os componentes
da linha que você teclou, o Shell parte para a resolução de redirecionamentos.
O Shell tem incorporado em seu elenco de vantagens o que chamamos de redirecionamento, que pode ser de entrada (stdin), de saída (stdout)
ou dos erros (stderr), conforme vou explicar a seguir.
Substituição de variáveis
Neste ponto, o Shell verifica se as eventuais variáveis (parâmetros começados por $), encontradas no escopo do comando, estão definidas e as substitui por seus valores atuais.
Substituição de metacaracteres
Se algum metacaractere (*, ? ou []) é encontrado na linha de comando, neste ponto ele é substituído por seus possíveis valores.
Supondo que o único arquivo no seu diretório corrente começado pela letra "n" seja um diretório chamado "nomegrandeprachuchu", se você fizer:
$ cd n*
o Shell transforma o n* em nomegrandeprachuchu e o comando cd será
executado com sucesso, porque até aqui quem está trabalhando a sua linha é o Shell e o comando (programa) cd ainda não foi executado.
Passar linha de comando para o kernel
Completadas as tarefas anteriores,
o Shell monta a linha de comandos, já com todas as substituições feitas, chama o kernel para executá-la em um novo Shell (Shell filho), ganhando um número de processo (PID ou Process Identification) e permanece inativo, tirando uma soneca, durante a execução do programa. Uma vez encerrado esse processo (juntamente com o Shell filho), o Shell recebe novamente o controle e, exibindo um prompt, mostra que está pronto para executar outros comandos.
Decifrando a Pedra de Roseta
Para tirar aquela sensação que você tem quando vê um script Shell, que mais parece uma sopa de letrinhas ou um hieroglifo, vou mostrar os principais caracteres especiais para que você possa sair por aí como o Champollion decifrando a Pedra de Roseta.
Caracteres para remoção do significado
É isso mesmo, quando não desejamos que o Shell interprete um caractere especial, devemos "escondê-lo" dele. Isso pode ser feito de três formas distintas:
Apóstrofos ()
Quando o Shell vê uma cadeia de caracteres entre apóstrofos, ele tira os apóstrofos da cadeia e não interpreta seu conteúdo.
$ ls revista*
revistadolinux
$ ls revista*
bash: revista* no such file or directory
No primeiro caso o Shell "expandiu" o asterisco e descobriu o arquivo revistadolinux para listar. No segundo, os apóstrofos inibiram a interpretação do Shell e veio a resposta que não existe o arquivo revista*.
Contrabarra ou Barra Invertida (\)
Idêntico aos apóstrofos exceto que
a barra invertida inibe a interpretação somente do caractere que a segue.
Suponha que você acidental- mente tenha criado um
arquivo chamado * (asterisco) que alguns sabores de Unix permitem e deseje removê-lo. Se você fizesse:
$ rm *
estaria na maior encrenca, pois o rm removeria todos os arquivos do diretório corrente. A forma correta de escrever para remover somente o arquivo * é:
$ rm \*
Desse modo, o Shell não interpreta o asterisco, fazendo sua expansão.
Teste o seguinte:
$ cd /etc
$ echo *
$ echo \*
$ echo *
Viu a diferença?
Aspas (")
São exatamente iguais ao apóstrofo exceto que, se a cadeia entre aspas contiver um cifrão ($), uma crase
(`), ou uma barra invertida (\), esses caracteres serão interpretados pelo Shell.
Não precisa se estressar, não dei exemplos do uso das aspas porque você ainda não conhece o cifrão ($) nem a crase (`). Daqui para frente
veremos com muita freqüência o uso desses caracteres especiais, o mais
importante é entender o significado
de cada um.
Caracteres de redirecionamento
A maioria dos comandos tem uma entrada, uma saída e pode gerar mensagens de erro. Essa entrada é chamada Entrada Padrão ou stdin e seu
default é o teclado do terminal. Analogamente, a saída do comando é chamada Saída Padrão ou stdout e seu default é a tela do terminal. Para a
tela também são enviadas por default as mensagens de erro oriundas do
comando, e, nesse caso, ela é chamada Saída de Erro Padrão ou stderr. Veremos agora como alterar esse estado
de coisas.
Vamos fazer um programa gago. Para isso escreva:
$ cat
O cat é uma instrução que lista o conteúdo do arquivo especificado para a Saída Padrão (stdout). Caso a entrada não seja definida, ele espera os dados da stdin. Ora, como não especifiquei a entrada, ele está esperando-a pelo teclado (Entrada Padrão), e como também não citei a saída, o que eu teclar irá para a tela (Saída Padrão), fazendo dessa forma, como eu havia proposto, um programa gago.
Experimente!
Redirecionamentos da Saída Padrão
Para especificarmos a saída de
um programa usamos o > (maior que) ou o >> (maior, maior) seguido do nome para onde se deseja mandar a saída.
Vamos transformar o progra- ma gago em um "editor de textos" (que pretensão, hein?).
$ cat > Arq
O cat continua sem ter a entrada especificada, portanto está aguardando que os dados sejam teclados, porém sua saída está sendo desviada para o arquivo Arq. Assim sendo, tudo que
é teclado vai para dentro do Arq.
Se eu escrever novamente:
$ cat > Arq
Os dados contidos em Arq serão perdidos, já que antes do redirecionamento o Shell criará um Arq vazio. Para colocar mais informações no final do arquivo deve-se escrever:
$ cat >> Arq
O >> (maior, maior) serve para
inserir texto no final do arquivo.
Redirecionamento da Saída de
Erro Padrão
Como já foi dito, o Shell resolve a linha e depois manda o comando para execução. Assim, se você redirecionar a saída de um arquivo para ele próprio, primeiramente o Shell "esvazia" esse arquivo e depois manda o comando para execução. Dessa forma você perde o conteúdo de seu arquivo.
Assim como o default do Shell é receber os dados do teclado e mandar as saídas para a tela, os erros também serão enviados para a tela se você não especificar para onde deverão ser enviados. Para redirecionar os erros escreva 2> SaidaDeErro. E atenção! Não confunda >> com 2>.