Diferenças

Aqui você vê as diferenças entre duas revisões dessa página.

Link para esta página de comparações

Ambos lados da revisão anterior Revisão anterior
unix:shell_avancado [2019/08/12 13:19] mazierounix:shell_avancado [2020/08/18 18:59] (atual) maziero
Linha 1: Linha 1:
 +====== UNIX: Uso avançado do Shell ======
 +
 +Neste texto são apresentados os principais conceitos associados a entradas e saídas padrão, como redirecionamentos e ''pipes''. Também são vistos uma série de programas simples (os filtros), que podem ser muito úteis quando associados através de pipes.
 +
 +===== Entradas e saídas padrão =====
 +
 +A maioria dos comandos UNIX pode comunicar-se com o sistema através de descritores de arquivos especiais conhecidos como entradas e saídas padrão. Eles são:
 +
 +  * Entrada padrão (''stdin'' - //standard input//): onde o comando vai ler seus dados de entrada. No Bash, esse arquivo é referenciado pelo descritor 0.
 +  * Saída padrão (''stdout'' - //standard output//): onde o comando vai escrever seus dados de saída. No Bash, esse arquivo é referenciado pelo descritor 1.
 +  * Saída de erro (''stderr'' - //standard error//): onde o comando vai enviar mensagens de erro. No Bash, esse arquivo é referenciado pelo descritor 2.
 +
 +{{ processo.png }}
 +
 +Quando um comando é lançado sem indicar seu arquivo de trabalho, ele busca seus dados da entrada padrão. Por default, o shell onde o comando foi lançado associa o processo ao seu terminal, ou seja: a entrada padrão do processo é associada ao teclado e as saídas padrão e de erros à tela da sessão corrente.
 +
 +{{ desv-terminal.png }}
 +
 +Um exemplo de uso da entrada e saída padrão é o comando ''rev'', que escreve em sua saída padrão as linhas de texto lidas em sua entrada padrão, invertendo-as:
 +
 +<code>
 +$ rev
 +vamos fazer um teste
 +etset mu rezaf somav                   *
 +temos que achar um palindromo
 +omordnilap mu rahca euq somet          *
 +opoetaamaateopo
 +opoetaamaateopo                        *
 +^D
 +$
 +</code>
 +
 +No exemplo, as linhas marcadas com ''*'' indicam as saídas geradas pelo comando rev. O caractere ''^D'' (//Control-D//) no final indica o final da entrada padrão (ou seja, o fim de arquivo). Ao receber esse caractere, o comando ''rev'' encerra sua execução, pois chegou ao final de seu arquivo de entrada (que neste caso é o teclado). Outro exemplo de uso da entrada e saída padrão é comando ''sort'':
 +
 +<code>
 +$ sort
 +joao
 +maria
 +antonio
 +carlos
 +manoel
 +^D
 +antonio carlos joao manoel maria
 +$
 +</code>
 +
 +Normalmente o shell direciona a entrada padrão para o teclado e a saída padrão para a tela da sessão do usuário, mas ele pode ser instruído para redirecioná-las para arquivos ou mesmo para outros programas, como será visto na seqüência.
 +
 +===== Redireção para arquivos =====
 +
 +O shell pode redirecionar as entrada e saídas padrão de comandos para arquivos normais no disco, usando operadores de redireção, como mostra a figura abaixo:
 +
 +{{ desv-arquivos.png }}
 +
 +<note important>
 +A sintaxe de redireção é específica para cada shell, isto é, não é a mesma entre o C-Shell e o Bourne Shell; aqui será apresentada a sintaxe do shell Bash.
 +</note>
 +
 +Os principais operadores de redireção para arquivos são:
 +
 +  * Saída em arquivo: a saída padrão (//stdout//) do comando é desviada para um arquivo usando o operador ''>'':
 +
 +<code>
 +$ ls > listagem.txt
 +</code>
 +
 +  * Entrada de arquivo: a entrada padrão (//stdin//) pode ser obtida a partir de um arquivo usando o operador ''<'':
 +
 +<code>
 +$ rev < listagem.txt
 +</code>
 +
 +  * Uso combinado: os dois operadores podem ser usados simultaneamente.
 +
 +<code>
 +$ rev < listagem.txt > listrev.txt
 +</code>
 +
 +  * Concatenação: a saída padrão pode ser concatenada a um arquivo existente usando-se o operador ''>>'', como mostra o exemplo:
 +
 +<code>
 +$ ls /etc >> listagem.txt
 +</code>
 +
 +  * Saída de erros: a saída de erros (//stderr//) também pode ser redirecionada, através do operador ''2>'' (que faz referência ao descritor 2):
 +
 +<code>
 +$ ls /xpto > teste.txt
 +ls: /xpto: No such file or directory
 +
 +$ ll /xpto 2> erro.txt
 +$ cat error.txt
 +ls: /xpto: No such file or directory
 +</code>
 +
 +  * As saídas padrão e de erro podem ser redirecionadas de forma independente:
 +
 +<code>
 +$ ll /xpto /etc/passwd > acerto.txt 2> erro.txt
 +
 +$ cat error.txt
 +ls: /xpto: No such file or directory
 +
 +$ cat acerto.txt
 +-rw-r--r-- 1 root root 2136 Mai 14 17:02 /etc/passwd 
 +</code>
 +
 +  * Além disso, a saída de erro pode ser sobreposta à saída padrão:
 +
 +<code>
 +$ ll /xpto /etc/passwd > acerto.txt 2>&1
 +
 +$ cat acerto.txt
 +-rw-r--r-- 1 root root 2136 Mai 14 17:02 /etc/passwd ls: /xpto: No such file or directory
 +</code>
 +
 +  * Forçar um desvio: Caso a saída seja redirecionada para um arquivo já existente, o shell recusa a operação indicando o erro (somente se a variável ''noclobber'' estiver setada através do comando ''set -C''). Essa operação pode ser forçada através do operador ''!'':
 +
 +<code>
 +$ ls > teste.txt
 +teste.txt: File exists.
 +
 +$ ls >! teste.txt
 +
 +$ ls >> novo.txt
 +novo.txt: No such file or directory
 +
 +$ ls >>! novo.txt
 +</code>
 +
 +===== Redireção usando pipes =====
 +
 +O shell permite a construção de comandos complexos através da combinação de vários comandos simples. O operador ''|'', conhecido como //pipe//, ou tubo, permite conectar a saída padrão de um comando à entrada padrão de outro. Com isso, um mesmo fluxo de dados pode ser tratado por diversos comandos consecutivamente, como mostra a figura:
 +
 +{{ desv-pipes.png }}
 +
 +É importante ressaltar que os comandos conectados são lançados simultaneamente pelo shell e executam ao mesmo tempo. O shell controla a execução de cada um para que não haja acumulo de dados entre os comandos (a cada pipe é associado um buffer de tamanho limitado).
 +
 +A sintaxe usada para redireção é simples. Eis alguns exemplos:
 +
 +<code>
 +$ ls -l /etc | more
 +$ ls -l /tmp | sort | more
 +$ ls -l /usr/bin | cut -c31-40 | sort | more
 +</code>
 +
 +O mecanismo de redireção de entrada/saída é genérico, ou seja, funciona para qualquer programa que use as entradas e saídas padrão, em qualquer linguagem de programação.
 +
 +===== Filtros =====
 +
 +Um filtro é basicamente um programa que lê dados da entrada padrão, realiza algum processamento e escreve os dados resultantes na saída padrão. Um exemplo simples de filtro seria:
 +
 +<code c filtro.c>
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +// lê caracteres em stdin e escreve em stdout, convertendo
 +// vogais minúsculas em '*' e vogais maiúsculas em '#'.
 +
 +int main ()
 +{
 +  char c ;
 +
 +  c = getchar () ;
 +  while (c != EOF)
 +  {
 +    switch (c)
 +    {
 +      case 'a':
 +      case 'e':
 +      case 'i':
 +      case 'o':
 +      case 'u':
 +         c = '*' ;
 +         break ;
 +      case 'A':
 +      case 'E':
 +      case 'I':
 +      case 'O':
 +      case 'U':
 +         c = '#' ;
 +         break ;
 +    }
 +    putchar (c) ;
 +    c = getchar () ;
 +  }
 +  return (0);
 +}
 +</code>
 +
 +Para compilar esse filtro basta digitar: ''gcc -o filtro filtro.c''. Uma vez compilado, o arquivo executável ''filtro'' pode ser usado nas linha de comando UNIX, como qualquer outro filtro.
 +
 +Existe um grande número de comandos UNIX bastante simples, cujo uso direto é pouco útil, mas que podem ser de grande valia quando associados entre si através de pipes. Esses comandos são chamados filtros, porque funcionam como filtros para o fluxo de dados. Eis alguns filtros de uso corrente:
 +
 +  * ''cat'' : concatena diversos arquivos na saída padrão
 +  * ''tac'' : idem, mas inverte a ordem das linhas
 +  * ''more'' : permite a paginação do fluxo de dados
 +  * ''tr'' : troca de caracteres entre dois conjuntos
 +  * ''head'' : seleciona as ''n'' linhas iniciais do fluxo de dados
 +  * ''tail'' : seleciona as ''n'' linhas finais do fluxo de dados
 +  * ''wc'' : conta o número de linhas, palavras e bytes do fluxo
 +  * ''sort'' : ordena as linhas segundo critérios ajustáveis
 +  * ''uniq'' : remove linhas repetidas, deixando uma só linha
 +  * ''sed'' : para operações complexas de strings (trocas, etc)
 +  * ''grep'' : seleciona linhas contendo uma determinada expressão
 +  * ''cut'' : seleciona colunas do fluxo de entrada
 +  * ''rev'' : reverte a ordem dos caracteres de cada linha do fluxo de entrada
 +  * ''tee'' : duplica o fluxo de entrada (para um arquivo e para a saída standard)
 +  * ... : qualquer programa que leia dados de stdin e escreva sua saída em stdout pode ser usado como filtro
 +
 +Para conhecer melhor cada um dos comandos acima, basta consultar suas respectivas páginas de manual.
 +
 +===== Exercícios =====
 +
 +  - Usando comandos e //pipes//, determine o número de linhas da página de manual do shell Bash.
 +  - Determine quanto arquivos normais (não diretórios nem links) existem em ''/usr''.
 +  - Monte uma linha de comandos usando //pipes// para identificar todos os usuários proprietários de arquivos ou diretórios a partir de'' /tmp'', colocando o resultado no arquivo ''users-tmp.txt''. Siga os seguintes passos:
 +    * Use o comando ''find ''para listar os proprietários de todos os arquivos dentro de ''/tmp'' (dica: use a opção ''-printf'' do comando ''find'').
 +    * Ordene a listagem obtida, usando o comando ''sort''
 +    * Remova as linhas repetidas, usando o comando ''uniq''
 +    * Direcione a saída para o arquivo indicado ''users-tmp.txt''.
 +  - Use o comando cut na saída de um comando ls -l para mostrar apenas as permissões dos arquivos no diretório /etc. Depois use sort e uniq para mostrar quantas permissões diferentes existem naquele diretório.
 +  - Quantos arquivos invisíveis (iniciados com .) há na sua área HOME?
 +  - Quantos diretórios há na sua área HOME?
 +  - Liste todos os atributos de todos os arquivos de um diretório e utilize o cut para mostrar apenas suas permissões e seu nome.
 +  - Liste todos os arquivos e seus atributos (somente os arquivos, diretórios não devem aparecer) do diretório /etc, ordenando a saída por data do arquivo, e guarde a saída no arquivo teste.txt na sua área.
 +  - Mostre apenas o vigésimo arquivo do diretório /etc
 +  - Mostre apenas os arquivos e diretórios para os quais você tem permissão de execução na sua área HOME.
 +  - Acesse o servidor ssh.inf.ufpr.br. Utilize o comando finger para mostrar o Login de todos usuários cujo primeiro nome seja Daniel.
 +  - Execute os comandos a seguir como usuário normal. Determine o que é stdin, stdout e stderr para cada comando (o conteúdo de cada fluxo para cada comando):
 +    - ''cat nonexistentfile''
 +    - ''file /sbin/ifconfig''
 +    - ''grep root /etc/passwd /etc/nofiles > grepresults''
 +    - ''/etc/init.d/sshd start > /var/tmp/output''
 +    - ''/etc/init.d/crond start > /var/tmp/output 2>&1''
 +    - Confira seu resultado repetindo os comandos e atribuindo //stdout// para ''$HOME/saida.txt'' e //stderr// para ''$HOME/erro.txt''.
 +  - Observe as seguintes sequências de comandos e responda às perguntas: <code>
 +$ mkdir vazio
 +$ cd vazio
 +$ cp a b
 +cp: cannot stat 'a': No such file or directory
 +$ cp a b >a
 +</code>
 +    - Por que não há mensagem de erro após o segundo comando ''cp''? Qual o conteúdo do arquivo ''a''? <code>
 +$ date >a
 +$ cat a
 +Wed Feb  8 03:01:21 EST 2012
 +
 +$ cp a b
 +$ cat b
 +Wed Feb  8 03:01:21 EST 2012
 +
 +$ cp a b >a
 +$ cat b
 +</code>
 +    - Por que o arquivo ''b'' está vazio? O que há no arquivo ''a''?