Analisando e transformando textos no Bash
Curso de Bash
Seção do curso de Bash sobre processamento de textos com wc, tr, diff, patch e paste.
Comandos para transformar, analisar e extrair dados de arquivos de texto.
Conteúdo
Contando linhas, palavras e caracteres com wc
Utilizaremos o arquivo criado abaixo nesta seção.
printf "X Y\nN\n" > contagem
cat contagem
X Y
N
wc conta a quantidade de linhas, caracteres e palavras de arquivos de texto.
A opção -l exibe a quantidade de novalinha (que é o caractere inserido quando pressionamos Enter para encerrar cada linha e iniciar a próxima) seguida do nome do arquivo.
wc -l contagem2 contagemA opção -w exibe a quantidade de palavras seguida do nome do arquivo.
wc -w contagem3 contagemA opção -m exibe a quantidade de caracteres seguida do nome do arquivo.
wc -m contagemespaço e novalinha são contados também no arquivo contagem, por isso o resultado é 6.
6 contagemSem opções, wc exibe quantidade de linhas, palavras, bytes e nome do arquivo.
wc contagem2 3 6 contagemSem argumentos ou com - como argumento, wc lê a entrada padrão. Pressione Ctrl + D uma vez para terminar quando em uma linha vazia e duas quando conteúdo foi digitado.
wcAqui Bash foi digitado e Ctrl + D pressionado duas vezes. A saída de wc é exibida na mesma linha.
Bash 0 1 4Abaixo Bash foi digitado, Enter foi pressionado e então Ctrl + D pressionado uma vez.
Bash
1 1 5
Note que o primeiro número passou de 0 para 1 entre as duas saídas pois Enter inseriu o caractere "invisível" novalinha. E a quantidade de bytes aumentou de 4 para 5 pelo mesmo motivo.
⚠️ bytes e caracteres não são intercambiáveis.
P. ex. á equivale a um caractere e dois bytes.
wc -mNa saída abaixo, á foi digitado e Ctrl + d pressionado duas vezes. 1 é a saída efetiva de wc.
á1A opção -c de wc conta a quantidade de bytes.
wc -cá2Com mais de um argumento, wc exibe a soma.
wc -w contagem contagem 3 contagem
3 contagem
6 total
Substituindo caracteres com tr
tr remove ou substitui um ou mais caracteres em textos.
tr não recebe arquivos como argumento, apenas por meio de redirecionamento de entrada. O operador < realiza o redirecionamento do arquivo à direita.
O arquivo frase abaixo será utilizado ao longo desta seção.
printf "Bash, \n1 shell para Linux.\n" > frase
cat frase
Bash,
1 shell para Linux.
Removendo caracteres com -d
A opção -d de tr remove os caracteres enviados como argumento. O primeiro argumento é chamado de conjunto1.
Abaixo, os dois l de shell são removidos.
tr -d l < fraseBash,
1 she para Linux.
Todos caracteres a serem excluídos devem ser definidos no primeiro argumento, sem espaço entre eles. A ordem em que aparecem é indiferente: hl, como abaixo, é equivalente à lh.
tr -d hl < fraseBas,
1 se para Linux.
Sequências de caracteres podem ser definidas utilizando o formato inicio-fim, novamente sem espaço entre os caracteres.
No argumento as-x0-2 abaixo é especificado que a letra a será removida bem como as letras entre s e x e os números entre 0 e 2. Efetivamente as-x0-2 é uma forma resumida de astuvwx012.
tr -d as-x0-2 < fraseBh,
hell pr Lin.
Classes de caractere são sequências pré-definidas como [:punct:] que representa todas as pontuações. Consulte a página de manual de tr para visualizar a lista completa ou leia no próximo capítulo.
tr -d [:punct:] < fraseBash
1 shell para Linux
Alguns caracteres apresentam certa dificuldade em serem representados diretamente. Caso de novalinha que, por esse motivo, é representado pela sequência \n envolvida em um par de '.
Abaixo os dois novalinha são removidos (por isso o prompt aparece na mesma linha da saída).
tr -d '\n' < fraseBash, 1 shell para Linux.Substituindo caracteres
Com dois argumentos, tr substitui os caracteres no primeiro pelos do segundo.
tr 1 o < fraseBash,
o shell para Linux.
O primeiro caractere do conjunto1 será substituído pelo primeiro caractere do conjunto2 e assim por diante.
Abaixo, l é substituído por L e espaço por .. espaço é envolto por '' para que não seja interpretado como separador de palavras.
tr l' ' L. < fraseBash,.
1.sheLL.para.Linux.
⚠️ Contudo quando o conjunto1 contém mais caracteres que o conjunto 2, tr repete a última letra do conjunto2 para que ambas tenham a mesma quantidade de caracteres.
O conjunto2 HS portanto é efetivamente interpretado como HSSS já que o conjunto1 contém 4 caracteres.
tr hsal HS < fraseBSSH,
1 SHeSS pSrS Linux.
Classes de caracteres também são suportadas.
Abaixo, letras minúsculas são substituídas por maiúsculas.
tr [:lower:] [:upper:] < fraseBASH,
1 SHELL PARA LINUX.
A opção -c transforma o conjunto1 em todos os caracteres não especificados no conjunto1. Função conhecida como complemento de conjunto.
Abaixo todos os caracteres diferentes de a-zA-Z são substituídos por ..
tr -c a-zA-Z . < fraseBash.....shell.para.Linux..Deduplicação de caracteres com -s
Sequências repetidas de caracteres são substituídas por apenas uma ocorrência daquele.
Abaixo, ll de shell torna-se l.
tr -s l < fraseBash,
1 shel para Linux.
Comparando linhas com diff
diff recebe dois arquivos, os compara linha a linha e exibe na saída as linhas que diferem entre eles e como elas diferem. Caso não haja diferenças nenhuma saída é produzida.
diff não altera nenhum dos arquivos, apenas exibe o delta entre eles, ou seja, o que deveria ser mudado no arquivo da esquerda (primeiro argumento) para que ele fosse igual ao arquivo da direita (segundo argumento).
Arquivos original e atualizacao1, criados abaixo, serão comparados ao longo desta seção.
printf "%s\n" Omega Zeta Alfa Betha Gama > original
printf "%s\n" Alfa Beta Gama Delta > atualizacao1
head original atualizacao1
==> original <==
Omega
Zeta
Alfa
Betha
Gama
==> atualizacao1 <==
Alfa
Beta
Gama
Delta
A opção -q apenas atesta se os arquivos diferem. Caso sejam iguais nenhuma saída é exibida.
diff -q esquerda direitaFiles esquerda and direita differFormato normal de saída
O formato normal de saída é o padrão de diff. Apenas as diferenças são exibidas.
Nesse formato cada diferença é exibida em trechos. Cada trecho é iniciado com o comando de mudança — por ex. 4c2,3, que especifica: o número das linhas alteradas no arquivo da esquerda, uma letra que indica o tipo da mudança e as linhas alteradas no arquivo da direita — seguido pelas linhas que diferem entre os arquivos e é terminado antes do próximo comando de mudança ou o final da saída.
diff original atualizacao11,2d0
< Omega
< Zeta
4c2
< Betha
---
> Beta
5a4
> Delta
O primeiro trecho de diferença começa em 1,2d0 e termina em < Zeta, o segundo em 4c2 e termina em > Beta e o último começa em 5a4 e termina em > Delta.
O comando de mudança 1,2d0 é dividido em três partes:
1,2que representa os números das linhas no arquivo da esquerda. A,denota um intervalo de números com1sendo o menor e2o maior.2,7representaria da linha 2 até a 7. Um número, ao invés de um intervalo, também seria válido;dsignifica que as linhas foram excluídas ou seja já não aparecem no arquivo da direita, outras possibilidades contam comapara linhas adicionadas ecpara linhas substituídas;2o número da linha do arquivo da direita. Um intervalo também seria possível aqui.
< Omega e < Zeta são as linhas 1 e 2 do primeiro arquivo. < no começo da linha indica que a linha pertence ao arquivo da esquerda e > ao da direita.
Resumidamente, na saída acima:
1,2d0significa que as linhas1até2do arquivo da esquerda foram excluídas (d) no da direita. Caso não tivessem sido apareceriam após a linha0do arquivo da direita. O conteúdo removido foiOmegaeZeta(linhas 1 e 2 do arquivooriginal).4c2significa que a linha4do arquivo da esquerda foi substituída (c) pela linha2do arquivo da direita.Betha(linha 4 do arquivooriginal) é substituído porBeta(linha 2 do arquivoatualizacao1).5a4significa que após a linha5do arquivo da esquerda apareceria a linha4, incluída (a) no arquivo da direita.Delta(linha 4 do arquivoatualizacao1) é adicionado.
Formato unificado de saída
A opção -u exibe o formato unificado. Diferentemente do formato normal, esse formato exibe linhas comuns aos dois arquivos, chamadas de contexto. Geralmente o contexto conta com 3 linhas antes e 3 linhas depois de alguma diferença.
Os arquivos abaixo contêm todo o alfabeto, com exceção de E em esquerda e N em direita.
printf "%s\n" {A..D} {F..Z} > esquerda
printf "%s\n" {A..M} {O..Z} > direitaA opção --color com o argumento always coloriza as diferenças.
diff -u --color=always esquerda direita--- esquerda 2020-11-22 17:18:55.296029454 -0300
+++ direita 2020-11-22 17:19:31.228421642 -0300
@@ -2,6 +2,7 @@
B
C
D
+E
F
G
H
@@ -10,7 +11,6 @@
K
L
M
-N
O
P
Q
A primeira linha inicia com ---, que indica qual o arquivo da esquerda e então o nome do arquivo, data e horário seguido de fuso da última alteração do arquivo. +++, da segunda linha, indica o arquivo da direita.
O primeiro trecho começa no cabeçalho @@ -2,6 +2,7 @@ e termina antes do segundo trecho, que começa com o cabeçalho @@ -10,7 +11,6 @@.
Sobre o cabeçalho @@ -2,6 +2,7 @@:
- O par de
@@delimitam o início e fim do cabeçalho. -indica que os caracteres subsequentes são relativos ao arquivo da esquerda e+aos da direita.2,6significa que o trecho abaixo do cabeçalho inicia na linha 2 e se estende por 6 linhas, ou seja, termina na linha 7 (linha 2 + 6 linhas - 1 = linha 7).
Quanto às linhas abaixo do cabeçalho, as que começam com espaço são comuns aos dois arquivos, com + são exclusivas do arquivo da direita e com - do arquivo da esquerda.
No primeiro cabeçalho temos @@ -2,6 +2,7 @@. O trecho aparece nas linhas 2 à 7 no arquivo da esquerda e nas linhas 2 à 8 no da direita. + E aparece apenas no arquivo da direita enquanto as demais linhas são comuns aos dois arquivos.
No segundo cabeçalho temos @@ -10,7 +11,6 @@ indicando que o trecho aparece nas linhas 10-16 no arquivo da esquerda e nas linhas 11-16 no da direita. - N aparece apenas no arquivo da esquerda.
Comparando diretórios por arquivos em comum
diff pode receber diretórios como argumento e então exibir o nome dos arquivos que não são comuns.
O diretório um_dir conta com os arquivos esquerda e direita, já outro_dir apenas com direita.
mkdir um_dir outro_dir
cp esquerda direita um_dir
cp direita outro_dir
diff então exibe o nome do arquivo esquerda que está exclusivamente sob um_dir. Caso ambos os diretórios tivessem os mesmos arquivos, diff não produziria saída.
diff um_dir outro_dirOnly in um_dir: esquerdaIncluindo linhas diferentes com patch
patch modifica um arquivo utilizando a saída de diff -u salva em um arquivo.
Abaixo, a saída unificada de diff é salva no arquivo delta.
diff -u esquerda direita > deltaO primeiro argumento de patch é o arquivo a ser modificado e o segundo as modificações. A opção -b cria uma cópia do arquivo a ser modificado.
patch cria a cópia esquerda.orig e então atualiza esquerda com as modificações descritas em delta.
patch -b esquerda deltapatching file esquerdaAgora, esquerda e direita estão idênticos.
diff esquerda direitaPara reverter as modificações, utilize a opção -R.
patch -R esquerda deltapatching file esquerdaApós a reversão, a diferença entre esquerda e direita volta a ser a mesma.
diff esquerda direita4a5
> E
13d13
< N
E esquerda é idêntico à esquerda.orig mais uma vez.
diff esquerda esquerda.origDividindo linhas entre arquivos com split
split divide um arquivo em múltiplos outros, cada um com 1000 linhas ou menos.
O arquivo tres_mil_e_um é criado com 3.001 linhas.
printf "%s\n" {1..3001} > tres_mil_e_um
wc -l tres_mil_e_um
3001 tres_mil_e_um--verbose exibe a lista de arquivos criados.
Abaixo split divide tres_mil_e_um em 4 arquivos: 3 com 1000 linhas e 1 com 1 linha.
split --verbose tres_mil_e_umcreating file 'xaa'
creating file 'xab'
creating file 'xac'
creating file 'xad'
wc -l xaa xab xac xad 1000 xaa
1000 xab
1000 xac
1 xad
3001 total
O segundo argumento de split define o prefixo no nome dos arquivos a serem criados. Sem o segundo argumento, x é o prefixo padrão.
split --verbose tres_mil_e_um arquivo_creating file 'arquivo_aa'
creating file 'arquivo_ab'
creating file 'arquivo_ac'
creating file 'arquivo_ad'
A opção -l altera a quantidade máxima de linhas dos arquivos que serão criados.
split -l 700 tres_mil_e_um _wc -l _aa _ab _ac _ad _ae 700 _aa
700 _ab
700 _ac
700 _ad
201 _ae
3001 total
Mesclando linhas com paste
paste mescla linhas de dois ou mais arquivos e exibe o resultado na saída.
Arquivos números e letras criados abaixo.
printf "%s\n" {1..5} > numeros
printf "%s\n" {A..E} > letras
cat numeros letras
1
2
3
4
5
A
B
C
D
E
paste dispõe as colunas na mesma ordem dos argumentos.
paste numeros letras1 A
2 B
3 C
4 D
5 E
tabulação é o delimitador padrão. -d permite alterá-lo.
paste -d . numeros letras1.A
2.B
3.C
4.D
5.E
A opção -s muda a exibição para a disposição horizontal.
paste -s numeros letras1 2 3 4 5
A B C D E