A shell tem um grande número de estruturas e sistemas muito úteis que
estão à sua disposição para facilitar a sua interação com o sistema.
Nesta aula nós vamos proceder a uma exploração geral de algumas das
principais estruturas e sistemas, sem pretender nos aprofundar no uso
mais sofisticado de todas as possibilidades existentes. Vamos nos limitar
aqui ao uso interativo básico da shell, sem considerar procedimentos de
programação na linha de comando. Mais adiante haverá outras aulas,
especificamente para tratar de programação de linha de comando, bem como
de shell scripts.
Para seu maior conforto, é melhor fazer estas tarefas no sistema de
janelas X11, mas também é possível realizá-las em um terminal de
caracteres.
- Uma das coisas úteis que a shell pode fazer é completar para você,
de forma automática, os nomes de comandos e arquivos que você começa a
digitar. Para solicitar uma operação de completamento, basta digitar a
tecla [Tab]. Se você já tiver digitado parte do nome de um comando,
uma das seguintes coisas vai acontecer:
- se houver um único comando no sistema com o início do nome igual à
parte que você já digitou, o nome será completado;
- se houver mais de um comando com este mesmo início de nome, será
mostrada uma lista das possíveis alternativas;
- se não houver nenhum comando com este início de nome, você ouvirá
um ``beep'' no altofalante do computador.
Para testar isto, aumente ao máximo o seu terminal xterm, digite o
caracter a seguido de [Tab] e veja o que acontece. Tente o
mesmo com várias outras letras ou combinações de letras.
- O comportamento da shell também é parecido com este no caso do
completamento de nomes de arquivos. Se você já tiver digitado todo o nome
do comando e estiver digitando o nome de um arquivo como argumento do
comando, um [Tab] fará com que a shell tente completar o nome do
arquivo para você. Se você iniciar o nome sem um caracter /, ou
seja, usar um nome de arquivo relativo, o arquivo será procurado pela
shell no seu diretório corrente. As alternativas relativas ao que pode
acontecer quando você faz isto são as mesmas de antes. Isto é conveniente
para lidar com arquivos cujos nomes sejam muito grandes. Crie um arquivo
vazio com um nome ridiculamente grande como
este-e-um-arquivo-vazio-com-um-nome-ridiculamente-grande
Em seguida execute o comando ls -l <arquivo> usando este arquivo
como argumento. Após ter digitado o ls, provavelmente basta digitar
este- e em seguida digitar [Tab].
- No caso do completamento de nomes de arquivos também aparece uma
lista de alternativas possíveis, o que também é muito útil. Imagine, por
exemplo, que você queira ir para algum subdiretório da raiz do sistema
mas tenha esquecido o seu nome. Digite ls / e tecle em seguida [Tab]. Quando aparece a lista, muitas vezes basta digitar uma ou duas
letras iniciais do nome do diretório apropriado e mais um [Tab]
para ser levado para o lugar certo, mas nem sempre, às vezes é necessário
digitar a maior parte do nome. Tente fazer isto, indo para o diretório
/etc/init.d/. Em seguida, volte para casa.
- Uma outra coisa muito útil que a shell faz é lembrar os comandos
que você usou anteriormente. Esta estrutura é chamada de history.
Existe também um comando com este nome, que mostra todos os comandos já
executados. Use-o para ver todos o comandos que você usou até agora. É
provável que você tenha de usar o comando combinado com um paginador,
como por exemplo history | more, para poder ver todos os comandos,
pois a lista completa provavelmente já está um tanto longa.
- É possível passar pela lista de history de comando em comando
usando as teclas de cursor, aquelas marcadas apenas com setas apontando
para cima, para baixo, para a esquerda e para a direita. Vamos denotar
estas teclas por [
], [
], [
] e [
], respectivamente. Se for o caso
de repetir um comando, basta usar a tecla [
] para passar
sucessivamente para cada comando anterior, enquanto a tecla [
] volta para os comandos mais recentes. Uma vez encontrado
o comando que se quer, é possível executá-lo novamente com a tecla [Enter]. Faça isto com alguns dos comandos que você já usou antes.
- Também é possível modificar os comandos da history antes de
executá-los novamente, editando-os de forma simples com o uso das teclas
[
], [
] e [BackSpace]. As
teclas de seta para a esquerda e para a direita farão com que o cursor do
teclado caminhe nas respectivas direções sobre o texto do comando que foi
lembrado, enquanto a tecla [BackSpace] pode ser usada para apagar
caracteres para trás. Use o sistema de history para voltar ao
comando que usou anteriormente para criar um arquivo com o nome
ridiculamente longo e edite o comando antes de executá-lo novamente, para
criar um novo arquivo com o nome ridiculamente longo, tal como
este-e-outro-arquivo-vazio-com-um-nome-ridiculamente-longo
- Observe que, enquanto você está editando um comando, você pode
passar do modo ``insert'' para o modo ``overwrite'' e de volta, digitando
repetidamente a tecla [Insert]. Isto muda a forma como os novos
caracteres que você digita são inseridos na linha de comando. No modo
insert eles são colocados onde estiver o cursor e os caracteres à direita
dele são movidos para a direita para abrir espaço. No modo overwrite, os
novos caracteres são escritos sobre aqueles que estão à direita do
cursor. Dois outros comandos úteis na edição da linha de comando são as
combinações de teclas [Ctrl]-A, que leva o cursor para o início da
linha, e [Ctrl]-E, que leva o cursor para o fim da linha. Estes
dois comandos seguem a sintaxe do editor de texto emacs, como
veremos mais adiante. Para verificar como isto funciona, edite novamente
o comando anterior e experimente as várias possibilidades. Caso você não
queira de fato voltar a executar o comando, a combinação de teclas [Ctrl]-C cancela o comando no qual você está trabalhando.
- Quando se usa wilcards, em geral a shell faz as substituições com
base nos arquivos que existem no diretório onde você está. A wildcard
``*'' representa qualquer conjunto de caracteres seguidos, com a
exceção do caracter ``espaço em branco'' mas incluindo o conjunto vazio.
Para experimentar com esta wildcard, vá para o seu home e digite echo *. Compare o resultado com o output do comando ls. Você
compreende qual foi a substituição que a shell fez, para que o resultado
seja aquele que de fato aparece?
- Para tornar as coisas mais claras, tente digitar também echo
.c* e compare com o output de ls -a. Tente usar também ls *
e ls .c* e compare os resultados. Você entende completamente a
diferença entre o que os comandos echo e ls fazem quando
usados desta forma? Verifique a diferença entre os outputs de echo
. e ls ., que é o mesmo que simplesmente ls. Havendo
necessidade, faça pipelines do output destes comandos pelo paginador more.
- A wildcard ``?'' é usada para representar um único caracter,
que pode ser qualquer um exceto o espaço em branco, mas não pode ser o
conjunto vazio. Para experimentar esta wildcard, tente as combinações
echo .?, echo .??, echo .???, echo .????, echo .?????, echo .?????? e echo .???????. Tente também ls -l .? e ls -l .????? para ver o que acontece. Finalmente, tente
a combinação ls -l .??* das duas wildcards.
- Existe mais um tipo de wildcard que é muito útil, pois ele permite
que nós discriminemos explicitamente um ou mais caracteres de uma string
de caracteres, deixando os demais arbitrários. Isto é feito através do
uso dos brackets quadrados [ ]. A combinação [abc] pode ser
substituída por um único caracter que seja um dos três que estão
discriminados entre os brackets. Para experimentar com esta estrutura,
tente as combinações ls -l .c*, ls -l .l* e ls .[cl]*.
- Também é possível discriminar intervalos de caracteres, ordenados
segundo a sequência ASCII que, para letras, coincide com a ordem
alfabética. Isto é feito colocando-se um hífen entre dois caracteres
dentro dos brackets quadrados. Para experimentar com esta alternativa,
tente as combinações echo .[A-Z]*, echo .[a-z]*, echo
.[A-z]*, echo .[a-Z]* e ls -l .[c-l]*. Além disso, é
possível colocar entre os brackets vários intervalos de caracteres,
separados por vírgulas. Tente, por exemplo, ls -l .[A-Z,a-z]* e
ls -l .[X,c-l]*.
- Vá para a raiz da conta de algum outro usuário, por exemplo para
/home/delyra e, uma vez lá, tente executar de novo todos os
comandos envolvendo wildcards que você acaba de executar em sua própria
conta. Em geral a maior parte das contas de outros usuários estará aberta
para leitura, apesar de que você não poderá escrever nelas. Não há nada
de errado com isto, de fato esta é uma das formas de se promover a livre
troca de informações entre os usuários do sistema. Se quiser, cada
usuário pode fechar, protegendo contra leitura por outros usuários,
quaisquer partes de sua conta.
- Outra estrutura de interesse da shell é a possibilidade de definir
aliases, ou seja, outros nomes para comandos que já existam no sistema.
Assim você pode associar a determinados comandos nomes mais curtos ou que
sejam mais fáceis de lembrar por qualquer motivo. Para ver um exemplo
disto, execute alias dir. Dê uma olhada na página de manual do
comando ls para ver o que a opção -C significa. O comando
alias <nome> mostra a que comando corresponde de fato o nome
discriminado, caso ele seja um alias. Naturalmente, muitas pessoas estão
habituadas a usar o comando dir para listar arquivos, pois ele é
popular em outros sistemas. Com este alias, torna-se a vida destas
pessoas mais fácil no Linux, onde o comando que tem esta função é o ls.
- Outra possível utilidade deste tipo de estrutura é dar ao usuário
inexperiente (ou a todos os usuários) uma proteção adicional, por exemplo
contra a perda acidental de arquivos por se copiar ou movimentar um
arquivo em cima de outro que já exista. Para ver algumas das proteções
que estão instaladas no sistema contra a perda acidental de arquivos,
execute os comandos alias cp, alias mv e alias rm. Olhe
as páginas de manual destes comandos para identificar a função da opção
-i que aparece nestes aliases. Observe que, neste caso, o nome dos
aliases são os mesmos dos comandos originais. A shell sempre procura e
interpreta aliases antes de executar arquivos executáveis contendo
comandos. Para usar o comando original quando há um alias com o mesmo
nome, basta usar o nome do comando precedido de um
\
, como por
exemplo em \rm
.
Mas cuidado, o exemplo que acabamos de mostrar pode ser muito
perigoso! O mesmo cuidado deve ser tomado para os outros casos
mencionados. O comando \rm *
apagará instantaneamente todos os
arquivos do diretório onde você estiver, sem hesitação e sem fazer
perguntas. Só faça este tipo de coisa se estiver absolutamente certo
do que está fazendo!
- Já existe uma grande quantidade de aliases definidos no sistema,
como parte da configuração de ambiente de usuários que implementamos nele
durante a instalação. O comando alias usado sem argumentos mostra
uma lista completa de todos os aliases que existem. Dê uma olhada no
conjunto completo usando a pipeline de comandos alias | more. Você
verá que há aliases definindo várias versões dos comandos mais usados com
os nomes mais populares de outros sistemas. Verifique se você consegue
entender a função de cada um dos aliases existentes.
- Você pode criar os seus próprios aliases usando o comando alias. Usado com dois argumentos, da forma alias <nome> <comando>,
o comando alias atribui o novo nome ao comando especificado. O
comando pode incluir opções e argumentos, pode até mesmo ser uma pipeline
de comandos. Defina um alias que implemente o comando ``xtop'' que foi
discutido anteriormente, executando o comando
alias xtop xterm -e top
Em seguida execute alias xtop para verificar que a operação foi bem
sucedida. Por fim, experimente usar o novo comando executando xtop
para ver se ele de fato funciona. Lembre-se que para sair do top
basta digitar q.
- Já vimos anteriormente que o comando env mostra todas as
variáveis de environment e seus valores. Podemos também obter e usar os
valores de variáveis individuais, usando o nome da variável precedido do
caracter $. Por exemplo, o valor da variável USER pode ser
obtido usando-se $USER. Para verificar isto, execute o comando
echo USER = $USER. Faça o mesmo com as variáveis HOME, TERM, SHELL e DISPLAY. Estas são algumas das variáveis mais
importantes do environment básico do sistema.
- Na tcsh podemos manipular as variáveis de environment usando
o comando setenv. Primeiramente, execute echo $TEST para
verificar que ainda não há uma variável com este nome. Em seguida execute
o comando setenv TEST, seguido mais uma vez de echo $TEST.
Observe o que acontece. O comando setenv usado com um único
argumento cria a variável mas não atribui a ela nenhum valor. Execute
agora o comando setenv TEST 'Este e um teste', seguido mais uma vez
de echo $TEST. Observe a necessidade do uso das aspas, pois o
comando setenv aceita apenas dois argumentos, na forma setenv
<variável> <valor>, de forma que é necessário passar o segundo argumento
na forma de uma única string, incluindo os espaços em branco. Para
terminar, execute o comando unsetenv TEST, seguido mais uma vez de
echo $TEST.
- Como vimos antes, o comando set sem argumentos nos mostra
todas as variáveis da shell. Assim como no caso das variáveis de
environment, podemos obter e usar os valores de variáveis de shell
individuais usando seu nome precedido de um caracter $. Para
verificar isto, execute echo user = $user. Faça o mesmo com as
variáveis home, term e shell. Como você pode ver, a
shell define e mantém algumas variáveis com funções idênticas às
correspondentes variáveis de environment, obtendo dele os valores
apropriados para definir estas variáveis. Existem, entretanto, várias
outras. Execute o mesmo comando com as variáveis uid, tty,
group e gid. Para ilustrar os significados de algumas destas
variáveis, bem como exercitar o uso de variáveis de um modo geral,
execute o comando
echo O usuario $user e identificado pelo numero $uid
Faça algo parecido com as variáveis group e gid.
- Existem também variáveis cujos valores não são fixos, sendo
modificados pela shell dependendo das circunstâncias. Execute, por
exemplo, o comando echo cwd = $cwd. Em seguida, mude para algum
outro diretório e repita o comando. Observe que o nome ``cwd'' é uma
abreviação de ``current working directory''. Uma outra variável
importante deste tipo é a variável status. Para verificar o que ela
faz, execute o comando true seguido imediatamente de echo
$status. Em seguida, execute o comando false seguido mais uma vez
de echo $status. Para entender a diferença, pode ser útil dar uma
olhada nas páginas de manual dos comandos true e false.
- Na tcsh podemos manipular as variáveis de shell usando o
comando set. Primeiramente, execute echo $test para
verificar que não há uma variável com este nome. Em seguida execute o
comando set test, seguido mais uma vez de echo $test. O
comando set usado com um único argumento cria a variável mas não
atribui a ela nenhum valor. Execute agora o comando set test = 'Este
e um teste', seguido mais uma vez de echo $test. Tente executar
também o mesmo comando sem as aspas, set test = Este e um teste,
seguido mais uma vez de echo $test. Observe a diferença de
comportamento em relação às variáveis de environment. Para terminar,
execute o comando unset test, seguido de echo $test.
- As variáveis de shell da tcsh têm uma propriedade adicional,
muito útil, que as variáveis de environment não têm: elas podem ser
definidas como ``arrays'' ou vetores, com um conjunto de valores em vez
de um único valor. Execute a pipeline set | grep argv para filtrar
da lista completa produzida pelo comando set a linha contendo a
descrição da variável argv. Faça o mesmo com a variável path. Observe que há algo de diferente em relação a elas, por comparação
com as outras. Trata-se de variáveis do tipo ``array''. A variável argv, em particular, é muito útil, seus valores são os argumentos de
linha de comando, os argumentos que se coloca em um comando que é
executado na linha de comando. Vamos usar muito esta variável quando
estivermos explorando o mundo dos shell scripts.
- Por enquanto, para compreender como lidar com variáveis do tipo
array, vamos criar uma nós mesmos. Para começar, repita o comando
set test = 'Este e um teste'
seguido desta vez de echo $test[1] e de echo $test[2]. A
variável test foi criada com um único elemento ou ``word'', que é
acessado pelo uso de $test[1], no estilo típico da linguagem C.
Execute agora o comando
set test = ( Este e um teste )
seguido desta vez de echo $test. Aparentemente não há nenhuma
diferença em relação ao caso anterior, onde usamos aspas em vez de
parêntesis. Entretanto, tente agora executar echo $test[1], echo $test[2], echo $test[3] e echo $test[4]. Desta vez a
variável test foi transformada em uma array com quatro
palavras. Enquanto $test representa o valor de todas as palavras,
$test[n] representa o valor da n-ésima palavra. Para
terminar, execute mais uma vez o comando unset test, seguido de
echo $test.
- Vimos que podemos usar aspas simples para definir strings de
caracteres, como em 'Esta e uma string'. Essencialmente o mesmo
efeito pode ser obtido com aspas duplas,
"Esta e uma string"
.
Para ver a diferença que há entre o uso de um tipo ou outro de aspa,
execute os comandos echo '$USER'
e echo "$USER"
. Tente
executar também os comandos echo "'$USER'"
e echo '"$USER"'
para ver o que acontece. Finalmente, tente repetir os exemplos acima
trocando o $ por
$. Você pode combinar estas
estruturas de várias formas diferentes para obter diferentes resultados.
- É possível usar também aspas reversas, que têm um papel
completamente diferente. Se colocarmos um comando ou uma pipeline de
comandos dentro de aspas reversas, a shell tentará executar o que está
ali dentro, como se tivesse sido digitado diretamente na linha de
comando. O resultado da operação resultante é substituído, como uma
string, no local onde estão as aspas reversas e o seu conteúdo. Por
exemplo, vá para o seu home e execute o comando
\ls
, que produzirá
uma listagem de arquivos sem cores. Em seguida, atribua esta lista a uma
variável chamada files executando o comando
set files = `\ls`
Para verificar o resultado, execute echo $files. Em seguida,
execute echo $files[n] para valores apropriados de n. Deve
estar claro que este tipo de coisa será muito útil mais tarde, quando
estivermos programando com a linguagem da shell.