Papo de Botequim - Parte I
Diálogo entreouvido entre um Linuxer e em empurrador de mouse:
- Quem é o Bash?
- O Bash é o filho mais novo da família Shell.
- Pô cara! Estás 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 que dar dez boots por dia e não tem domínio nenhum sobre o que esta acontecendo no seu computador. Mas deixa isso prá lá, vou te explicar o que é Shell e os componentes de sua família e ao final da explanação você dirá: "Meu Deus do Shell! Porque eu não optei pelo Linux antes?".
O Ambiente Linux
Para você entender o que é e como funciona o Shell, primeiro vou te mostrar como funciona o ambiente em camadas do Linux. Dê uma olhada no gráfico abaixo:
Neste gráfico dá para ver que a camada de hardware é a mais profunda e é formada pelos componentes físicos do seu computador. Envolvendo esta, vem a camada do kernel que é o cerne do Linux, seu núcleo, e é quem bota o hardware para funcionar, fazendo seu gerenciamento e controle. Os programas e comandos que envolvem o kernel, dele se utilizam para realizar as tarefas aplicativas para que foram desenvolvidos. Fechando tudo isso vem o Shell que leva este nome porque em inglês, Shell significa concha, carapaça, isto é, fica entre o usuário e o sistema operacional, de forma que tudo que interage com o sistema operacional, tem que passar pelo seu crivo.
O Ambiente Shell
Bom já que para chegar ao núcleo do Linux, no seu kernel que é o que interessa a todo aplicativo, é necessária a filtragem do Shell, vamos entender como ele funciona de forma a tirar o máximo proveito das inúmeras facilidades que ele nos oferece.
O Linux por definição é um sistema multiusuário - não podemos nunca esquecer disto – e para permitir o acesso de determinados usuários e barrar a entrada de outros, existe um arquivo chamado /etc/passwd que além fornecer dados para esta função de "leão-de-chácara" do Linux, também provê informações para o login daqueles que passaram por esta primeira barreira. O último campo de seus registros informa ao sistema qual Shell a pessoa vai receber ao se "logar" (ARGH!!!).
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 o quanto se pode incrementar a segurança com este simples artifício.
Lembra que eu te falei de Shell, família, irmão? Pois é, vamos começar a entender isto: o Shell, que se vale da imagem de uma concha envolvendo o sistema operacional propriamente dito, é o nome genérico para tratar os filhos desta idéia que, ao longo dos anos de existência do sistema operacional Unix foram aparecendo. Atualmente existem diversos sabores de Shell, dentre estes eu destaco o sh (Bourne Shell), o ksh (Korn Shell), bash (Bourne Again Shell) e o 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), este foi durante muitos anos o Shell default do sistema operacional Unix. É também chamado de Standard Shell por ter sido durante vários anos o único e 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 este ambiente.
Boune Again Shell (bash)
Este é o Shell mais moderno 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 incorpora inclusive diversos instruções características 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 destes Shells existem outros, mas irei falar contigo somente sobre os três primeiros, tratando-os genericamente por Shell e assinalando as especificidades de cada um que porventura hajam.
Explicando o funcionamento do Shell
O Shell é o primeiro programa que você ganha ao se "logar" no Linux. É ele que vai resolver um monte de coisas de forma a não onerar o kernel com tarefas repetitivas, aliviando-o para tratar assuntos mais nobres. Como cada usuário possui o seu próprio Shell interpondo-se entre ele e o Linux, é o Shell quem interpreta os comandos que são teclados e examina as suas sintaxes, passando-os esmiuçados para execução.
- Êpa! Esse negócio de interpreta comando não tem nada a haver com interpretador não, né?
- 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 permite construção de loops (laços), de tomadas de decisão e de armazenamento de valores em variáveis, como vou te mostrar.
Vou te explicar as principais tarefas que o Shell cumpre, na sua ordem de execução. Preste atenção nesta ordem porque ela é fundamental para o entendimento do resto do nosso bate papo.
Exame da Linha de Comandos
Neste exame o Shell identifica os caracteres especiais (reservados) que têm significado para interpretação da linha, logo após verifica se a linha passada é uma atribuição ou um comando.
Atribuição
Se o Shell encontra dois campos separados por um sinal de igual (= ) sem espaços em branco entre eles, identifica esta seqüência como uma atribuição.
Exemplos
$ ls linux
linux
Neste exemplo o Shell identificou o ls como um programa e o linux como um parâmetro passado para o programa ls .
$ valor=1000
Neste 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 .
Jamais Faça:
$ valor = 1000
bash: valor: not found
Neste caso, o Bash achou a palavra valor isolada por brancos e julgou que você estivesse mandando executar um programa chamado valor, para o qual estaria passando dois parâmetros: = e 1000 .
Comando
Quando uma linha é digitada no prompt do Linux, ela é dividida em pedaços separados por espaço em branco: o primeiro pedaço é o nome do programa que terá sua existência pesquisada; identifica 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), dando um erro caso você não esteja credenciado a executar esta tarefa.
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 ao 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 te 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 Meta Caracteres
Se algum metacaractere (* , ? ou [] ) foi encontrado na linha de comando, neste ponto ele será 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*
Como até aqui quem esta trabalhando a sua linha é o Shell e o comando (programa) cd ainda não foi executado, o Shell transforma o n* em nomegrandeprachuchu e o comando cd será executado com sucesso.
Passa 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 este processo (juntamente com o Shell filho), recebe novamente o controle e, exibindo um prompt, mostra que está pronto para executar outros comandos.
Decifrando a Pedra da Roseta
Para tirar aquela sensação que você tem quando vê um script Shell, que mais parece uma sopa de letrinhas ou um hieróglifo vou lhe mostrar os principais caracteres especiais para que você saia por ai como o Jean-François Champollion decifrando a Pedra da Roseta (dê uma "googlada" para descobrir quem é este cara, acho que vale a pena).
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óstrofo ou plic (' )
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 linux*
linuxmagazine
$ ls 'linux*'
bash: linux* no such file or directory
No primeiro caso o Shell "expandiu" o asterisco e descobriu o arquivo linuxmagazine para listar. No segundo, os apóstrofos inibiram a interpretação do Shell e veio a resposta que não existe o arquivo linux* .
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ê acidentalmente tenha criado um arquivo chamado * (asterisco) – que alguns sabores de Unix permitem - e deseja removê-lo. Se você fizesse:
$ rm *
Você estaria fazendo a maior encrenca, pois o rm removeria todos os arquivos do diretório corrente. A melhor forma de fazer o pretendido é:
$ rm \*
Desta forma, o Shell não interpretaria o asterisco, e em conseqüência não faria a sua expansão.
Faça a seguinte experiência científica:
$ cd /etc
$ echo '*'
$ echo \*
$ echo *
Viu a diferença? Então não precisa explicar mais.
Aspas (" )
Exatamente igual ao apóstrofo exceto que, se a cadeia entre aspas contiver um cifrão ($ ), uma crase (` ), ou uma barra invertida (\ ), estes caracteres serão interpretados pelo Shell.
Não precisa se estressar, eu não te dei exemplos do uso das aspas por que você ainda não conhece o cifrão ($ ) nem a crase (` ). Daqui para frente veremos com muita constância o uso destes 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 erros. Esta 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 que neste caso é a chamada Saída de Erro Padrão ou stderr . Veremos agora como alterar este estado de coisas.
Vamos fazer um programa gago. Para isto faça:
$ 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 eu 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 desta forma, como eu havia proposto um programa gago. Experimente!
Redirecionamento da Saída Padrão
Para especificarmos a saída de um programa usamos o > (maior que) ou o >> (maior, maior) seguido do nome do arquivo para o qual se deseja mandar a saída.
Vamos transformar o programa gago em um editor de textos (que pretensão heim!).
$ cat > Arq
O cat continua sem ter a entrada especificada, portanto está aguardando que os dados sejam teclados, porém a sua saída está sendo desviada para o arquivo Arq . Assim sendo, tudo que esta sendo teclado esta indo para dentro de Arq , de forma que fizemos o editor de textos mais curto e ruim do planeta.
Se eu fizer 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 eu deveria ter feito:
$ cat >> Arq
Como já havia lhe dito, o Shell resolve a linha e depois manda o comando para a execução. Assim, se você redirecionar a saída de um arquivo para ele próprio, primeiramente o Shell "esvazia" este arquivo e depois manda o comando para execução, desta forma você acabou de perder o conteúdo do seu querido arquivo.
Com isso dá para notar que o >> (maior maior) serve para inserir texto no final do arquivo.
Redirecionamento da Saída de Erro Padrão
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 use 2> SaidaDeErro . Note que entre o número 2 e o sinal de maior (> ) não existe espaço em branco.
Preste atenção! Não confunda >> com 2> . O primeiro anexa dados ao final de um arquivo, e o segundo redireciona a Saída de Erro Padrão ( stderr ) para um arquivo que está sendo designado. Isso é importante!
Suponha que durante a execução de um script você pode, ou não (dependendo do rumo tomado pela execução do programa), ter criado um arquivo chamado /tmp/seraqueexiste$$ . Para não ficar sujeira no seu disco, ao final do script você colocaria uma linha:
$ rm /tmp/seraqueexiste$$
Caso o arquivo não existisse seria enviado para a tela uma mensagem de erro. Para que isso não aconteça deve-se fazer:
$ rm /tmp/seraqueexiste$$ 2> /dev/null
Sobre o exemplo que acabamos de ver tenho duas dicas a dar:
Dica # 1
O $$ contém o PID, isto é, o número do seu processo. Como o Linux é multiusuário, é bom anexar sempre o $$ ao nome dos dos arquivos que serão usados por várias pessoas para não haver problema de propriedade, isto é, caso você batizasse o seu arquivo simplesmente como seraqueexiste , o primeiro que o usasse (criando-o então) seria o seu dono e todos os outros ganhariam um erro quando tentassem gravar algo nele.
Para que você teste a Saída de Erro Padrão direto no prompt do seu Shell, vou dar mais um exemplo. Faça:
$ ls naoexiste
bash: naoexiste no such file or directory
$ ls naoexiste 2> arquivodeerros
$
$ cat arquivodeerros
bash: naoexiste no such file or directory
Neste exemplo, vimos que quando fizemos um ls em naoexiste , ganhamos uma mensagem de erro. Após, redirecionarmos a Saída de Erro Padrão para arquivodeerros e executarmos o mesmo comando, recebemos somente o prompt na tela. Quando listamos o conteúdo do arquivo para o qual foi redirecionada a Saída de Erro Padrão, vimos que a mensagem de erro tinha sido armazenada nele. Faça este teste ai.
Dica # 2
- Quem é esse tal de /dev/null ?
- Em Unix existe um arquivo fantasma. Chama-se /dev/null . Tudo que é mandado para este arquivo some. Assemelha-se a um Buraco Negro. No caso do exemplo, como não me interessava guardar a possível mensagem de erro oriunda do comando rm , redirecionei-a para este arquivo.
É interessante notar que estes caracteres de redirecionamento são cumulativos, isto é, se no exemplo anterior fizéssemos:
$ ls naoexiste 2>> arquivodeerros
a mensagem de erro oriunda do ls seria anexada ao final de arquivodeerros .
Redirecionamento da Entrada Padrão
Para fazermos o redirecionamento da Entrada Padrão usamos o < (menor que).
- E prá que serve isso? - você vai me perguntar.
- Deixa eu te dar um exemplo que você vai entender rapidinho.
Suponha que você queira mandar um mail para o seu chefe. Para o chefe nós caprichamos, né? então ao invés de sair redigindo o mail direto no prompt da tela de forma a tornar impossível a correção de uma frase anterior onde, sem querer, escreveu um "nós vai", você edita um arquivo com o conteúdo da mensagem e após umas quinze verificações sem constatar nenhum erro, decide enviá-lo e para tal faz:
$ mail chefe < arquivocommailparaochefe
O teu chefe então receberá o conteúdo do arquivocommailparaochefe .
Here Document
Um outro tipo de redirecionamento muito louco que o Shell te permite é o chamado here document. Ele é representado por << (menor menor) e serve para indicar ao Shell que o escopo de um comando começa na linha seguinte e termina quando encontra uma linha cujo conteúdo seja unicamente o label que segue o sinal << .
Veja o fragmento de script a seguir, com uma rotina de ftp :
ftp -ivn hostremoto << fimftp
user $Usuário $Senha
binary
get arquivoremoto
fimftp
Neste pedacinho de programa temos um monte de detalhes interessantes:
- As opções que usei para o
ftp (-ivn ) servem para ele ir listando tudo que está acontecendo (—v de verbose), para não perguntar se você tem certeza de que deseja transmitir cada arquivo (—i de interactive), e finalmente a opção —n serve para dizer ao ftp para ele não solicitar o usuário e sua senha, pois esses serão informados pela instrução específica (user );
- Quando eu usei o
<< fimftp , estava dizendo o seguinte para o intérprete: - Olhe aqui Shell, não se meta em nada a partir daqui até encontrar o label fimftp . Você não entenderia nada, já que são instruções específicas do comando ftp e você não entende nada de ftp . Se fosse só isso seria simples, mas pelo próprio exemplo dá para ver que existem duas variáveis ($Usuário e $Senha ), que o Shell vai resolver antes do redirecionamento. Mas a grande vantagem desse tipo de construção é que ela permite que comandos também sejam interpretados dentro do escopo do here document, o que também contraria o que acabei de dizer. Logo a seguir explico como esse negócio funciona. Agora ainda não dá, está faltando ferramenta.
- O comando
user é do repertório de instruções do ftp e serve para passar o usuário e a senha que haviam sido lidos em uma rotina anterior a esse fragmento de código e colocados respectivamente nas duas variáveis: $Usuário e $Senha .
- O
binary é outra instrução do ftp , que serve para indicar que a transferência de arquivoremoto será feita em modo binário, isto é, o conteúdo do arquivo não será interpretado para saber se está em ASCII, EBCDIC, ...
- O
get arquivoremoto diz ao ftp para pegar esse arquivo em hostremoto e trazê-lo para o nosso host local. Se fosse para mandar o arquivo, usaríamos o comando put .
Um erro muito freqüente no uso de labels (como o fimftp do exemplo anterior) é causado pela presença de espaços em branco antes ou após o mesmo. Fique muito atento quanto a isso, por que este tipo de erro costuma dar uma boa surra no programador, até que seja detectado. Lembre-se: um label que se preze tem que ter uma linha inteira só para ele.
- Está bem, está bem! Eu sei que dei uma viajada e entrei pelos comandos do ftp , fugindo ao nosso assunto que é Shell, mas como é sempre bom aprender e é raro as pessoas estarem disponíveis para ensinar...
Redirecionamento de Comandos
Os redirecionamentos que falamos até aqui sempre se referiam a arquivos, isto é mandavam para arquivo, recebiam de arquivo, simulavam arquivo local, ... O que veremos a partir de agora redireciona a saída de um comando para a entrada de outro. É utilíssimo e quebra os maiores galhos. Seu nome é pipe (que em inglês significa tubo, já que ele encana a saída de um comando para a entrada de outro) e sua representação é uma barra vertical (| ).
$ ls | wc -l
21
O comando ls passou a lista de arquivos para o comando wc , que quando está com a opção –l conta a quantidade de linha que recebeu. Desta forma, podemos afirmar categoricamente que no meu diretório existiam 21 arquivos.
$ cat /etc/passwd |sort | lp
Esta linha de comandos manda a listagem do arquivo /etc/passwd para a entrada do comando sort . Este a classifica e manda-a para o lp que é o gerenciador do spool de impressão.
Caracteres de Ambiente
Quando quer priorizar uma expressão você coloca-a entre parênteses não é? Pois é, por causa da aritmética é normal pensarmos deste jeito. Mas em Shell o que prioriza mesmo são as crases (` ) e não os parênteses. Vou dar exemplos de uso das crases para você entender melhor.
Eu quero saber quantos usuários estão "logados" no computador que eu administro. Eu posso fazer:
$ who | wc -l
8
O comando who passa a lista de usuários conectados para o comando wc –l que conta quantas linhas recebeu e lista a resposta na tela. Pois bem, mas ao invés de ter um oito solto na tela, o que eu quero é que ele esteja no meio de uma frase.
Ora para mandar frases para a tela eu uso o comando echo , então vamos ver como é que fica:
$ echo "Existem who | wc -l usuários conectados"
Existem who | wc -l usuários conectados
Hi! Olha só, não funcionou! É mesmo, não funcionou e não foi por causa das aspas que eu coloquei, mas sim por que eu teria que ter executado o who | wc -l antes do echo . Para resolver este problema, tenho que priorizar esta segunda parte do comando com o uso de crases, fazendo assim:
$ echo "Existem `who | wc -l` usuários conectados"
Existem 8 usuários conectados
Para eliminar esse monte de brancos antes do 8 que o wc -l produziu, basta tirar as aspas. Assim:
$ echo Existem `who | wc -l` usuários conectados
Existem 8 usuários conectados
Como eu disse antes, as aspas protegem tudo que esta dentro dos seus limites, da interpretação do Shell. Como para o Shell basta um espaço em branco como separador, o monte de espaços será trocado por um único após a retirada das aspas.
Antes de falar sobre o uso dos parênteses deixa eu mandar uma rapidinha sobre o uso de ponto-e-vírgula (; ). Quando estiver no Shell, você deve sempre dar um comando em cada linha. Para agrupar comandos em uma mesma linha teremos que separá-los por ponto-e-vírgula. Então:
$ pwd ; cd /etc; pwd; cd -; pwd
/home/meudir
/etc/
/home/meudir
Neste exemplo, listei o nome do diretório corrente com o comando pwd , mudei para o diretório /etc , novamente listei o nome do diretório e finalmente voltei para o diretório onde estava anteriormente (cd - ), listando seu nome. Repare que coloquei o ponto-e-vírgula (; ) de todas as formas possíveis para mostrar que não importa se existe espaços em branco antes ou após este caractere.
Finalmente vamos ver o caso dos parênteses. Veja só o caso a seguir, bem parecido com o exemplo anterior:
$ (pwd ; cd /etc ; pwd;)
/home/meudir
/etc/
$ pwd
/home/meudir
- Quequeiiisso minha gente? Eu estava no /home/meudir , mudei para o /etc , constatei que estava neste diretório com o pwd seguinte e quando o agrupamento de comandos terminou, eu vi que continuava no /home/meudir , como se eu nunca houvesse saído de lá!
- Ih! Será que é tem coisa de mágico aí?
- Tá me estranhando, rapaz? Não é nada disso! O interessante do uso de parênteses é que ele invoca um novo Shell para executar os comandos que estão no seu interior. Desta forma, realmente fomos para o diretório /etc , porém quando todos os comandos dentro dos parênteses foram executados, o novo Shell que estava no diretório /etc morreu e voltamos ao Shell anterior cujo diretório corrente era /home/meudir . Faça outros testes usando cd , e ls para você firmar o conceito.
Agora que já conhecemos estes conceitos veja só este exemplo a seguir:
$ mail suporte << FIM
> Ola suporte, hoje as ‘date "+%H:%M"‘
> ocorreu novamente aquele problema
> que eu havia reportado por
> telefone. Conforme seu pedido
> ai vai uma listagem dos arquivos
> do diretorio:
> ‘ls —l‘
> Abracos a todos.
> FIM
Finalmente agora temos conhecimento para mostrar o que havíamos conversado sobre here document. Os comandos entre crases (` ) serão priorizados e portanto o Shell os executará antes da instrução mail . Quando o suporte receber o e-mail, verá que os comandos date e ls foram executados imediatamente antes do comando mail , recebendo então uma fotografia do ambiente no momento em que a correspondência foi enviada.
O prompt primário default do Shell, como vimos, é o cifrão ($ ), porém o Shell usa o conceito de prompt secundário, ou de continuação de comando, que é enviado para a tela quando há uma quebra de linha e a instrução não terminou. Esse prompt, é representado por um sinal de maior (> ), que vemos precedendo a partir da 2ª linha do exemplo.
Para finalizar e bagunçar tudo, devo dizer que existe uma construção mais moderna que vem sendo utilizada como forma de priorização de execução de comandos, tal qual as crases (` ). São as construções do tipo $(cmd) , onde cmd é um (ou vários) comando que será(ão) executado(s) com prioridade em seu contexto.
Assim sendo, o uso de crases (` ) ou construções do tipo $(cmd) servem para o mesmo fim, porém para quem trabalha com sistemas operacionais de diversos fornecedores (multiplataforma), aconselho o uso das crases, já que o $(cmd) não foi portado para todos os sabores de Shell. Aqui dentro do Botequim, usarei ambas as formas, indistintamente.
Vejamos novamente o exemplo dado para as crases sob esta nova ótica:
$ echo Existem $(who | wc -l) usuários conectados
Existem 8 usuários conectados
Veja só este caso:
$ Arqs=ls
$ echo $Arqs
ls
Neste exemplo eu fiz uma atribuição (= ) e executei uma instrução. O que eu queria era que a variável $Arqs , recebesse a saída do comando ls . Como as instruções de um script são interpretadas de cima para baixo e da esquerda para a direita, a atribuição foi feita antes da execução do ls . Para fazer o que desejamos é necessário que eu priorize a execução deste comando em detrimento da atribuição e isto pode ser feito de qualquer uma das maneiras a seguir:
$ Arqs=`ls`
ou:
$ Arqs=$(ls)
Para encerrar este assunto vamos ver só mais um exemplo. Digamos que eu queira colocar dentro da variável $Arqs a listagem longa (ls -l ) de todos os arquivos começados por arq e seguidos de um único caractere (? ). Eu deveria fazer:
$ Arqs=$(ls -l arq?)
ou:
$ Arqs=`ls -l arq?`
Mas veja:
$ echo $Arqs
-rw-r--r-- 1 jneves jneves 19 May 24 19:41 arq1 -rw-r--r-- 1 jneves jneves 23 May 24 19:43 arq2 -rw-r--r-- 1 jneves jneves 1866 Jan 22 2003 arql
- Pô, saiu tudo embolado!
- Pois é cara, como eu já te disse, se você deixar o Shell “ver” os espaços em branco, sempre que houver diversos espaços juntos, eles serão trocados por apenas um. Para que a listagem saia bonitinha, é necessário proteger a variável da interpretação do Shell, assim:
$ echo "$Arqs"
-rw-r--r-- 1 jneves jneves 19 May 24 19:41 arq1
-rw-r--r-- 1 jneves jneves 23 May 24 19:43 arq2
-rw-r--r-- 1 jneves jneves 1866 Jan 22 2003 arql
- Olhe, amigo, vá treinando esses exemplos, porque, quando nos encontrarmos novamente, vou lhe explicar uma série de instruções típicas de programação Shell. Tchau! Ahh! Só mais uma coisinha que eu ia esquecendo de lhe dizer. Em Shell, o "jogo da velha" (# ) é usado quando desejamos fazer um comentário.
$ exit # pede a conta ao garcon
Vou aproveitar também para mandar o meu jabá: diga para os amigos que quem estiver afim de fazer um curso porreta de programação em Shell que mande um e-mail para a nossa gerencia de treinamento para informar-se.
Qualquer dúvida ou falta de companhia para um chope ou até para falar mal dos políticos é só mandar um e-mail para mim.
Valeu!
|