Tutorial AWK: 25 exemplos práticos de comando AWK no Linux
Quer saber como usar o comando AWK no Linux? Aqui estão 25 exemplos de comandos AWK com explicação adequada que o ajudarão a dominar os fundamentos do AWK.
O comando AWK remonta aos primeiros dias do Unix. Faz parte do padrão POSIX e deve estar disponível em qualquer sistema semelhante ao Unix. E além.
Embora às vezes desacreditado devido à sua idade ou falta de recursos em comparação com uma linguagem multifuncional como Perl, o AWK continua sendo uma ferramenta que gosto de usar no meu trabalho diário. Às vezes, para escrever programas relativamente complexos, mas também por causa das poderosas linhas simples que você pode escrever para resolver problemas com seus arquivos de dados.
Então, esse é exatamente o objetivo deste artigo. Mostrando como você pode aproveitar o poder do AWK em menos de 80 caracteres para realizar tarefas úteis. Este artigo não pretende ser um tutorial completo do AWK, mas ainda incluí alguns comandos básicos no início, portanto, mesmo que você tenha pouca ou nenhuma experiência anterior, poderá entender os principais conceitos do AWK.
Meus arquivos de amostra para este tutorial do AWK
Todos os one-liners descritos nesse artigo serão testados no mesmo arquivo de dados:
cat file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Você pode obter uma cópia desse arquivo online no GitHub.
Conheça variáveis pré-definidas e automáticas no AWK
AWK suporta algumas variáveis predefinidas e automáticas para ajudá-lo a escrever seus programas. Entre eles você encontrará frequentemente:
RS –O separador de registros. O AWK processa seus dados um registro por vez. O separador de registros é o delimitador usado para dividir o fluxo de dados de entrada em registros. Por padrão, este é o caractere de nova linha. Portanto, se você não alterá-lo, um registro será uma linha do arquivo de entrada.
NR – O número do registro de entrada atual. Se você estiver usando o delimitador de nova linha padrão para seus registros, ele corresponderá ao número da linha de entrada atual.
FS/OFS –O(s) caractere(s) usado(s) como separador de campo. Depois que o AWK lê um registro, ele o divide em campos diferentes com base em o valor de FS
. Quando o AWK imprime um registro na saída, ele unirá novamente os campos, mas desta vez, usando o separador OFS
em vez do separador FS
. Normalmente, FS
e OFS
são iguais, mas isso não é obrigatório. “espaço em branco” é o valor padrão para ambos.
NF – O número de campos no registro atual. Se você estiver usando o delimitador padrão de “espaço em branco” para seus campos, ele corresponderá ao número de palavras no registro atual.
Existem outras variáveis AWK mais ou menos padrão disponíveis, então vale a pena verificar seu manual de implementação específico do AWK para obter mais detalhes. No entanto, este subconjunto já é suficiente para começar a escrever frases interessantes.
A. Uso básico do comando AWK
1. Imprima todas as linhas
Este exemplo é praticamente inútil, mas mesmo assim será uma boa introdução à sintaxe do AWK:
awk '1 { print }' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Os programas AWK são feitos de uma ou mais instruções pattern { action }
.
Se, para um determinado registro (“linha”) do arquivo de entrada, o padrão for avaliado como não -zero (equivalente a “true” no AWK), os comandos no bloco de ação correspondente são executados. No exemplo acima, como 1
é uma constante diferente de zero, o bloco de ação { print }
é executado para cada registro de entrada.
Outro truque é que { print }
é o bloco de ação padrão que será usado pelo AWK se você não especificar explicitamente um. Portanto, o comando acima pode ser abreviado como:
awk 1 file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Quase tão inútil, o seguinte programa AWK consumirá sua entrada, mas não produzirá nada na saída:
arquivo awk 0
2. Remova um cabeçalho de arquivo
awk 'NR>1' file
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Lembre-se, isso equivale a escrever explicitamente:
awk 'NR>1 { print }' file
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Este one-liner escreverá registros do arquivo de entrada, exceto o primeiro, pois nesse caso a condição é 1>1
o que obviamente não é verdadeiro.
Como este programa está usando os valores padrão para RS
, na prática ele descartará a primeira linha do arquivo de entrada.
3. Imprimir linhas em um intervalo
Isto é apenas uma generalização do exemplo anterior e não merece muitas explicações, exceto dizer que &&
é o operador lógico and
:
awk 'NR>1 && NR < 4' file
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
4. Removendo linhas somente com espaços em branco
awk 'NF' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
O AWK divide cada registro em campos, com base no separador de campo especificado na variável FS
. O separador de campo padrão é um ou vários caracteres de espaço em branco (também conhecidos como espaços ou tabulações). Com essas configurações, qualquer registro que contenha pelo menos um caractere que não seja um espaço em branco conterá pelo menos um campo.
Ou seja, o único caso em que NF
é 0 (“falso”) é quando o registro contém apenas espaços. Portanto, essa linha só imprimirá registros contendo pelo menos um caractere que não seja espaço.
5. Removendo todas as linhas em branco
awk '1' RS='' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Esta linha única é baseada em uma regra POSIX obscura que especifica se o RS
está definido como uma string vazia, “então os registros são separados por sequências que consistem em uma
Vale a pena mencionar na terminologia POSIX que uma linha em branco é uma linha completamente vazia. Linhas que contêm apenas espaços em branco não contam como “em branco”.
6. Extraindo campos
Este é provavelmente um dos casos de uso mais comuns do AWK: extrair algumas colunas do arquivo de dados.
awk '{ print $1, $3}' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
,
,
,
17,abhishek
Aqui, defino explicitamente os separadores de campos de entrada e saída como vírgula. Quando o AWK divide um registro em campos, ele armazena o conteúdo do primeiro campo em $1, o conteúdo do segundo campo em $2 e assim por diante. Não uso isso aqui, mas vale a pena mencionar que $0 é o registro inteiro.
Neste one-liner, você deve ter notado que uso um bloco de ação sem padrão. Nesse caso, 1 (“true”) é assumido para o padrão, então o bloco de ação é executado para cada registro.
Dependendo de suas necessidades, pode não produzir o que gostaríamos para linhas em branco ou apenas com espaços em branco. Nesse caso, essa segunda versão poderia ser um pouco melhor:
awk 'NF { print $1, $3 }' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
,
17,abhishek
Em ambos os casos, passei valores personalizados para FS
e OFS
na linha de comando. Outra opção seria usar um bloco BEGIN
especial dentro do programa AWK para inicializar essas variáveis antes que o primeiro registro seja lido. Então, dependendo do seu gosto, você pode preferir escrever isso:
awk 'BEGIN { FS=OFS="," } NF { print $1, $3 }' file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
,
17,abhishek
Vale a pena mencionar aqui que você também pode usar blocos END
para realizar algumas tarefas após a leitura do último registro. Como veremos agora. Dito isto, admito que isso está longe de ser perfeito, já que as linhas somente com espaços em branco não são tratadas com elegância. Em breve veremos uma possível solução, mas antes vamos fazer algumas contas…
7. Realizando cálculos em colunas
AWK oferece suporte aos operadores aritméticos padrão. E converterá valores entre texto e números automaticamente dependendo do contexto. Além disso, você pode usar suas próprias variáveis para armazenar valores intermediários. Tudo isso permite escrever programas compactos para realizar cálculos em colunas de dados:
awk '{ SUM=SUM+$1 } END { print SUM }' FS=, OFS=, file
263
Ou, de forma equivalente, usando a sintaxe abreviada +=
:
awk '{ SUM+=$1 } END { print SUM }' FS=, OFS=, file
263
Observe que as variáveis AWK não precisam ser declaradas antes do uso. Presume-se que uma variável indefinida contém a string vazia. Que, de acordo com as regras de conversão do tipo AWK, é igual ao número 0. Por causa desse recurso, não me preocupei em lidar explicitamente com o caso em que $1
contém texto (no título), espaços em branco ou simplesmente nada. Em todos esses casos, contará como 0 e não interferirá no nosso somatório. Claro, seria diferente se eu fizesse multiplicações. Então, por que você não usaria a seção de comentários para sugerir uma solução para esse caso?
8. Contando o número de linhas não vazias
Já mencionei a regra END
antes. Aqui está outra aplicação possível para contar o número de linhas não vazias em um arquivo:
awk '/./ { COUNT+=1 } END { print COUNT }' file
9
Aqui usei a variável COUNT
e a incrementei (+=1
) para cada linha correspondente à expressão regular /./
. Ou seja, cada linha contém pelo menos um caractere. Finalmente, o bloco END é usado para exibir o resultado final depois que todo o arquivo for processado. Não há nada de especial no nome COUNT
. Eu poderia ter usado Count
, count
, n
, xxxx
ou qualquer outro nome que estivesse em conformidade com as regras de nomenclatura de variáveis AWK
Porém, esse resultado está correto? Bem, depende da sua definição de linha “vazia”. Se você considerar que apenas as linhas em branco (de acordo com POSIX) estão vazias, isso está correto. Mas talvez você prefira considerar as linhas apenas com espaços em branco como vazias também?
awk 'NF { COUNT+=1 } END { print COUNT }' file
8
Desta vez, o resultado é diferente, pois a versão posterior também ignora linhas somente com espaços em branco, enquanto a versão inicial ignorou apenas linhas em branco. Você consegue ver a diferença? Eu deixei você descobrir isso sozinho. Não hesite em usar a seção de comentários se isso não estiver claro o suficiente!
Finalmente, se você estiver interessado apenas em linhas de dados e considerando meu arquivo de dados de entrada específico, eu poderia escrever isso:
awk '+$1 { COUNT+=1 } END { print COUNT }' file
7
Funciona por causa das regras de conversão do tipo AWK. O sinal de mais unário no padrão força a avaliação de $1 em um contexto numérico. No meu arquivo, os registros de dados contêm um número no primeiro campo. Registros que não são de dados (título, linhas em branco, linhas somente com espaços em branco) contêm texto ou nada. Todos eles sendo iguais a 0 quando convertidos em números.
Observe que com a solução mais recente, um registro para um usuário que eventualmente tivesse 0 créditos também seria descartado.
B. Usando matrizes no AWK
Matrizes são um recurso poderoso do AWK. Todos os arrays no AWK são arrays associativos, portanto permitem associar uma string arbitrária a outro valor. Se você estiver familiarizado com outras linguagens de programação, talvez as conheça como hashes, tabelas associativas, dicionários ou mapas.
9. Um exemplo simples de array AWK
Vamos imaginar que eu queira saber o crédito total de todos os usuários. Posso armazenar uma entrada para cada usuário em uma matriz associativa e, cada vez que encontro um registro para esse usuário, incremento o valor correspondente armazenado na matriz.
awk '+$1 { CREDITS[$3]+=$1 }
END { for (NAME in CREDITS) print NAME, CREDITS[NAME] }' FS=, file
abhishek 17
sonia 129
öle 8
sylvain 109
Admito que isso não é mais uma frase única. Principalmente por causa do loop for
usado para exibir o conteúdo do array após o processamento do arquivo. Então, vamos voltar agora a exemplos mais curtos:
10. Identificando linhas duplicadas usando AWK
Arrays, assim como outras variáveis AWK, podem ser usados tanto em blocos de ação quanto em padrões. Aproveitando isso, podemos escrever uma linha para imprimir apenas linhas duplicadas:
awk 'a[$0]++' file
52,01 dec 2018,sonia,team
O operador ++
é o operador pós-incremento herdado da família da linguagem C (cujo AWK é um membro orgulhoso, graças a Brian Kernighan ter sido um de seus autores originais).
Como o próprio nome indica, o operador pós-incremento incrementa (“adicionar 1”) uma variável, mas somente após seu valor ter sido obtido para a avaliação da expressão abrangente.
Nesse caso, a[$0]
é avaliado para ver se o registro será impresso ou não, e uma vez tomada a decisão, em todos os casos, a entrada do array é incrementada.
Portanto, na primeira vez que um registro é lido, a[$0]
é indefinido e, portanto, equivalente a zero para AWK. Portanto, esse primeiro registro não está escrito na saída. Então essa entrada é alterada de zero para um.
Na segunda vez que o mesmo registro de entrada é lido, a[$0]
agora é 1. Isso é “true”. A linha será impressa. Porém, antes disso, a entrada do array é atualizada de 1 para 2. E assim por diante.
11. Removendo linhas duplicadas
Como corolário da linha anterior, podemos querer remover linhas duplicadas:
awk '!a[$0]++' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
A única diferença é o uso do operador lógico, não (!
) que inverte o valor verdade da expressão. O que era falso torna-se verdadeiro e o que era verdadeiro torna-se falso. A lógica não tem absolutamente nenhuma influência no pós-incremento ++
que funciona exatamente como antes.
C. Magia do separador de campos e registros
12. Alterando os separadores de campo
awk '$1=$1' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01 dec 2018;sonia;team
52;01 dec 2018;sonia;team
25;01 jan 2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12 jun 2018;öle;team:support
17;05 apr 2019;abhishek;guest
Esse programa define as variáveis FS
e OFS
para usar uma vírgula como separador de campos de entrada e um ponto e vírgula como separador de campos de saída. Como o AWK não altera o registro de saída, desde que você não altere um campo, o truque $1=$1
é usado para forçar o AWK a quebrar o registro e remontá-lo usando o separador de campo de saída.
Lembre-se aqui que o bloco de ação padrão é { print }
. Então você poderia reescrever isso mais explicitamente como:
awk '$1=$1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01 dec 2018;sonia;team
52;01 dec 2018;sonia;team
25;01 jan 2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12 jun 2018;öle;team:support
17;05 apr 2019;abhishek;guest
Você deve ter notado que ambos os exemplos também estão removendo linhas vazias. Por que? Bem, lembre-se das regras de conversão do AWK: uma string vazia é “falsa. ”Todas as outras strings são“ verdadeiras. ” A expressão $1=$1
é uma afetação que altera $1
. No entanto, esta também é uma expressão. E avalia o valor de $1
– que é “false” para a string vazia. Se você realmente deseja todas as linhas, talvez seja necessário escrever algo assim:
awk '($1=$1) || 1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01 dec 2018;sonia;team
52;01 dec 2018;sonia;team
25;01 jan 2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12 jun 2018;öle;team:support
17;05 apr 2019;abhishek;guest
Você se lembra do operador &&
? Foi o AND lógico. ||
é o OR lógico. O parêntese é necessário aqui devido às regras de precedência dos operadores. Sem eles, o padrão teria sido interpretado erroneamente como $1=($1 || 1)
. Deixei como exercício para vocês testarem como o resultado teria sido diferente então.
Finalmente, se você não gosta muito de aritmética, aposto que preferirá essa solução mais simples:
awk '{ $1=$1; print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01 dec 2018;sonia;team
52;01 dec 2018;sonia;team
25;01 jan 2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12 jun 2018;öle;team:support
17;05 apr 2019;abhishek;guest
13. Removendo vários espaços
awk '$1=$1' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Este é quase o mesmo programa do anterior. No entanto, deixei os separadores de campos com seus valores padrão. Portanto, vários espaços em branco são usados como separador de campos de entrada, mas apenas um espaço é usado como separador de campos de saída. Isso tem o bom efeito colateral de unir múltiplos espaços em branco em um espaço.
14. Unindo linhas usando AWK
Já usamos OFS
, o separador de campos de saída. Como você deve ter adivinhado, ele possui a contraparte ORS
para especificar o separador de registros de saída:
awk '{ print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle abhishek
Aqui, usei um espaço após cada registro em vez de um caractere de nova linha. Essa linha única é suficiente em alguns casos de uso, mas ainda apresenta algumas desvantagens.
Obviamente, ele não descarta linhas somente com espaços em branco (os espaços extras após öle vêm disso). Então, posso acabar usando uma expressão regular simples:
awk '/[^[:space:]]/ { print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle abhishek
Está melhor agora, mas ainda há um possível problema. Será mais óbvio se mudarmos o separador para algo visível:
awk '/[^[:space:]]/ { print $3 }' FS=, ORS='+' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek+
Há um separador extra no final da linha — porque o separador de campo é escrito após cada registro. Incluindo o último.
Para corrigir isso, reescreverei o programa para exibir um separador personalizado antes do registro, começando pelo segundo registro de saída.
awk '/[^[:space:]]/ { print SEP $3; SEP="+" }' FS=, ORS='' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek
Como eu mesmo cuido de adicionar o separador, também defino o separador de registro de saída AWK padrão para a string vazia. No entanto, quando você começa a lidar com separadores ou formatação, pode ser um sinal de que você deve considerar usar a função printf em vez da instrução print
. Como veremos agora.
D. Formatação de campo
Já mencionei a relação entre as linguagens de programação AWK e C. Entre outras coisas, da biblioteca padrão da linguagem C o AWK herda a poderosa função printf
, permitindo grande controle sobre a formatação do texto enviado para a saída.
A função printf
usa um formato como primeiro argumento, contendo texto simples que será exibido literalmente e curingas usados para formatar seções diferentes da saída. Os curingas são identificados pelo caractere %
. Os mais comuns são %s
(para formatação de strings), %d
(para formatação de números inteiros) e %f
(para formatação de números de ponto flutuante ). Como isso pode ser bastante abstrato, vejamos um exemplo:
awk '+$1 { printf("%s ", $3) }' FS=, file; echo
sylvain sonia sonia sonia sylvain öle abhishek
Você pode notar que, ao contrário da instrução print
, a função printf
não usa OFS
e ORS
valores. Portanto, se você quiser algum separador, deverá mencioná-lo explicitamente, como eu fiz, adicionando um caractere de espaço no final da string de formato. Este é o preço a pagar por ter controle total da produção.
Embora não seja um especificador de formato, esta é uma excelente ocasião para introduzir a notação \n
que pode ser usada em qualquer string AWK para representar um caractere de nova linha.
awk '+$1 { printf("%s\n", $3) }' FS=, file
sylvain
sonia
sonia
sonia
sylvain
öle
abhishek
15. Produzindo resultados tabulares
AWK impõe um formato de dados de registro/campo baseado em delimitadores. No entanto, usando a função printf
, você também pode produzir uma saída tabular de largura fixa. Porque cada especificador de formato em uma instrução printf
pode aceitar um parâmetro de largura opcional:
awk '+$1 { printf("%10s | %4d\n", $3, $1) }' FS=, file
sylvain | 99
sonia | 52
sonia | 52
sonia | 25
sylvain | 10
öle | 8
abhishek | 17
Como você pode ver, ao especificar a largura de cada campo, o AWK os preenche à esquerda com espaços. Para texto, geralmente é preferível preencher à direita, algo que pode ser conseguido usando um número de largura negativo. Além disso, para números inteiros, podemos preencher os campos com zeros em vez de espaços. Isso pode ser obtido usando um 0 explícito antes da largura do campo:
awk '+$1 { printf("%-10s | %04d\n", $3, $1) }' FS=, file
sylvain | 0099
sonia | 0052
sonia | 0052
sonia | 0025
sylvain | 0010
öle | 0008
abhishek | 0017
16. Lidando com números de ponto flutuante
O formato %f
não merece muitas explicações…
awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%f",SUM/NUM); }' FS=, file
AVG=37.571429
… exceto talvez para dizer que você quase sempre deseja definir explicitamente a largura do campo e a precisão do resultado exibido:
awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%6.1f",SUM/NUM); }' FS=, file
AVG= 37.6
Aqui, a largura do campo é 6, o que significa que o campo ocupará o espaço de 6 caracteres (incluindo o ponto e, eventualmente, preenchido com espaços à esquerda, como normalmente). A precisão .1 significa que queremos exibir o número com 1 número decimal após o ponto. Deixei você adivinhar o que %06.1
seria exibido.
E. Usando funções de string no AWK
Além da função printf
, o AWK contém algumas outras funções interessantes de manipulação de strings. Nesse domínio, implementações modernas como o Gawk possuem um conjunto mais rico de funções internas ao preço de menor portabilidade. Quanto a mim, ficarei aqui apenas com algumas funções definidas pelo POSIX que devem funcionar da mesma forma em qualquer lugar.
17. Convertendo texto para maiúsculas
Este, eu uso muito, porque lida muito bem com questões de internacionalização:
awk '$3 { print toupper($0); }' file
99,01 JUN 2018,SYLVAIN,TEAM:::ADMIN
52,01 DEC 2018,SONIA,TEAM
52,01 DEC 2018,SONIA,TEAM
25,01 JAN 2019,SONIA,TEAM
10,01 JAN 2019,SYLVAIN,TEAM:::ADMIN
8,12 JUN 2018,ÖLE,TEAM:SUPPORT
17,05 APR 2019,ABHISHEK,GUEST
Na verdade, esta é provavelmente a melhor e mais portátil solução para converter texto em maiúsculas do shell.
18. Alterando parte de uma string
Usando o comando substr
, você pode dividir uma sequência de caracteres em um determinado comprimento. Aqui eu uso para colocar em maiúscula apenas o primeiro caractere do terceiro campo:
awk '{ $3 = toupper(substr($3,1,1)) substr($3,2) } $3' FS=, OFS=, file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,Sylvain,team:::admin
52,01 dec 2018,Sonia,team
52,01 dec 2018,Sonia,team
25,01 jan 2019,Sonia,team
10,01 jan 2019,Sylvain,team:::admin
8,12 jun 2018,Öle,team:support
17,05 apr 2019,Abhishek,guest
A função substr
pega a string inicial, o índice (baseado em 1) do primeiro caractere a extrair e o número de caracteres a extrair. Se esse último argumento estiver faltando, substr
pega todos os caracteres restantes da string.
Portanto, substr($3,1,1)
será avaliado para o primeiro caractere de $3
e substr($3,2)
para o restante uns.
19. Divisão de campos em subcampos
O modelo de dados de campo de registro AWK é muito bom. No entanto, às vezes você deseja dividir os campos em várias partes com base em algum separador interno:
awk '+$1 { split($2, DATE, " "); print $1,$3, DATE[2], DATE[3] }' FS=, OFS=, file
99,sylvain,jun,2018
52,sonia,dec,2018
52,sonia,dec,2018
25,sonia,jan,2019
10,sylvain,jan,2019
8,öle,jun,2018
17,abhishek,apr,2019
Surpreendentemente, isso funciona mesmo que alguns dos meus campos estejam separados por mais de um espaço em branco. Principalmente por razões históricas, quando o separador é um espaço único, split
considerará “os elementos são separados por espaços em branco. ”E não apenas por um. A variável especial FS
segue a mesma convenção.
No entanto, no caso geral, uma sequência de caracteres corresponde a um caractere. Portanto, se você precisar de algo mais complexo, lembre-se de que o separador de campos é uma expressão regular estendida.
Como exemplo, vamos ver como seria tratado o campo de grupo que parece ser um campo de vários valores usando dois pontos como separador:
awk '+$1 { split($4, GRP, ":"); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team
sonia team
sonia team
sonia team
sylvain team
öle team support
abhishek guest
Embora eu esperasse exibir até dois grupos por usuário, ele mostra apenas um para a maioria deles. Esse problema é causado pelas múltiplas ocorrências do separador. Então, a solução é:
awk '+$1 { split($4, GRP, /:+/); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team admin
sonia team
sonia team
sonia team
sylvain team admin
öle team support
abhishek guest
As barras em vez das aspas indicam que o literal é uma expressão regular em vez de uma string simples, e o sinal de mais indica que esta expressão corresponderá a uma ou várias ocorrências do caractere anterior. Então, nesse caso, cada separador é feito (da sequência mais longa de) um ou vários dois pontos consecutivos.
20. Pesquisando e substituindo por comandos AWK
Falando em expressões regulares, às vezes você deseja realizar substituições como o comando sed s///g
, mas apenas em um campo. O comando gsub
é o que você precisa nesse caso:
awk '+$1 { gsub(/ +/, "-", $2); print }' FS=, file
99 01-jun-2018 sylvain team:::admin
52 01-dec-2018 sonia team
52 01-dec-2018 sonia team
25 01-jan-2019 sonia team
10 01-jan-2019 sylvain team:::admin
8 12-jun-2018 öle team:support
17 05-apr-2019 abhishek guest
A função gsub
usa uma expressão regular para pesquisar, uma string de substituição e a variável que contém o texto a ser modificado. Se isso estiver faltando, $0 será assumido.
F. Trabalhando com comandos externos no AWK
Outro ótimo recurso do AWK é que você pode facilmente invocar comandos externos para processar seus dados. Existem basicamente duas maneiras de fazer isso: usando a instrução system
para invocar um programa e deixá-lo misturar sua saída no fluxo de saída do AWK. Ou usar um pipe para que o AWK possa capturar a saída do programa externo para um controle mais preciso do resultado.
Esses podem ser tópicos enormes por si só, mas aqui estão alguns exemplos simples para mostrar o poder por trás desses recursos.
21. Adicionando a data no topo de um arquivo
awk 'BEGIN { printf("UPDATED: "); system("date") } /^UPDATED:/ { next } 1' file
UPDATED: Thu Feb 15 00:31:03 CET 2018
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Nesse programa AWK, começo exibindo o trabalho ATUALIZADO. Em seguida, o programa invoca o comando de data externo, que enviará seu resultado na saída logo após o texto produzido pelo AWK naquele estágio.
O resto do programa AWK apenas remove uma instrução de atualização eventualmente presente no arquivo e imprime tudo as demais linhas (com a regra 1
).
Observe a instrução next
. É usado para abortar o processamento do registro atual. É uma forma padrão de ignorar alguns registros do arquivo de entrada.
22. Modificando um campo externamente
Para casos mais complexos, pode ser necessário considerar o | getline VARIABLE
idioma do AWK:
awk '+$1 { CMD | getline $5; close(CMD); print }' CMD="uuid -v4" FS=, OFS=, file
99,01 jun 2018,sylvain,team:::admin,5e5a1bb5-8a47-48ee-b373-16dc8975f725
52,01 dec 2018,sonia,team,2b87e9b9-3e75-4888-bdb8-26a9b34facf3
52,01 dec 2018,sonia,team,a5fc22b5-5388-49be-ac7b-78063cbbe652
25,01 jan 2019,sonia,team,3abb0432-65ef-4916-9702-a6095f3fafe4
10,01 jan 2019,sylvain,team:::admin,592e9e80-b86a-4833-9e58-1fe2428aa2a2
8,12 jun 2018,öle,team:support,3290bdef-fd84-4026-a02c-46338afd4243
17,05 apr 2019,abhishek,guest,e213d756-ac7f-4228-818f-1125cba0810f
Isto irá executar o comando armazenado na variável CMD
, ler a primeira linha da saída desse comando e armazená-la na variável $5
.
Preste atenção especial à instrução close, crucial aqui, pois queremos que o AWK crie uma nova instância do comando externo cada vez que executar o comando CMD | instrução getline
. Sem a instrução close, o AWK tentaria ler várias linhas de saída da mesma instância de comando.
23. Invocando comandos gerados dinamicamente
Os comandos no AWK são apenas strings simples, sem nada de especial. É o operador pipe que aciona a execução de programas externos. Portanto, se necessário, você pode construir dinamicamente comandos complexos arbitrários usando funções e operadores de manipulação de string AWK.
awk '+$1 { cmd = sprintf(FMT, $2); cmd | getline $2; close(cmd); print }' FMT='date -I -d "%s"' FS=, file
99 2018-06-01 sylvain team:::admin
52 2018-12-01 sonia team
52 2018-12-01 sonia team
25 2019-01-01 sonia team
10 2019-01-01 sylvain team:::admin
8 2018-06-12 öle team:support
17 2019-04-05 abhishek guest
Já conhecemos a função printf
. sprintf
é muito semelhante, mas retornará a string construída em vez de enviá-la para a saída.
24. Unindo dados
Para mostrar o propósito da declaração fechada, deixo você experimentar o último exemplo:
awk '+$1 { CMD | getline $5; print }' CMD='od -vAn -w4 -t x /dev/urandom' FS=, file
99 01 jun 2018 sylvain team:::admin 1e2a4f52
52 01 dec 2018 sonia team c23d4b65
52 01 dec 2018 sonia team 347489e5
25 01 jan 2019 sonia team ba985e55
10 01 jan 2019 sylvain team:::admin 81e9a01c
8 12 jun 2018 öle team:support 4535ba30
17 05 apr 2019 abhishek guest 80a60ec8
Ao contrário do exemplo usando o comando uuid
acima, há aqui apenas uma instância de od
iniciada enquanto o programa AWK está rodando, e ao processar cada registro, lemos mais uma linha da saída desse mesmo processo.
Conclusão
Esse rápido tour pelo AWK certamente não pode substituir um curso ou tutorial completo sobre essa ferramenta. No entanto, para aqueles que não estavam familiarizados com ele, espero que tenha dado ideias suficientes para que possam adicionar imediatamente o AWK à sua caixa de ferramentas.
Por outro lado, se você já era um aficionado do AWK, talvez tenha encontrado aqui alguns truques que pode usar para ser mais eficiente ou simplesmente para impressionar seus amigos.
No entanto, não pretendo ser exaustivo. Portanto, em todos os casos, não hesite em compartilhar sua frase favorita do AWK ou qualquer outra dica do AWK usando a seção de comentários abaixo!