Este texto refere -se apenas à implementação do GNU da awk , conhecida como Witch gawk , é a mais usada e vem com qualquer distribuição moderna do Linux / Unix .
[O Guia do Usuário do GNU AWK] [GNU-AWK] é sua referência, para os exemplos que usei casos do mundo real tirados principalmente das minhas respostas [StackOverflow] [So].
Awk é um idioma semelhante ao Perl, apenas consideravelmente mais elegante.
- Arnold Robbins
O AWK é uma linguagem de programação projetada para processamento de texto e normalmente usada como uma ferramenta de extração e relatório de dados. É um recurso padrão da maioria dos sistemas operacionais do tipo UNIX.
awk ...Seu nome é derivado dos sobrenomes de seus autores - Alfred A Ho, Peter Weinberger e Brian Kernighan .
awk ...Procura linhas que contêm certos padrões nos arquivos ou a entrada padrão.
Utilizado principalmente para extração e relatórios de dados, como resumir informações da saída de outros programas de serviços públicos.
Sintaxe C-like
Divido de dados: descreve os dados com os quais você deseja trabalhar e, em seguida, com que ação fazer quando os encontrar.
pattern { action }
pattern { action }Se o programa for curto :
awk ' program ' input-file1 input-file2NOTA: Cuidado com os problemas de cotação do shell 1 .
cmd | awk ' program ' Nota: O pipe redireciona a saída do comando esquerdo ( cmd ) para a entrada do comando awk 2 .
Quando o código é longo , geralmente é mais conveniente colocá -lo em um arquivo e executá -lo com um comando como este:
awk -f program-file input-file1 input-file2
cmd | awk -f program-file Ou apenas faça com que seja executável como um shebang :
#! /bin/awk -f
BEGIN { print " hello world!! " } -F fs Defina a variável FS como fs .
-v var=val Defina a variável var para o valor val antes do início da execução do programa.
Nota : pode ser usado mais de uma vez, definindo outra variável a cada vez.
Esses padrões especiais ou blocos fornecem ações de inicialização e limpeza para programas awk .
BEGIN{
// initialize variables
}
{
/pattern/ { action }
}
END{
// cleanup
}BEGIN antes do primeiro registro de entrada, END depois que toda a entrada for consumida.
$ echo " hello " | awk ' BEGIN{print "BEGIN";f=1}
{print $f}
END{print "END"} '
BEGIN
hello
ENDgrepping se você tem awk ?? $ cat lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ grep dolor lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.$ awk ' /dolor/ ' lorem_ipsum.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.NOTA: Se a ação não receber a ação padrão será imprimir o registro que corresponde ao padrão fornecido.
Mas ... como podemos descobrir a primeira e a última palavra de cada linha?
Claro que grep pode, mas precisa de duas etapas:
$ grep -Eo ' ^[^ ]+ ' lorem_ipsum.dat
Lorem
Maecenas
Nunc
Curabitur
Lorem
Aliquam$ grep -Eo ' [^ ]+$ ' lorem_ipsum.dat
elit.
condimentum.
ex.
tellus.
elit.
ultrices. Vamos ver awk em ação aqui:
$ awk ' {print $1,$NF} ' lorem_ipsum.dat
Lorem elit.
Maecenas condimentum.
Nunc ex.
Curabitur tellus.
Lorem elit.
Aliquam ultrices. awk divide a entrada do seu programa em registros e campos .
Os registros são separados por um caractere chamado Record Separator RS . Por padrão , o separador de registros é o caractere do Unix Newline n .
É por isso que os registros são, por padrão , linhas únicas .
Além disso, awk possui o separador de registros de saída ORS para controlar a maneira como os registros são apresentados no stdout .
RS e ORS devem ser fechados em aspas , que indicam uma constante de sequência.
Para usar um caractere diferente ou um regex , basta atribuí -lo às variáveis RS ou / e ORS :
BEGIN da execução, antes que qualquer entrada seja processada, para que o primeiro registro seja lido com o separador adequado.Exemplos:
$ awk ' BEGIN{RS=" *, *";ORS="<<<---n"}
{print $0} ' lorem_ipsum.dat
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci <<< ---
euismod id nisi eget <<< ---
interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat <<< ---
et facilisis neque ultrices.
<<< --- $ awk ' {print $0} ' RS= " *, * " ORS= " <<<---n " lorem_ipsum.dat
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci <<< ---
euismod id nisi eget <<< ---
interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet <<< ---
consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat <<< ---
et facilisis neque ultrices.
<<< --- Os registros awk são analisados automaticamente ou separados em pedaços chamados campos .
Por padrão, os campos são separados pelo espaço em branco (qualquer string de um ou mais espaços, guias ou linhas novas), como palavras em uma linha.
Para se referir a um campo em um programa awk , você usa um sinal $ seguido pelo número do campo desejado.
Assim, $1 refere -se ao primeiro campo, $2 para o segundo e assim por diante.
Importante : $0 representa todo o registro de entrada.
$ awk ' {print $3} ' lorem_ipsum.dat
dolor
erat
orci,
dapibus
dolor
mauris NF é uma variável predefinida, seu valor é o número de campos no registro atual . Portanto, $NF será sempre o último campo do registro.
$ awk ' {print NF} ' lorem_ipsum.dat
8
7
10
4
8
10vs.
$ awk ' {print $NF} ' lorem_ipsum.dat
elit.
condimentum.
ex.
tellus.
elit.
facilisis. FS detém o valor do separador de campo , esse valor é uma sequência de caracteres única ou um regex que corresponde às separações entre os campos em um registro de entrada.
O valor padrão é " " , uma string que consiste em um único espaço. Como uma exceção especial, esse valor significa que qualquer sequência de espaços , guias e/ou newlines é um único separador.
Da mesma maneira que ORS temos uma OFS para gerenciar como nossos campos serão enviados para o fluxo de saída.
$ cat /etc/group
nobody: * :-2:
nogroup: * :-1:
wheel: * :0:root
daemon: * :1:root
kmem: * :2:root
sys: * :3:root
tty: * :4:root$ awk ' !/^(_|#)/&&$1=$1 ' FS= " : " OFS= " <-> " /etc/group
nobody < - > * < - > - 2< - >
nogroup < - > * < - > - 1< - >
wheel < - > * < - > 0 < - > root
daemon < - > * < - > 1 < - > root
kmem < - > * < - > 2 < - > root
sys < - > * < - > 3 < - > root
tty < - > * < - > 4 < - > root Nota : Ummm ... $1=$1 ???? 3
Mantendo os registros e os campos em mente, agora estavam prontos para entender nosso código anterior:
$ awk ' {print $1,$NF} ' lorem_ipsum.dat
Lorem elit.
Maecenas condimentum.
Nunc ex.
Curabitur tellus.
Lorem elit.
Aliquam ultrices.Estas são duas variáveis embutidas úteis:
NR : O número de registros de entrada awk processou desde o início da execução do programa.
FNR : número de registro atual no arquivo atual, awk redefine FNR para zero cada vez que inicia um novo arquivo de entrada.
$ cat n1.dat
one
two$ cat n2.dat
three
four$ awk ' {print NR,FNR,$0} ' n1.dat n2.dat
1 1 one
2 2 two
3 1 three
4 2 fourA sequência do formato é muito semelhante à da ISO c .
Sintaxe:
printf format, item1, item2, …
$ awk ' {printf "%20s <-> %sn",$1,$NF} ' lorem_ipsum.dat
Lorem < - > elit.
Maecenas < - > condimentum.
Nunc < - > ex.
Curabitur < - > tellus.
Lorem < - > elit.
Aliquam < - > ultrices. A saída do print e printf é direcionada para a saída padrão por padrão, mas podemos usar o redirecionamento para alterar o destino.
Os redirecionamentos no awk são escritos como redirecionamentos nos comandos do shell , exceto que eles estão escritos dentro do programa awk .
$ awk ' BEGIN{print "hello">"hello.dat"} ' $ awk ' BEGIN{print "world!">>"hello.dat"} ' $ cat hello.dat
hello
world !Também é possível enviar saída para outro programa através de um tubo :
$ awk ' BEGIN{sh="/bin/sh";print "date"|sh;close(sh)} '
dom nov 13 18:36:25 CET 2016 Os [fluxos] podem ser apontados para o stdin , o stdout e o stderr .
Por exemplo, podemos escrever uma mensagem de erro para o stderr assim:
$ awk ' BEGIN{print "Serious error detected!" > "/dev/stderr"} '
Serious error detected ! No Awk, as matrizes são associativas , cada uma é uma coleção de pares , índice - valor , onde qualquer número ou string pode ser um índice.
Nenhuma declaração é necessária; Novos pares podem ser adicionados a qualquer momento.
| Índice | Valor |
|---|---|
| "Perro" | "cachorro" |
| "gato" | "gato" |
| "Uno" | "um" |
| 1 | "um" |
| 2 | "dois" |
Para referir uma matriz:
array[index-expression]
Para atribuir valores:
array[index-expression] = value
Para verificar se uma chave é indexada:
indx in array
Para itera:
for (var in array) {
var, array[var]
}Usando valores numéricos como índices e preservando o pedido:
for (i = 1 ; i < = max_index ; i++) {
print array[i]
}Um exemplo completo:
$ cat dict.dat
uno one
dos two
tres three
cuatro fourawk ' {dict[$1]=$2}
END{if ("uno" in dict)
print "Yes we have uno in dict!"
if (!("cinco" in dict))
print "No , cinco is not in dict!"
for (esp in dict){
print esp, "->" ,dict[esp]
}
} ' dict.datDá a você:
Yes we have uno in dict !
No , cinco is not in dict !
uno - > one
dos - > two
tres - > three
cuatro - > four gawk não classifica as matrizes por padrão:
awk ' BEGIN{
a[4]="four"
a[1]="one"
a[3]="three"
a[2]="two"
a[0]="zero"
exit
}
END{for (idx in a){
print idx, a[idx]
}
} ' 4 four
0 zero
1 one
2 two
3 threeMas você pode aproveitar [procinfo] para classificar:
awk ' BEGIN{
PROCINFO["sorted_in"] = "@ind_num_asc"
a[4]="four"
a[1]="one"
a[3]="three"
a[2]="two"
a[0]="zero"
exit
}
END{for (idx in a){
print idx, a[idx]
}
} ' 0 zero
1 one
2 two
3 three
4 four gensub(regexp, replacement, how [, target]) : é a função mais avançada para substituição da string.
E suas alternativas mais simples:
gsub(regexp, replacement [, target])
sub(regexp, replacement [, target])
Tendo este arquivo:
$ cat lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.Vamos trocar a posição das palavras colocadas à esquerda e à direita de cada vírgula.
$ awk ' {print gensub(/([^ ]+)( *, *)([^ ]+)/,
"\3\2\1", "g")} ' lorem.dat
Lorem ipsum dolor sit consectetur, amet adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim euismod, orci id nisi interdum, eget cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit consectetur, amet adipiscing elit.
Aliquam interdum mauris volutpat nisl et, placerat facilisis. Usando gensub capturamos três grupos e depois trocamos o pedido.
Para ilustrar uma ação mais simples, vamos mudar pontos para vírgulas :
awk ' $0=gensub(/./, ",", "g") ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Maecenas pellentesque erat vel tortor consectetur condimentum,
Nunc enim orci, euismod id nisi eget, interdum cursus ex,
Curabitur a dapibus tellus,
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Aliquam interdum mauris volutpat nisl placerat, et facilisis, Usando gsub Alternative:
awk ' gsub(/./, ",") ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Maecenas pellentesque erat vel tortor consectetur condimentum,
Nunc enim orci, euismod id nisi eget, interdum cursus ex,
Curabitur a dapibus tellus,
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Aliquam interdum mauris volutpat nisl placerat, et facilisis,Esta opção parece melhor quando nenhuma captura de grupo é necessária.
Outras funções interessantes são index e substr .
index(in, find)
substr(string, start [, length ])
Funciona assim:
$ awk ' BEGIN{t="hello-world";print index(t, "-")} '
6$ awk ' BEGIN{t="hello-world";print substr(t,index(t, "-")+1)} '
world A função split é usada para criar uma matriz a partir de uma string que a divide por um char separador , ele retorna o número de elementos da matriz criada.
split(string, array [, fieldsep [, seps ] ])
$ cat passwd
jd001:x:1032:666:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:666:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:668:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:668:Rosa Camacho:/home/rc005:/bin/rbash$ awk ' n=split($0, a, ":"){print n, a[n]} ' passwd
7 /bin/rbash
7 /bin/rbash
7 /bin/bash
7 /bin/rbash
7 /bin/rbashNota : Isso pode ser feito de uma maneira muito mais simples:
$ awk ' {print NF,$NF} ' FS= ' : ' passwd
7 /bin/rbash
7 /bin/rbash
7 /bin/bash
7 /bin/rbash
7 /bin/rbashEscreva uma função personalizada é bastante simples:
awk ' function test(m)
{
printf "This is a test func, parameter: %sn", m
}
BEGIN{test("param")} 'Dê -nos:
This is a test func, parameter: param Também podemos devolver uma expressão usando uma declaração return :
awk ' function test(m)
{
return sprintf("This is a test func, parameter: %s", m)
}
BEGIN{print test("param")} 'A análise por parâmetro é a única maneira de fazer uma variável local dentro de uma função.
Os valores escalares são passados por valor e matrizes por referência; portanto, qualquer alteração feita em uma matriz dentro de uma função será refletida no escopo global:
awk ' function test(m)
{
m[0] = "new"
}
BEGIN{m[0]=1
test(m)
exit
}
END{print m[0]} 'Saídas:
newNossos desafios :
01. Penúltima palavra de um registro.
02. Substituindo um registro.
03. Coloque um semicolon no final de cada registro.
04. Coloque uma vírgula entre cada palavra.
05. Todos juntos?
06. Redirecionando registros ímpares para um arquivo e até para outro.
07. Dado um arquivo de senha, obtenha o campo ausente.
08. Troca de campo.
09. Traceroute Hacking.
10. Onde estão meus filhos?
11. Agregação de dados.
12. Registros entre dois padrões.
13. Transformação de campo.
14. Registros para colunas.
15. Processamento de arquivos FASTA.
16. Relatórios complexos.
17. Arquivos Joiner.
18. Passwd e Group.
19. Conexões de usuário.
20. Média total de carga total do tempo de atividade.
Tendo este arquivo de origem :
$ cat lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ awk ' {print $(NF-1)} ' lorem.dat
adipiscing
consectetur
cursus
dapibus
adipiscing
neque Não é demais para explicar aqui, NF armazena o número de campos no registro atual; portanto, os pontos NF-1 para o campo antes do último e $(NF-1) serão o seu valor.
Nossa tarefa, substituição de registro de arquivos, a terceira linha deve se tornar:
This not latin
Nada mais simples, basta brincar com NR ( número de registros ).
Código:
$ awk ' NR==3{print "This is not latin";next}{print} ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
This is not latin
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis. Solução alternativa para evitar next instrução: atribua a nova linha ao registro completo $0 .
Exemplo:
$ awk ' NR==3{$0="This is not latin"}1 ' lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Maecenas pellentesque erat vel tortor consectetur condimentum.
This is not latin
Curabitur a dapibus tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aliquam interdum mauris volutpat nisl placerat, et facilisis.$ awk ' 1 ' ORS= " ;n " lorem.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ;
Maecenas pellentesque erat vel tortor consectetur condimentum. ;
Nunc enim orci, euismod id nisi eget, interdum cursus ex. ;
Curabitur a dapibus tellus. ;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ;
Aliquam interdum mauris volutpat nisl placerat, et facilisis neque ultrices. ; Como o RS padrão é a linha de interrupção do Unix n precisamos apenas prefixar o semicolon para o OFS de registro de saída.
1 ? 4
$ awk ' {$1=$1}1 ' OFS= ' , ' lorem.dat
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit.
Maecenas,pellentesque,erat,vel,tortor,consectetur,condimentum.
Nunc,enim,orci,,euismod,id,nisi,eget,,interdum,cursus,ex.
Curabitur,a,dapibus,tellus.
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit.
Aliquam,interdum,mauris,volutpat,nisl,placerat,,et,facilisis,neque,ultrices. A parte mais significativa deste código é como força uma reconstrução recorde com $1=$1 para o valor atual dos OFS .
$ awk ' {$1=$1}1 ' OFS= ' , ' ORS= ' ;n ' lorem.dat
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit. ;
Maecenas,pellentesque,erat,vel,tortor,consectetur,condimentum. ;
Nunc,enim,orci,,euismod,id,nisi,eget,,interdum,cursus,ex. ;
Curabitur,a,dapibus,tellus. ;
Lorem,ipsum,dolor,sit,amet,,consectetur,adipiscing,elit. ;
Aliquam,interdum,mauris,volutpat,nisl,placerat,,et,facilisis,neque,ultrices. ; Como simplesmente tocar com vars de saída: OFS e ORS .
Vamos começar com a solução final:
$ awk ' NR%2{print > "even.dat";next}
{print > "odd.dat"} ' lorem.dat$ cat even.dat
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nunc enim orci, euismod id nisi eget, interdum cursus ex.
Lorem ipsum dolor sit amet, consectetur adipiscing elit$ cat odd.dat
Maecenas pellentesque erat vel tortor consectetur condimentum.
Curabitur a dapibus tellus.
Aliquam interdum mauris volutpat nisl placerat, et facilisis. A função [MODULO] ( % ) encontra o restante após a divisão para o número de registro atual NR dividido por dois:
$ awk ' {print NR%2} ' lorem.dat
1
0
1
0
1
0 Até onde agora, no awk 1 é verdadeiro e 0 Falso . Redirecionamos nossa produção avaliando esse fato.
next , requer uma atenção especial, força awk a interromper imediatamente o processo de registro atual e passar para o próximo.
Dessa maneira, iludimos uma condição dupla que se pareceria assim:
awk ' NR % 2{print > "even.dat"}
!NR % 2{print > "odd.dat"} ' lorem.dat$ cat /etc/passwd
jd001:x:1032:666:Javier Diaz::/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez::/bin/rbash
jp003:x:1000:666:Jose Perez::/bin/bash
ms004:x:8051:668:Maria Saenz::/bin/rbash
rc005:x:6550:668:Rosa Camacho::/bin/rbash Vamos supor o diretório doméstico prefixando a sequência fixa "/home/" para o nome de usuário :
$ awk ' $6="/home/"$1 ' FS= ' : ' OFS= ' : ' /etc/passwd
jd001:x:1032:666:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:668:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:666:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:668:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:668:Rosa Camacho:/home/rc005:/bin/rbashNosso primeiro passo deve considerar o separador de campo, um cólon, para obter informações e saídas.
Em seguida, precisamos encontrar a posição do campo vazio, 6 para este exemplo.
Por fim, compotemos o valor necessário usando a string fornecida e o login do usuário armazenado no primeiro campo.
print não é necessária porque o valor de retorno de atribuição $6 sempre será verdadeiro e a ação padrão awk é imprimir o registro afetado.
Nosso objetivo: o último campo deve se tornar o primeiro e o primeiro último.
Código final:
$ awk -F : ' {last=$1;$1=$NF;$NF=last}1 ' FS= " : " OFS= ' : ' /etc/passwd
/bin/rbash:x:1032:666:Javier Diaz:/home/jd001:jd001
/bin/rbash:x:8050:668:Alejandro Gonzalez:/home/ag002:ag002
/bin/bash:x:1000:666:Jose Perez:/home/jp003:jp003
/bin/rbash:x:8051:668:Maria Saenz:/home/ms004:ms004
/bin/rbash:x:6550:668:Rosa Camacho:/home/rc005:rc005 Estamos jogando com uma variável intermediária usada para armazenar o primeiro valor de campo, trocamos seu valor com o último, finalmente atribuímos last variável a $NF ( $NF=last ).
Tendo esta saída:
$ traceroute -q 1 google.com 2> /dev/null
1 hitronhub.home (192.168.1.1) 5.578 ms
2 217.217.0.1.dyn.user.ono.com (217.217.0.1) 9.732 ms
3 10.127.54.181 (10.127.54.181) 10.198 ms
4 62.42.228.62.static.user.ono.com (62.42.228.62) 35.519 ms
5 72.14.235.20 (72.14.235.20) 26.003 ms
6 216.239.50.133 (216.239.50.133) 25.678 ms
7 mad01s24-in-f14.1e100.net (216.58.211.238) 25.019 msPrecisamos calcular o pacote que viaja no tempo total.
$ traceroute -q 1 google.com 2> /dev/null |
awk ' {total+=$(NF-1)}
END{print "Total ms: "total} '
Total ms: 153.424Como nenhuma condição é especificada, a ação é executada para todos os registros .
total+=$(NF-1) : A variável total é usada para acumular o valor de cada campo de penúltimo grau de registro $(NF-1) .
Finalmente, usamos a regra END para mostrar o valor total final.
Nosso trabalho: obtenha nossos processos dependentes shell .
$ echo $$
51026Primeira coisa : inicie os processos em segundo plano.
$ sleep 10 & sleep 15 & sleep 20 &
[1] 86751
[2] 86752
[3] 86753 Usando ps Utility , awk procurará o terceiro campo conhecido como PPID .
Nota : Estamos usando -v para definir o PPID VAR antes do início da execução do programa.
$ ps -ef | awk -v ppid= $$ ' $3==ppid '
501 86751 51026 0 7:57PM ttys001 0:00.00 sleep 10
501 86752 51026 0 7:57PM ttys001 0:00.00 sleep 15
501 86753 51026 0 7:57PM ttys001 0:00.00 sleep 20
0 86754 51026 0 7:57PM ttys001 0:00.00 ps -ef
501 86755 51026 0 7:57PM ttys001 0:00.00 awk $3 ==51026Só precisamos do sono :
$ ps -ef | awk -v ppid= $$ ' $3 == ppid && /slee[p]/
{print $2" -> "$5} '
86751 - > 7:57PM
86752 - > 7:57PM
86753 - > 7:57PM A solução precisa de uma nova condição para adicionar: encontre o padrão de sono em nosso registro /slee[p]/ .
A ação desencadeada será imprimir o segundo campo $2 , com o PID e o quinto $5 , o carimbo de hora .
Tendo este arquivo:
$ cat ips.dat
IP BYTES
81.220.49.127 328
81.220.49.127 328
81.220.49.127 329
81.220.49.127 367
81.220.49.127 5302
81.226.10.238 328
81.227.128.93 84700 Nossa tarefa é calcular quantos bytes por IP são processados.
$ awk ' NR>1{ips[$1]+=$2}
END{for (ip in ips){print ip, ips[ip]}} ' ips.dat
81.220.49.127 6654
81.227.128.93 84700
81.226.10.238 328Bando de coisas aqui para explicar.
NR>1{ips[$1]+=$2} : a ação ips[$1]+=$2 é executada apenas quando o número do registro atual é maior que um NR>1 . Isso é necessário para evitar o cabeçalho.
ips é uma matriz indexada pelo valor IP (o campo de $1 ), para cada tecla que vamos acumular no valor do segundo campo.
Observe um fato importante , se uma chave não estiver presente na matriz , awk adicionará um novo elemento à estrutura , caso contrário, atualizará o valor anterior apontado por essa chave (como em nosso exemplo).
A regra END é usada apenas para iterar a matriz por índices e valores.
Este código pode ser reescrito de maneira completa para evitar o uso de matrizes e preservar o pedido aproveitado o arquivo IPS classificado:
awk ' NR==1{next}
lip && lip != $1{print lip,sum;sum=0}
{sum+=$2;lip=$1}
END{print lip,sum} ' ips.dat
81.220.49.127 6654
81.226.10.238 328
81.227.128.93 84700 NR==1{next} : ignorar o cabeçalho.
lip && lip != $1{print lip,sum;sum=0} : Aqui usamos um Var chamado lip ( Last-IP ). lip && lip != $1 Quando lip não é nulo e o valor não é igual ao primeiro campo (que mantém o IP atual), a ação acionada será imprimir lip e sum a quantidade total de bytes para o último IP. Então inicializamos a sum=0 .
O truque é claro, toda vez que o IP ( $1 ) altera, mostramos as estatísticas do anterior.
{sum+=$2;lip=$1} : para atualizar o contador de bytes sum+=$2 e atribuir o IP atual ao lip : lip=$1 . É a última etapa do nosso processamento de registros.
O bloco END é usado para imprimir os valores pendentes .
Esse código preserva a ordem, mas, na minha opinião , isso ocorre à custa de uma complexidade significativamente aumentada.
Nossa tarefa é duas linhas entre e OUTPUT e END .
$ cat pat.dat
test -3
test -2
test -1
OUTPUT
top 2
bottom 1
left 0
right 0
page 66
END
test 1
test 2
test 3 Este é um exemplo clássico usado para ilustrar como a correspondência de padrões funciona no awk e em suas ações associadas que dediquei a um [Post] completo.
$ awk ' /END/{flag=0}flag;/OUTPUT/{flag=1} ' pat.dat
top 2
bottom 1
left 0
right 0
page 66 É baseado no valor variável flag , será verdadeiro ( 1 ) quando a OUTPUT do padrão inicial for encontrada e falsa ( 0 ) quando a etiqueta END for atingida.
Para evitar uma etapa adicional, a ordem de ação é muito importante , se seguirmos a sequência lógica :
$ awk ' /OUTPUT/{flag=1}flag;/END/{flag=0} ' pat.dat
OUTPUT
top 2
bottom 1
left 0
right 0
page 66
ENDAs tags de padrão são mostradas através da saída.
O motivo: após o padrão OUTPUT , o sinalizador é ativado, pois a próxima ação depende desse sinalizador, o registro é impresso.
Podemos evitar esse comportamento colocando a ativação da bandeira como a última etapa do fluxo.
Vamos supor este arquivo:
$ cat space.dat
10.80 kb
60.08 kb
35.40 kb
2.20 MB
1.10 MB
40.80 kb
3.15 MB
20.50 kbNosso trabalho será calcular nossos registros em peso total em mega bytes :
$ awk ' {total+= $1 / ($2=="kb" ? 1024: 1)}
END{print total} ' space.dat
6.61365Para entender como funciona, um conceito deve ser claro, o operador [ternário] (sujeito de um post antigo).
total será usado para acumular a divisão do primeiro campo de $1 pelo segundo $2 , que manterá o valor dado pelo operador ternário : 1024 quando $2 forem iguais a kb e 1 se não for necessária uma transformação.
Finalmente, imprimimos o valor total no bloco END .
Fonte original:
$ cat group.dat
string1
string2
string3
string4
string5
string6
string8Nossa missão é agrupar registros em blocos de três colunas como esta:
string1 string2 string3
string4 string5 string6
string8 Pode parecer complexo, mas se torna muito mais simples se entendermos como usar o separador de campo de saída OFS :
$ awk ' ORS = NR%3 ? FS : RS; END{print "n"} ' group.dat
string1 string2 string3
string4 string5 string6
string8 Se definirmos o ORS como um caractere em branco , o valor padrão FS , toda a saída se tornará uma única linha:
$ awk ' ORS=FS; END{print "n"} ' group.dat
string1 string2 string3 string4 string5 string6 string7 ORS = NR%3 ? FS : RS : Finalmente, usamos o operador ternário (explicado pouco antes) para avaliar o resultado [MODULO] NR%3 da divisão do número atual do campo NR por três.
Se o restante for verdadeiro , o ORS se tornará FS , um espaço em branco , caso contrário, o valor padrão RS será atribuído, a quebra da linha Unix n .
Em Bioinformática , [FASTA] é um formato de arquivo baseado em texto.
Tendo o seguinte exemplo:
$ cat fasta.dat
> header1
CGCTCTCTCCATCTCTCTACCCTCTCCCTCTCTCTCGGATAGCTAGCTCTTCTTCCTCCT
TCCTCCGTTTGGATCAGACGAGAGGGTATGTAGTGGTGCACCACGAGTTGGTGAAGC
> header2
GGT
> header3
TTATGATPrecisamos do comprimento total de cada sequência e um currículo final .
Deve ser assim:
> header1
117
> header2
3
> header3
7
3 sequences, total length 127 awk é a ferramenta perfeita para este esforço de relatório , para este exemplo que usaremos:
awk ' /^>/ { if (seqlen) {
print seqlen
}
print
seqtotal+=seqlen
seqlen=0
seq+=1
next
}
{
seqlen += length($0)
}
END{print seqlen
print seq" sequences, total length " seqtotal+seqlen
} ' fasta.dat
A primeira ação está ligada à detecção de cabeçalho /^>/ , isso é porque todos os cabeçalhos estrelam > personagem.
Quando seqlen não é nulo , seu valor, que mantém o comprimento da sequência anterior, é impresso no stdout conectado ao novo cabeçalho. seqtotal é atualizado e seqlen inicializado para servir a próxima sequência. Finalmente, quebramos mais o processamento de registros com next .
A segunda ação {seqlen += length($0)} é usada para atualizar seqlen soma o comprimento total do registro.
O objetivo da regra END é mostrar a sequência não impressa e os totais.
O truque aqui é imprimir o comprimento da sequência anterior quando encontramos um novo cabeçalho .
Quando processamos o primeiro registro seqlen não tem valor, então ignoramos a visualização.
Fonte:
$ cat report.dat
snaps1: Counter: 4966
Opens: Counter: 357283
Instance: s.1.aps.userDatabase.mount275668.attributes
snaps1: Counter: 0
Opens: Counter: 357283
Instance: s.1.aps.userDatabase.test.attributes
snaps1: Counter: 5660
Opens: Counter: 37283
Instance: s.1.aps.userDatabase.mount275000.attributes Nosso dever : crie um relatório para visualizar snaps e instance , mas somente quando o Snap First Counter tag é maior que zero .
Saída esperada:
snaps1: Counter: 4966
Instance: s.1.aps.userDatabase.mount275668.attributes
snaps1: Counter: 5660
Instance: s.1.aps.userDatabase.mount275000.attributesEstamos jogando novamente em torno de padrões e bandeiras :
awk ' {$1=$1}
/snaps1/ && $NF>0{print;f=1}
f && /Instance/ {print;f=0} ' report.dat Para cada registro que a primeira ação é executada , força awk a reconstruir todo o registro , usando os valores atuais para OFS 3 .
Esse truque nos permite converter um separador de espaço múltiplo em um único char , o valor padrão para o separador de campo de saída.
Vamos ver isso:
$ awk ' 1 ' text.dat
one two
three four$ awk ' $1=$1 ' text.dat
one two
three four A segunda ação é acionada quando o padrão é encontrado e o último campo maior que zero /snaps1/ && $NF>0 .
awk imprime o registro e atribui um valor verdadeiro à print;f=1 .
Última etapa: quando o sinalizador é verdadeiro e o padrão de instância na linha f && /Instance/ , mostre a linha e o sinalizador desativado: print;f=0 .
Vamos supor dois arquivos:
$ cat join1.dat
3.5 22
5. 23
4.2 42
4.5 44$ cat join2.dat
3.5
3.7
5.
6.5 Precisamos dos registros do primeiro join1.dat quando os primeiros campos estão no segundo join2.dat .
A saída deve ser:
3.5 22
5. 23Podemos usar o UNIX Junção Utility , é claro, mas precisamos classificar o primeiro arquivo:
$ join <( sort join1.dat ) join2.dat
3.5 22
5. 23 Não é necessário em awk :
$ awk ' NR == FNR{a[$1];next}
$1 in a ' join2.dat join1.datVamos estudar os filtros e as ações:
NR == FNR : número do registro igual ao número do arquivo de gravação significa que estamos processando o primeiro arquivo analisado para awk : join2.dat .
A ação do par a[$1];next será adicionar um novo valor de vazio à matriz indexada pelo primeiro campo. next declaração quebrará o processamento do registro e passará o fluxo para o próximo .
Para a segunda ação NR != FNR é aplicada implicitamente e afeta apenas o join1.dat , a segunda condição é de $1 in a , que será verdadeira quando o primeiro campo de join1.dat for uma chave de matriz.
Estes são para o Unix Classics :
$ cat /etc/group
dba:x:001:
netadmin:x:002:$ cat /etc/passwd
jd001:x:1032:001:Javier Diaz:/home/jd001:/bin/rbash
ag002:x:8050:002:Alejandro Gonzalez:/home/ag002:/bin/rbash
jp003:x:1000:001:Jose Perez:/home/jp003:/bin/bash
ms004:x:8051:002:Maria Saenz:/home/ms004:/bin/rbash
rc005:x:6550:002:Rosa Camacho:/home/rc005:/bin/rbashNosso objetivo, um relatório como este:
d001:dba
ag002:netadmin
jp003:dba
ms004:netadmin
rc005:netadminPrecisamos de um fluxo de arquivo múltiplo como estudamos em nosso último exemplo:
$ awk -F : ' NR == FNR{g[$3]=$1;next}
$4 in g{print $1""FS""g[$4]} ' /etc/group /etc/passwd Para processar /etc/group repetimos a comparação NR == FNR e depois armazenamos o nome do grupo $1 indexado por seu ID $3 : g[$3]=$1 . Finalmente, quebramos mais o processamento de registros com next .
A segunda condição segmentará apenas registros /etc/passwd , quando o quarto campo $4 ( ID do grupo ) estiver presente na matriz $4 in g , imprimiremos o login e o valor apontado pela matriz indexada pelo ID do grupo g[$4] , então: print $1""FS""g[$4] .
Exemplo de saída do utilitário de usuários:
$ users
negan rick bart klashxx klashxx ironman ironman ironmanVamos contar logons por usuário.
$ users | awk ' {a[$1]++}
END{for (i in a){print i,a[i]}} ' RS= ' + '
rick 1
bart 1
ironman 3
negan 1
klashxx 2A ação é executada para todos os registros.
a[$1]++ : este é o contador , para cada usuário $1 incrementa o valor pontual (os vars não inicializados têm o valor numérico zero).
No bloqueio END iterem a matriz por chave e o valor armazenado para apresentar os resultados.
Uma saída típica :
$ uptime
11:08:51 up 121 days, 13:09, 10 users, load average: 9.12, 14.85, 20.84Como podemos obter a média total de carga ?
$ uptime | awk ' {printf "Load average mean: %0.2fn",
($(NF-2)+$(NF-1)+$(NF))/3 } ' FS= ' (:|,) + '
Load average mean: 14.94Aqui está uma nova técnica.
Estamos usando um [regex] como o separador de campo (:|,) + , para que o FS possa ser um cólon e uma vírgula seguida por zero ou mais espaços em branco.
Só precisamos dos três últimos campos para executar a aritmética necessária e, em seguida, usamos printf conectado a uma máscara adequada .
Se você ainda está aqui, obrigado !!
Do meu ponto de vista, awk é um idioma subestimado e precisa de muito amor.
Se você estiver com fome por mais, deixe -me saber na seção de comentários abaixo e considerarei uma segunda parte para terminar minha missão ... o aborrecerá até a morte.
Codificação feliz!
[Um guia para a citação do Unix Shell] [citando-guide]. ↩
[Wikipedia em pipelines] [tubos]. ↩
Há momentos em que é conveniente forçar awk a reconstruir todo o registro, usando os valores atuais do FS e OFS .
Para fazer isso, usamos a atribuição aparentemente inócua: $1 = $1 ↩ ↩ 2
Resposta rápida, é apenas um atalho para evitar o uso da instrução de impressão.
No awk quando uma condição é correspondente, a ação padrão é imprimir a linha de entrada.
$ echo "test" |awk '1'
É equivalente a:
echo "test"|awk '1==1'
echo "test"|awk '{if (1==1){print}}'
Isso porque 1 sempre será [verdadeiro]. ↩