Definição |
Os comandos digitados pelo usuário são interpretados pelo shell. Esses comandos podem ser comandos embutidos do shell, mas na maioria das vezes eles são programas externos.
help
O shell analisa sintaticamente a linha de comando depois que ela é lida. A variável de ambiente IFS determina como isso é feito. Normalmente, IFS é configurada de tal forma que espaços em branco separam os diferentes argumentos de linha de comando.
Existem vários shells para Linux, onde cada shell tem seus próprios recursos, capacidades e limitações. Por exemplo, o shell padrão para a distribuição Conectiva é o bash.
Na realidade, o shell é apenas um arquivo executável armazenado em /bin. No modo gráfico, um shell é executado em cada terminal aberto.
Para ver qual é o seu shell padrão, basta digitar o comando
printenv SHELL
O comando acima exibe o conteúdo da variável ambiente SHELL que contém o caminho completo do shell. Outra forma de saber qual é o shell padrão, é verificar o último parâmetro definido para o usuário no arquivo /etc/passwd. Por exemplo,
aluno:x:501:501::/home/aluno:/bin/bash
mostra que o usuário aluno usa o shell bash.
Carregando o shell |
Suponha que o shell padrão do sistema seja o bash. Então, quando o usuário acessa o sistema, o bash carrega os seguintes arquivos do diretório home do usuário (estes arquivos são criados automaticamente pelo comando adduser, veja o diretório /etc/skel para saber quais são os arquivos padrão no seu sistema):
e os seguintes arquivos do diretório /etc:
Os arquivos no diretório home são exclusivos do usuário e portanto, uma alteração nestes arquivos afeta apenas o usuário em questão. Os arquivos do /etc são comuns a todos os usuários e portanto, uma alteração nestes arquivos afeta todos os usuários do sistema.
Os arquivos carregados pelo shell definem as variáveis de ambiente, que nada mais são que definições e valores que o shell e os outros programas do sistema reconhecem. Para ver quais as variáveis de ambiente no seu sistema você pode digitar printenv, env ou set. Por exemplo, são algumas das variáveis de ambiente do bash:
É possível criar novas variáveis, excluir variáveis existentes ou apenas alterar o conteúdo de uma variável de ambiente.
Para criar uma nova variável de ambiente, basta definir o nome e o valor da nova variável de ambiente e usar o comando export para permitir que a variável seja visualizada pelos aplicativos (por exemplo, um novo shell ou um novo terminal) inicializados no mesmo terminal (neste caso a variável existirá enquanto a sessão estiver aberta). Por exemplo,
TESTE=10; export TESTE
ou
export TESTE=10
cria a variável de ambiente TESTE com valor inicial 10. O comando export faz com que a nova variável seja conhecida por todos os processos a partir deste shell. Os nomes das variáveis de ambiente são, tradicionamente, definidas usando apenas letras maiúsculas. Entretanto, isto não é obrigatório. Você pode também usar letras minúsculas. Mas, CUIDADO! O Linux é case sensitive. Isto significa que o sistema diferencia letras maiúsculas de letras minúsculas. Portanto, o comando
teste=10; export teste
cria uma nova variável de ambiente chamada teste e que é diferente da variável TESTE criada anteriormente.
É importante observar que as variáveis de ambiente definidas a partir da linha de comando são temporárias. Para criar uma variável permanente, deve-se acrescentar a definição e o comando export no arquivo .bash_profile (no caso de shell bash).
Para excluir uma variável de ambiente, deve-se usar o comando unset. Por exemplo, o comando
unset teste
exclui a variável de ambiente teste criada no exemplo anterior.
Para alterar o valor de uma variável de ambiente, basta fornecer o nome da variável e o novo valor a ser atribuido a variável. Para a variável de ambiente TESTE definida anteriormente nesta seção, podemos digitar
TESTE=200
e a variável TESTE passa a ter valor 200. Neste caso, o conteúdo da variável é completamente alterado. Pode ser que ao invés de alterar o conteúdo, você queira apenas acrescentar mais alguma informação ao conteúdo armazenado em uma variável. Por exemplo, suponha que você queira acrescentar o diretório /teste/bin no caminho de busca, ou seja, no PATH. Devemos, então digitar
PATH=$PATH:/teste/bin
O símbolo $ usado acima antes de PATH informa ao shell para usar o conteúdo da variável de ambiente PATH.
Suponha agora que temos USER=aluno e que queremos USER=aluno linux. Então podemos definir
USER="$USER Linux"
As aspas acima são necessárias devido ao espaço em branco existente no novo conteúdo da variável USER. Note que novamente usamos $ para indicar ao shell que se deve substituir o nome USER pelo conteúdo da variável ambiente USER.
Os exemplos mostrados acima são alterações temporárias no ambiente do usuário. Para tornar uma alteração efetiva, deve-se alterar o arquivo profile do usuário (.bash_profile no shell bash). Por exemplo, suponha que queremos personalizar o prompt das linhas de comando. Podemos então incluir no arquivo de profile
PS1="[\W]\$"
e
export PS1
onde o novo prompt apenas exibe o nome do diretório atual de trabalho do usuário (\W) e o símbolo $ ( \$ ).
Consulte o manual on line para conhecer um pouco mais sobre os comandos embutidos no seu shell (por exemplo, man bash para ler sobre o script bash).
Usando as variáveis de ambiente em um programa C |
O terceiro argumento da função main() é a lista de variáveis de ambiente. Em um programa C, pode-se usar a função putenv() para criar/alterar variáveis de ambiente, a função getenv() para obter o valor das variáveis e a função unsetenv() para remover variáveis.
O exemplo mostrado a seguir exibe um dump das variáveis de ambiente e de algumas funções de C, antes e depois, que algumas modificações são introduzidas no ambiente do sistema.
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<string.h>
5 #include<errno.h>
6 void imprime_variavel(const char *desc, const void *addr)
7 {
8 printf("0x%08lX %-s\n", (long)addr, desc);
9 }
10 void dump(char * const envp[], const char *cabecalho)
11 {
12 int x; /* índice envp[x] */
13 /* Exibe um título */
14 printf("\n%s\n\n", cabecalho);
15
16 /* Exibe as variáveis de ambiente */
17 for (x = 0; envp[x] != 0; ++x)
18 {
19 imprime_variavel(envp[x], envp+x); /* imprime variável */
20 }
21 /* Exibe outras variáveis */
22 imprime_variavel("stderr",stderr);/* Dump do endereço de stderr */
23 imprime_variavel("stdout",stdout);/* Dump do endereço de stdout */
24 imprime_variavel("stdin", stdin); /* Dump do endereço de stdin */
25 imprime_variavel("dump", dump); /* Dump do endereço de main */
26 }
27 int main(int argc, char * const argv[], char * const envp[])
28 {
29 /* Faz dump das variáveis de ambiente */
30 dump(envp, "AMBIENTE INICIAL:");
31 /* inclui duas novas variáveis de ambiente */
32 if (putenv("TESTE1=100") == -1)
33 {
34 printf("%s: putenv()\n", strerror(errno));
35 exit(1);
36 }
37 if (putenv("TESTE2=teste de variaveis") == -1)
38 {
39 printf("%s: putenv()\n", strerror(errno));
40 exit(2);
41 }
42 /* altera valor da variável de ambiente LOGNAME */
43 if (putenv("LOGNAME=linux") == -1)
44 {
45 printf("%s: putenv()\n", strerror(errno));
46 exit(3);
47 }
48 /* Faz dump das variáveis de ambiente */
49 dump(envp, "AMBIENTE Modificado:");
50 /* Verifica o valor de LOGNAME, TESTE1 e TESTE2 */
51 printf("\ngetenv(LOGNAME) = %s;", getenv("LOGNAME"));
52 printf("\ngetenv(TESTE1) = %s;", getenv("TESTE1"));
53 printf("\ngetenv(TESTE2) = %s;\n", getenv("TESTE2"));
54 return 0;
55 }
Uma observação interessante em relação ao programa acima diz respeito ao envp[] antes e depois da alteração da variável LOGNAME e da inclusão das variáveis TESTE1 e TESTE2 : o array envp[] não se altera. Isto significa que as alterações introduzidas pelo programa no ambiente são feitas em outra área de memória e monitoradas pelo sistema enquanto o programa está sendo executado. Não é possível alterar as variáveis de ambiente do shell pois as variáveis do shell são definidas na própria memória privada do shell.
Alteração do shell |
É possível mudar de shell em tempo de execução. Para isto, basta digitar o nome do novo shell na linha de comandos. Para encerrar o novo shell e voltar para o shell anterior basta digitar exit. Para ver quais os shells que estão disponíveis no sistema, basta digitar
chsh -l
Na realidade, este comando exibe o conteúdo do arquivo /etc/shells. Para mudar o shell padrão, o usuário pode usar o comando chsh (change shell). Suponha, por exemplo, que queremos adotar como padrão o shell ash, então podemos digitar
chsh -s /bin/ash
O Linux apenas pedirá a senha do usuário logado para confirmar a alteração. Na próxima vez que o usuário logar no sistema, o ash será o seu shell padrão. O uso do comando chsh, sem parâmetros, fará com que, além da senha, o Linux também solicite o nome do novo shell. Neste caso, deve-se fornecer o caminho completo do novo shell.
É também importante observar que quando uma nova sessão é aberta com o comando bash, apenas os arquivos /etc/bash e ~/.bashrc são lidos. Nestes arquivos são colocados os comandos que devem continuar existindo na nova sessão como, por exemplo, os aliases.
Programação em shell script |
Como vimos acima, o shell funciona como mediador entre o usuário e o kernel do sistema. Quando o usuário digita um comando, o shell analisa o comando e solicita ao kernel a execução das ações correspondentes ao comando em questão.
Normalmente, o usuário (principalmente se ele é o administrador do sistema) executa um mesmo conjunto de tarefas diariamente. O ideal então é que o usuário possa automatizar as suas tarefas, ou seja, o usuário digita um único comando e o shell o interpreta como um conjunto de comandos a serem executados pelo sistema. Este tipo de automatização de tarefas é possível através da programação shell. Abaixo mostramos alguns exemplos de scripts.
EXEMPLO 1
Suponha que você queira bloqueiar o acesso de um usuário ao sistema e caso o usuário tente acessar o sistema mostrar uma mensagem de aviso. Para isto é necessário: criar um shell script que será usado na inicialização da conta do usuário e definir este script para ser chamado quando o usuário logar na conta. Por exemplo, queremos bloqueiar a conta do usuário aluno. Então, devemos
a) criar o script abaixo (sem a numeração do lado esquerdo)
1 #!/bin/sh
2 echo '***********************************************'
3 echo '* Sua conta foi encerrada. Procure o suporte. *'
4 echo '***********************************************'
5 sleep 10s
6 exit
b) alterar o arquivo /etc/passwd para que o novo script seja chamado quando o usuário logar no sistema.
aluno:x:501:501::/home/aluno:/bin/nsh
Agora, quando o usuário aluno tentar logar no sistema, ele receberá a mensagem que a conta foi encerrada.
EXEMPLO 2
Abaixo temos um segundo exemplo de script de shell. Este script mostra quais as permissões de acesso do usuário em relação a um determinado arquivo.
1 #!/bin/sh
2 echo -n 'Forneça o nome do arquivo a ser verificado: '
3 read
4 if [ $REPLY ] #usuário digitou o nome do arquivo?
5 then
6 arq=$REPLY
7 if [ -e $arq ]; then # o arquivo existe ?
8 echo 'o arquivo existe'
9 if [ -r $arq ]; then # o usuário pode ler o arquivo?
10 echo 'você pode ler o arquivo'
11 else
12 echo 'você não pode ler o arquivo'
13 fi
14 if [ -w $arq ]; then # o usuário pode alterar o arquivo?
15 echo 'você pode alterar o arquivo'
16 else
17 echo 'você não pode alterar o arquivo'
18 fi
19 if [ -x $arq ]; then # o usuário pode executar o arquivo?
20 echo 'você pode executar o arquivo'
21 else
22 echo 'você não pode executar o arquivo'
23 fi
24 if [ -d $arq ]; then # o arquivo é um diretório?
25 echo 'O arquivo é um diretório'
26 fi
27 else
28 echo 'o arquivo nâo existe'
29 fi
30 else
31 echo 'Você não forneceu o nome do arquivo'
32 fi
33 exit
Em relação ao script acima podemos comentar:
echo 'TESTE = $TESTE'
echo "TESTE = $TESTE"
mostram, respectivamente, os seguintes resultados
TESTE = $TESTE
TESTE = Maria
No primeiro caso, o uso de aspas simples informa ao bash para imprimir o conteúdo do string, sem nenhuma preocupação adicional. No segundo caso, o uso de aspas duplas faz com que o sh substitua o nome da variável TESTE pelo seu conteúdo. O símbolo $ indica ao sh quem é variável dentro do string.
O comando read da linha 3 recebe o nome do arquivo digitado pelo usuário e o armazena na variável padrão REPLY. Pode-se também usar uma variável qualquer para receber o nome do arquivo. Por exemplo, você pode alterar a linha 3 para read arq. Neste caso, o sh passa a executar duas ações quando interpreta a linha 3: primeiro, cria a variável arq, e segundo, armazena o nome do arquivo nesta variável.
A linha 4 do script verifica se o usuário digitou algo (ele pode ter apenas teclado ENTER). O comando if possui a seguinte estrutura
if [ condição ]
then
comandos
else
comandos
fi
Antes de discutirmos o script, algumas observações em relação ao comando if tornam-se necessárias. A condição (ou condições) a ser testada deve ser colocada entre colchetes (pode-se também usar explicitamente a palavra test no lugar dos colchetes, por exemplo, você pode substituir if [ $REPLY ] por if test $REPLY). Além disso, deve existir um espaço entre a condição e o colchete (abrindo e/ou fechando). O sh retorna 0 ou 1 como resultado do teste, dependendo se a condição é verdadeira (valor zero) ou falsa (valor 1). Caso a condição seja verdadeira, são executados os comandos definidos logo após o comando then. Caso a condição seja falsa, são executados os comandos logo após o comando else (você não é obrigado a definir um else para cada if). O comando if é fechado com um comando fi.
Olhando novamente o segundo exemplo de script podemos notar a seguinte estrutura
4 if [ $REPLY ] #usuário digitou o nome do arquivo?
5 then
executa linhas de 6 a 29
30 else
executa linha 31
32 fi
A linha 4 verifica se a variável REPLY tem algum valor armazenado. Caso o resultado do teste seja verdadeiro, são executados os comandos da linha 6 a linha 29; caso o resultado do teste seja falso, apenas a linha 32 é executada.
A linha 6 define a variável arq e copia o conteúdo da variável REPLY para a nova variável. Podemos aqui fazer três observações: primeiro, não existe espaço em branco entre as variáveis e o símbolo de atribuição ("="); segundo, a criação da variável arq não é necessária, poderíamos continuar usando a variável REPLY no resto do script; e terceiro, a variável arq é uma variável local (não existe um comando export para esta variável), isto significa que esta variável existe somente durante a execução do script.
A linha 7 mostra o comando if com o operador -e. O uso deste operador faz com que o sh verifique a existência do arquivo cujo nome foi informado pelo usuário. Podemos ver que o script tem a seguinte estrutura a partir deste teste
7 if [ -e $arq ]; then # o arquivo existe ?
executa linhas de 8 a 26
27 else
executa linha 28
29 fi
Note que temos dois comandos na linha 7: o comando if e o comando then. Quando mais de um comando são colocados em uma mesma linha, eles devem ser separados por um ponto-e-vírgula.
As linhas de 8 a 26 utilizam operadores junto com o comando if para verificar as permissões do arquivo. Abaixo mostramos alguns operadores que podem ser usados para testar arquivos (digite man test para obter mais informações).
-b : o arquivo existe e é um arquivo é especial de bloco ?
-c : o arquivo existe e é um arquivo é especial de caractere ?
-d : o arquivo existe e é um diretório ?
-e : o arquivo existe ?
-f : o arquivo existe e é um arquivo normal ?
-r : o arquivo existe e o usuário pode lê-lo ?
-s : o arquivo existe e tem tamanho maior que zero ?
-x : o arquivo existe e o usuário pode executá-lo ?
-w : o arquivo existe e o usuário pode alterá-lo ?
-G : o arquivo existe e pertence ao grupo do usuário ?
-L : o arquivo existe e é um link simbólico ?
-O : o arquivo existe e o usuário é dono do arquivo ?
arq1 nt arq2 : o arquivo arq1 é mais novo que o arquivo arq2 ?
arq1 ot arq2 : o arquivo arq1 é mais antigo que o arquivo arq2 ?
EXEMPLO 3
O terceiro script é uma ferramenta de backup para arquivos com extensão txt. O usuário fornece o nome do diretório e todos os arquivos .txt deste diretório são copiados para o diretório backup. Se o diretório backup já existe, ele é inicialmente apagado e depois criado.
1 #!/bin/sh
2 if [ ! $1 ]
3 then
4 echo 'Você deve fornecer o nome do diretório'
5 exit 1
6 fi
7 if [ ! -d $1 ]
8 then
9 echo "$1 não é um diretório"
10 exit 1
11 fi
12 rm -fr backup
13 mkdir ~/backup
14 for i in $1/*.txt; do
15 echo $i
16 cp -f $i ~/backup/
17 done
18 exit
Podemos comentar em relação ao script acima:
EXEMPLO 4
O quarto exemplo de script é o arquivo .bash_profile usado pelo sistema para definir o ambiente de trabalho do usuário durante o processo de inicialização.
1 # .bash_profile
2 if [ -f ~/.bashrc ]; then
3 . ~/.bashrc
4 fi
5 # User specific environment and startup programs
6 PS1="[\W]\$"
7 PATH=$PATH:$HOME/bin
8 BASH_ENV=$HOME/.bashrc
9 USERNAME=""
10 export USERNAME BASH_ENV PATH PS1
Podemos notar:
É importante observar que existe outra forma de executar os scripts. Ao invés de alterar as permissões de acesso do script com o comando chmod, pode-se simplesmente especificar o nome do shell na linha de comando. Por exemplo, suponha que queremos executar o arquivo teste usando o script bash. Então podemos digitar
bash teste