Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # 2. Getting Hands-on with I/O, Redirection Pipes, and Filters
- No dia-a-dia do nosso trabalho encontramos diferentes tipos de arquivos, como arquivos de texto, arquivos fonte em diferentes linguagens de programação (por exemplo, `file.sh`, `file.c` e `file.cpp`), e assim por diante. Muitas vezes precisamos realizar diversas tarefas em arquivos como procurar por uma determinada string ou padrão, substituir strings, imprimir algumas linhas de um arquivo, e assim por diante. Realizar tais tarefas manualmente não é algo fácil. Buscar um padrão em textos(strings) num diretório com milhares de arquivos pode levar meses e é muito fácil cometer erros.
- O shell nos fornece uma gama de comandos super poderosos que torna nosso trabalho mais fácil, rápido e menos sussetível a erros. Os comandos Shell têm a capacidade de manipular e filtrar textos de diferentes *"streams"*(fluxo) de dados, como os da entrada padrão(teclado), arquivos e assim por diante. Alguns desses comandos são `grep`, `sed`, `head`, `tr`, `sort` e por ai vai. O shell também possui uma carcterísca muito interessante que é a capacidade de redirecionar a saída de um comando para outro comando(transformar resultado em argumento de outra 'função') com o uso do pipe(**|**). O uso do pipe ajuda evitar a criação de arquivos temporários desnecessários.
- Se você já conhece o ambiente Linux sabe que cada comando possui uma página de manual mais conhecida como **man** page. Podemos ir diretamenta para a página `man` quando pintar aquela dúvida executando o comando `man <comando>`, exemplo: `man grep`. A maioria dos comandos tem opções como `--help` que exibe uma tela de ajuda simples e `--version` que exibe a versão do programa.
- Este capítulo abordará os seguintes tópicos em detalhes:
- * E/S padrão e error streams(fluxo de erros)
- * Redirecionamento de Entrada/Saída e erros
- * Pipe e pipelines - criando conexão entre os comandos
- * Expressões regulares
- * Filtragem de saída com `grep`
- * Duplicando uma stream com `tee`
- * Busca e ordenação em textos unicos
- * Tranformação baseada em caracteres com `tr`
- * Filtragem baseada em linhas - `head` e `tail`
- * Seleção baseada em cortes - `cut`
- ## Standard I/O and error streams
- No Shell existem diferentes maneiras de fornecer uma entrada(por exemplo, atráves do teclado/terminal) e exibir uma saída(por exemplo, termnial/arquivo) e erro(por exemplo, terminal) caso haja algum durante a execução de um comando ou programa.
- Os exemplos a seguir mostram entrada, saída e erro ao executar comandos:
- A captura de uma entrada(standard input stream) de usuário pode ser feita da sequinte maneira:
- ```sh
- $ read -p "Digite seu nome: "
- Digite seu nome: Lucas
- ```
- A exibição de uma saída(standard output stream) no terminal pode ser feita da seguinte maneira:
- ```sh
- $ echo "Linux Shell Scripting"
- Linux Shell Scripting
- ```
- A exibição de um erro(standard error stream) no terminal geralmente se parece com:
- ```sh
- $ cat hello.txt
- cat: hello.txt: No such file or directory
- ```
- Quando um programa é executado, por padrão três arquivos são abertos em função dele: `stdin`, `stdout` e `stderr`. A tabela a seguir fornece uma breve descrição sobre cada um:
- ```
- n° de descrição do arq.| Nome arq. | Descrição
- -----------------------+-----------+----------------------------------------------------------
- 0 | stdin | Esta é a entrada padrão a ser lida a partir do terminal
- 1 | stdout | Esta é a saída padrão para o terminal
- 2 | stderr | Este é o erro padrão para o terminal
- ```
- ### File descriptors
- Os descritores de arquivo são números inteiros que representam arquivos abertos em um sistema operacional. Cada arquivo aberto recebe o seu descritor. Os descritores de arquivos são iniciados do 0.
- Sempre que um novo processo no Linux é criado, os arquivos de entrada, saída e erro padrão são fornecidos juntamente com outros arquivos necessários para a execução dos processos.
- Para saber a relação de cada descritor a seu respectivo processo, consideremos o seguinte exemplo:
- Execute um programa qualquer e obtenha o seu `ID`. Considere a execuão do bash como exemplo. Capture o seu `PID`:
- ```sh
- $ pidof bash
- 2508 2480 2464 2431 1281
- ```
- Vemos que vários processos bash estão sendo executados. Pegue o PID de algum deles, o `2508` no meu caso, e execute o seguinte comando:
- ```sh
- $ ls -l /proc/2508/fd
- total 0
- lrwx------. 1 sinny sinny 64 May 20 00:03 0 -> /dev/pts/5
- lrwx------. 1 sinny sinny 64 May 20 00:03 1 -> /dev/pts/5
- lrwx------. 1 sinny sinny 64 May 19 23:22 2 -> /dev/pts/5
- lrwx------. 1 sinny sinny 64 May 20 00:03 255 -> /dev/pts/5
- ```
- Podemos observar que os arquivos descritores `0`, `1` e `2` estão associados ao processo `bash`. Atualmente, todos eles estão apontando para `/dev/pts/5.pts`, que é uma espécie de pseudo terminal escravo(terminal slave).
- Pois bem! Tudo o que fizermos com o bash, por exemplo: **entrada**, **saída** e **erro** relacionados a este `PID` será gravado no arquivo`/dev/pts/5`. No entanto, os arquivos `pts` são pseudo-arquivos e o seu conteúdo está na memória, ou seja, não veremos nada nos arquivos ao abrí-los.
- ## Redirecting the standard I/O and error streams
- Temos uma opção para redirecionar entradas/saídas/erros padrão. Podemos redirecionar para um arquivo, para outro comando, outra stream e por ai vai. O redirecionamento por nos ser útil em várias ocasiões. Suponha que eu tenho um script bash cuja saída e erros são exibidos na saída padrão - isto é, **terminal**. Podemos evitar misturar erro com saída redirecionando um deles ou ambos para um arquivo. Diferentes operadores são usados para o redirecionamento. A tabela a seguir mostra alguns dos operadores usados para redirecionamento, juntamente com sua descrição:
- ```
- Operador | Descrição
- ---------+--------------------------------------------------------------------------------
- > | redireciona a saída padrão para um arquivo(substitui o conteúdo anterior)
- >> | redireciona a saída padrão para um arquivo(concatena com o conteúdo anterior)
- < | redireciona a entrada padrão para um arquivo
- >& | redireciona o erro padrão para um arquivo(substitui o conteúdo anterior)
- >>& | redireciona o erro padrão para um arquivo(concatena com o conteúdo anterior)
- | | redireciona a saída para outro comando
- ```
- ### Redirecting standard output
- A saída de um programa/comando pode ser redirecionada para um arquivo. Salvar a saída em um arquivo pode ser útil quando temos que analisar a saída posteriormente. Quando queremos analisar o comportamento de um programa que recebe um número grande de entradas, geralmente redirecionamos as saídas para arquivos.
- Por exemplo, veja como fazer o redirecionamento da saída do comando `echo` para o arquivo `saida.txt`:
- ```sh
- $ echo "Eu estou redirecionando a saida para um arquivo" > saida.txt
- $
- ```
- Notou que a saída não foi exibida no terminal? Isso ocorreu porque ela foi redirecionada para `saida.txt`. O operador **>**(maior que) diz ao shell para redirecionar a saída para um arquivo qualquer cujo nome é indicado logo após o operador. Que no nosso caso é o `saida.txt`:
- ```sh
- $ cat saida.txt
- Eu estou redirecionando a saida para um arquivo
- ```
- Agora, vamos adicionar outra saída ao arquivo `saida.txt`:
- ```sh
- $ echo "Eu estou adicionando outra saida no arquivo" > saida.txt
- $ cat saida.txt
- Eu estou adicionando outra saida no arquivo
- ```
- Percebemos que o arquivo só possui a adição recente. Para manter o conteúdo anterior e concatenar(anexar) a saída redirecionada no arquivo, use o operador **>>**:
- ```sh
- $ echo "Apenas uma linha qualquer" >> saida.txt
- $ cat saida.txt
- Eu estou adicionando outra saida no arquivo
- Apenas uma linha qualquer
- ```
- Também podemos redirecionar a saída de um programa/comando para outro comando usando o operador **|** (pipe):
- ```sh
- $ ls /usr/lib64/ | grep libc.so
- libc.so
- libc.so.6
- ```
- Neste exemplo redirecionamos a saída de `ls` para o comando `grep` usando o operador `|` (pipe) para que o `grep` possa exibir caso encontre a biblioteca `libc.so`(no nosso caso encontrou) em meio à listagem passada a ele.
- ### Redirecting standard input
- Em vez de passar os dados para um programa pela entrada padrão(stdin), podemos redirecionar os dados de um arquivo para o programa usando o operador **<** (menor que). Por exemplo, queremos contar o número de palavras no arquivo `saida.txt` criado anteriormente:
- ```sh
- $ cat saida.txt
- Eu estou adicionando outra saida no arquivo
- Apenas uma linha qualquer
- $ wc -w < saida.txt
- 11
- ```
- ou fazer uma ordenação alfabética das linhas:
- ```sh
- $ sort < saida.txt # ordena saida.txt no stdout
- Apenas uma linha qualquer
- Eu estou adicionando outra saida no arquivo
- ```
- ### Redirecting standard errors
- Exite a possibilidade de obter um erro ao executar um comando/programa no bash devido a alguns motivos como por exemplo uma entrada inválida, argumentos insulficientes, arquivo não encontrado, bugs no programa e por ai vai:
- ```sh
- $ cd /root # tentando acessar o diretorio root com um usario non-root
- bash: cd: /root/: Permission denied
- O bash exibe a seguinte mensagem de erro: permission denied.
- ```
- Em geral, os erros são exibidos no terminal para que seja fácil identificar o motivo do mesmo. Imprimir a saída no terminal em meio à erros pode deixar a compreensão da saída confusa, pois temos que analisar linha a linha manualmente para distinguir o que é e o que não é erro:
- ```sh
- $ cd / ; ls; cat hello.txt; cd /bin/; ls *.{py,sh}
- ```
- No exemplo acima executamos uma sequencia de comandos. Primeiro `cd` para `/`, `ls` no conteúdo de `/`, `cat` no arquivo `hello.txt`, `cd` para `/bin` e listamos arquivos que possuem um nome qualquer, mas terminados em `*.py` ou `*.sh` no diretório `/bin/`. A seguinte saída é exibida:
- ```
- bin boot dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
- cat: hello.txt: Arquivo ou diretório não encontrado
- amuFormat.sh gettext.sh lprsetup.sh setup-nsssysinit.sh xmms2-client-launcher.sh
- event_rpcgen.py lesspipe.sh lsusb.py unix-lpr.sh
- ```
- Vemos que o arquivo `hello.txt` não existe no diretório `/` fazendo com que um erro seja exibido junto ao resultado. Podemos redirecionar o erro da sequinte maneira:
- ```sh
- $ (cd / ; ls; cat hello.txt; cd /bin/; ls *.{py,sh}) 2> erro.txt
- bin boot dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
- amuFormat.sh gettext.sh lprsetup.sh setup-nsssysinit.sh xmms2-client-launcher.sh
- event_rpcgen.py lesspipe.sh lsusb.py unix-lpr.sh
- ```
- Pois bem! O erro foi redirecionado para o arquivo `erro.txt`. Para verificá-lo, cheque o arquivo `erro.txt` da seguinte maneira:
- ```sh
- $ cat erro.txt
- cat: hello.txt: No such file or directory
- ```
- ### Multiple redirection
- Podemos fazer redirecionamentos combinados em nossos scripts e comandos com `stdin`, `stdout`, e `stderr`.
- Observe o comando que redireciona tanto `stdout` quanto `stderr`:
- ```sh
- $ (ls /home/ ; cat hello.txt) > log.txt 2>$1
- ```
- No exemplo acima, `stdout` é redirecionado para `log.txt` e as mensagens de erro também são redirecionadas para `log.txt`. Em `2>&1`, `2>` significa redirecionar um erro e `&1` significa redirecionar para `stdout`. No nosso caso, já redirecionamos `stdout` para o arquivo `log.txt`. Sendo assim, as saídas `stdout` e `stderr` serão escritas em `log.txt` e nada será impresso no terminal. Verifique o resultado exibindo o conteúdo de `log.txt`:
- ```sh
- $ cat log.txt
- lost+found
- sinny
- cat: hello.txt: No such file or directory
- ```
- O exemplo a seguir mostra o redicionameto de `stdin`, `stdout`, e `stderr`:
- ```sh
- $ cat < ~/.bashrc > out.txt 2> err.txt
- ```
- No exemplo, o arquivo `.bashrc` presente no diretório `home` atua como uma entrada para o comando `cat` e sua saída é redirecionada para o arquivo `out.txt` Qualquer erro encontrado durante a execução é redirecionado para o arquivo `err.txt`.
- O script abaixo explicará `stdin`, `stdout`, `stderr` e seu redirecionamento com mais clareza:
- ```sh
- #!/bin/bash
- # nomeDoArquivo: redirection.sh
- # Descrição: Exemplificando input, output, error
- # e seu redirecionamento
- ps -A -o pid -o command > p_snapshot1.txt
- echo "N° de processos em execução no snapshot1: $(wc -l < p_snapshot1.txt)"
- echo -n "Cria um processo de pid = "
- tail -f /dev/null & echo $! # cria o novo processo
- ps -A -o pid -o command > p_snapshot2.txt
- echo -n "N° de processos em execução no snapshot2: $(wc -l < p_snapshot2.txt)"
- echo -e "\n---------------------------------------------------"
- echo "Diferença entre os snapshots"
- diff p_snapshot1.txt p_snapshot2.txt
- ```
- Este script salva dois *snapshots* de todos os processos em execução no sistema e gera a diferença entre eles. A saída do mesmo será algo como:
- ```
- $ sh redirection.sh
- Running process count at snapshot1: 246
- Create a new process with pid = 23874
- Running process count at snapshot2: 247
- Diff bewteen two snapshot:
- 246c246,247
- < 23872 ps -A -o pid -o command
- ---
- > 23874 tail -f /dev/null
- > 23875 ps -A -o pid -o command
- ```
- ## Pipe and pipelines – connecting commands
- Redirecionamos saídas para arquivos quando temos que usá-los posteriormente. Às vezes os arquivos são gerados para servirem de entrada para outros comandos. Podemos evitar esses arquivos temporários "*canalizando*" a saída de um comando na entrada de outro usando o bash pipe e pipelines.
- ### Pipe
Add Comment
Please, Sign In to add comment