Guest User

Untitled

a guest
Oct 22nd, 2017
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.35 KB | None | 0 0
  1. # 2. Getting Hands-on with I/O, Redirection Pipes, and Filters
  2.  
  3. 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.
  4.  
  5. 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.
  6.  
  7. 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.
  8.  
  9. Este capítulo abordará os seguintes tópicos em detalhes:
  10.  
  11. * E/S padrão e error streams(fluxo de erros)
  12. * Redirecionamento de Entrada/Saída e erros
  13. * Pipe e pipelines - criando conexão entre os comandos
  14. * Expressões regulares
  15. * Filtragem de saída com `grep`
  16. * Duplicando uma stream com `tee`
  17. * Busca e ordenação em textos unicos
  18. * Tranformação baseada em caracteres com `tr`
  19. * Filtragem baseada em linhas - `head` e `tail`
  20. * Seleção baseada em cortes - `cut`
  21.  
  22. ## Standard I/O and error streams
  23.  
  24. 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.
  25.  
  26. Os exemplos a seguir mostram entrada, saída e erro ao executar comandos:
  27.  
  28. A captura de uma entrada(standard input stream) de usuário pode ser feita da sequinte maneira:
  29.  
  30. ```sh
  31. $ read -p "Digite seu nome: "
  32. Digite seu nome: Lucas
  33. ```
  34.  
  35. A exibição de uma saída(standard output stream) no terminal pode ser feita da seguinte maneira:
  36.  
  37. ```sh
  38. $ echo "Linux Shell Scripting"
  39. Linux Shell Scripting
  40. ```
  41.  
  42. A exibição de um erro(standard error stream) no terminal geralmente se parece com:
  43.  
  44. ```sh
  45. $ cat hello.txt
  46. cat: hello.txt: No such file or directory
  47. ```
  48.  
  49. 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:
  50.  
  51. ```
  52. n° de descrição do arq.| Nome arq. | Descrição
  53. -----------------------+-----------+----------------------------------------------------------
  54. 0 | stdin | Esta é a entrada padrão a ser lida a partir do terminal
  55. 1 | stdout | Esta é a saída padrão para o terminal
  56. 2 | stderr | Este é o erro padrão para o terminal
  57. ```
  58.  
  59. ### File descriptors
  60.  
  61. 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.
  62.  
  63. 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.
  64.  
  65. Para saber a relação de cada descritor a seu respectivo processo, consideremos o seguinte exemplo:
  66.  
  67. Execute um programa qualquer e obtenha o seu `ID`. Considere a execuão do bash como exemplo. Capture o seu `PID`:
  68.  
  69. ```sh
  70. $ pidof bash
  71. 2508 2480 2464 2431 1281
  72. ```
  73.  
  74. 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:
  75.  
  76. ```sh
  77. $ ls -l /proc/2508/fd
  78.  
  79. total 0
  80. lrwx------. 1 sinny sinny 64 May 20 00:03 0 -> /dev/pts/5
  81. lrwx------. 1 sinny sinny 64 May 20 00:03 1 -> /dev/pts/5
  82. lrwx------. 1 sinny sinny 64 May 19 23:22 2 -> /dev/pts/5
  83. lrwx------. 1 sinny sinny 64 May 20 00:03 255 -> /dev/pts/5
  84. ```
  85.  
  86. 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).
  87.  
  88. 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.
  89.  
  90.  
  91. ## Redirecting the standard I/O and error streams
  92.  
  93. 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:
  94.  
  95. ```
  96. Operador | Descrição
  97. ---------+--------------------------------------------------------------------------------
  98. > | redireciona a saída padrão para um arquivo(substitui o conteúdo anterior)
  99. >> | redireciona a saída padrão para um arquivo(concatena com o conteúdo anterior)
  100. < | redireciona a entrada padrão para um arquivo
  101. >& | redireciona o erro padrão para um arquivo(substitui o conteúdo anterior)
  102. >>& | redireciona o erro padrão para um arquivo(concatena com o conteúdo anterior)
  103. | | redireciona a saída para outro comando
  104. ```
  105.  
  106. ### Redirecting standard output
  107.  
  108. 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.
  109.  
  110. Por exemplo, veja como fazer o redirecionamento da saída do comando `echo` para o arquivo `saida.txt`:
  111.  
  112. ```sh
  113. $ echo "Eu estou redirecionando a saida para um arquivo" > saida.txt
  114. $
  115. ```
  116.  
  117. 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`:
  118.  
  119. ```sh
  120. $ cat saida.txt
  121. Eu estou redirecionando a saida para um arquivo
  122. ```
  123.  
  124. Agora, vamos adicionar outra saída ao arquivo `saida.txt`:
  125.  
  126. ```sh
  127. $ echo "Eu estou adicionando outra saida no arquivo" > saida.txt
  128. $ cat saida.txt
  129. Eu estou adicionando outra saida no arquivo
  130. ```
  131.  
  132. 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 **>>**:
  133.  
  134. ```sh
  135. $ echo "Apenas uma linha qualquer" >> saida.txt
  136. $ cat saida.txt
  137. Eu estou adicionando outra saida no arquivo
  138. Apenas uma linha qualquer
  139. ```
  140.  
  141. Também podemos redirecionar a saída de um programa/comando para outro comando usando o operador **|** (pipe):
  142.  
  143. ```sh
  144. $ ls /usr/lib64/ | grep libc.so
  145. libc.so
  146. libc.so.6
  147. ```
  148.  
  149. 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.
  150.  
  151. ### Redirecting standard input
  152.  
  153. 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:
  154.  
  155. ```sh
  156. $ cat saida.txt
  157. Eu estou adicionando outra saida no arquivo
  158. Apenas uma linha qualquer
  159. $ wc -w < saida.txt
  160. 11
  161. ```
  162.  
  163. ou fazer uma ordenação alfabética das linhas:
  164.  
  165. ```sh
  166. $ sort < saida.txt # ordena saida.txt no stdout
  167. Apenas uma linha qualquer
  168. Eu estou adicionando outra saida no arquivo
  169. ```
  170.  
  171. ### Redirecting standard errors
  172.  
  173. 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:
  174.  
  175. ```sh
  176. $ cd /root # tentando acessar o diretorio root com um usario non-root
  177. bash: cd: /root/: Permission denied
  178. O bash exibe a seguinte mensagem de erro: permission denied.
  179. ```
  180.  
  181. 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:
  182.  
  183. ```sh
  184. $ cd / ; ls; cat hello.txt; cd /bin/; ls *.{py,sh}
  185. ```
  186.  
  187. 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:
  188.  
  189. ```
  190. bin boot dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
  191. cat: hello.txt: Arquivo ou diretório não encontrado
  192. amuFormat.sh gettext.sh lprsetup.sh setup-nsssysinit.sh xmms2-client-launcher.sh
  193. event_rpcgen.py lesspipe.sh lsusb.py unix-lpr.sh
  194. ```
  195.  
  196. 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:
  197.  
  198. ```sh
  199. $ (cd / ; ls; cat hello.txt; cd /bin/; ls *.{py,sh}) 2> erro.txt
  200. bin boot dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
  201. amuFormat.sh gettext.sh lprsetup.sh setup-nsssysinit.sh xmms2-client-launcher.sh
  202. event_rpcgen.py lesspipe.sh lsusb.py unix-lpr.sh
  203. ```
  204.  
  205. Pois bem! O erro foi redirecionado para o arquivo `erro.txt`. Para verificá-lo, cheque o arquivo `erro.txt` da seguinte maneira:
  206.  
  207.  
  208. ```sh
  209. $ cat erro.txt
  210. cat: hello.txt: No such file or directory
  211. ```
  212.  
  213. ### Multiple redirection
  214.  
  215. Podemos fazer redirecionamentos combinados em nossos scripts e comandos com `stdin`, `stdout`, e `stderr`.
  216.  
  217. Observe o comando que redireciona tanto `stdout` quanto `stderr`:
  218.  
  219. ```sh
  220. $ (ls /home/ ; cat hello.txt) > log.txt 2>$1
  221. ```
  222.  
  223. 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`:
  224.  
  225. ```sh
  226. $ cat log.txt
  227. lost+found
  228. sinny
  229. cat: hello.txt: No such file or directory
  230. ```
  231.  
  232. O exemplo a seguir mostra o redicionameto de `stdin`, `stdout`, e `stderr`:
  233.  
  234.  
  235. ```sh
  236. $ cat < ~/.bashrc > out.txt 2> err.txt
  237. ```
  238.  
  239. 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`.
  240.  
  241. O script abaixo explicará `stdin`, `stdout`, `stderr` e seu redirecionamento com mais clareza:
  242.  
  243. ```sh
  244. #!/bin/bash
  245. # nomeDoArquivo: redirection.sh
  246. # Descrição: Exemplificando input, output, error
  247. # e seu redirecionamento
  248.  
  249. ps -A -o pid -o command > p_snapshot1.txt
  250. echo "N° de processos em execução no snapshot1: $(wc -l < p_snapshot1.txt)"
  251. echo -n "Cria um processo de pid = "
  252. tail -f /dev/null & echo $! # cria o novo processo
  253. ps -A -o pid -o command > p_snapshot2.txt
  254. echo -n "N° de processos em execução no snapshot2: $(wc -l < p_snapshot2.txt)"
  255. echo -e "\n---------------------------------------------------"
  256. echo "Diferença entre os snapshots"
  257. diff p_snapshot1.txt p_snapshot2.txt
  258. ```
  259.  
  260. 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:
  261.  
  262. ```
  263. $ sh redirection.sh
  264.  
  265. Running process count at snapshot1: 246
  266. Create a new process with pid = 23874
  267. Running process count at snapshot2: 247
  268. Diff bewteen two snapshot:
  269. 246c246,247
  270. < 23872 ps -A -o pid -o command
  271. ---
  272. > 23874 tail -f /dev/null
  273. > 23875 ps -A -o pid -o command
  274. ```
  275.  
  276. ## Pipe and pipelines – connecting commands
  277.  
  278. 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.
  279.  
  280. ### Pipe
Add Comment
Please, Sign In to add comment