substituição de comando dentro do awk

2

Existe uma maneira de executar a substituição de comandos dentro do AWK e poder referenciar os campos dentro do comando substituído usando a notação $n do AWK?

Por exemplo,

find | awk '/txt$/ {nl = $(wc -l $NF); print nl}'

Eu esperava que o texto acima imprimisse o número de linhas em cada arquivo .txt . Em vez disso, ele efetivamente retorna a mesma saída que:

find | awk '/txt$/ {print}'

Q1: existe uma maneira de realizar a substituição de comandos dentro do awk?

Q2: por que o primeiro encantamento acima está falhando silenciosamente e está simplesmente imprimindo os nomes dos arquivos?

Por favor, note o acima é oferecido apenas como um exemplo. Não estou perguntando como imprimir o número de linhas de cada arquivo por outros meios. Por exemplo, for f in $(find -iname \*.txt); do wc -l $f; done

A questão é especificamente sobre como alavancar a substituição de comandos em programas AWK.

    
por Marcus Junius Brutus 27.07.2017 / 22:27

2 respostas

4

Primeiro, um aviso de isenção: Por favor, não analise a saída de find . O código abaixo é apenas para ilustração, de como incorporar a substituição de comandos em um script Awk de tal maneira que os comandos possam atuar sobre partes da entrada do Awk.

Para realmente fazer uma contagem de linha ( wc -l ) em cada arquivo encontrado com find (que é o caso de uso de exemplo), use:

 find . -type f -name '*txt' -exec wc -l {} +

No entanto, para responder às suas perguntas conforme solicitado:

Q1

Para responder ao seu primeiro trimestre:

Q1: is there a way to perform command substitution inside awk?

Claro que existe uma maneira, de man awk :

command | getline [var] Run command piping the output either into $0 or var, as above, and RT.

Então (veja a citação !!):

find . | awk '/txt$/{"wc -l <\"" $NF "\"|cut -f1" | getline(nl); print(nl)}'

Por favor, note que a string construída e, portanto, o comando executado é

wc -l <file

Para evitar a impressão do nome do arquivo de wc .

Bem, evitei um arquivo "fechado" necessário para esse comando (seguro para alguns arquivos, mas tecnicamente incorreto). Você realmente precisa fazer:

find . | awk '/txt$/{
                       comm="wc -l <\"" $NF "\" | cut -f1"
                       comm | getline nl;
                       close (comm);
                       print nl 
                    }'

Isso também funciona para versões antigas do awk.
Lembre-se de evitar a impressão de um ponto . com find . , que faz o código falhar como um ponto é um diretório e wc não pode usar isso.

Ou, evite o uso de valores de ponto:

find . | awk '/txt$/ && $NF!="." {  comm="wc -l <\"" $NF "\" | cut -f1"
                                    comm | getline nl;
                                    close (comm);
                                    print nl 
                                 }'

Você pode converter isso para um de uma linha, mas vai parecer bem feio, pensa Me.

Q2

Quanto à sua segunda pergunta:

Q2: why is the first incantation above silently failing and is simply printing the filenames instead?

Porque o awk não analisa corretamente os comandos do shell. Entenda o comando como:

nl = $(wc -l $NF)
nl --> variable
$ --> pointer to a field
wc --> variable (that has zero value here)
-  --> minus sign
l  --> variable (that has a null string)
$  --> Pointer to a field
NF --> Last field

Em seguida, l $NF se torna a concatenação de null e o text dentro do campo las (um nome de um arquivo). A expansão desse texto como uma variável numérica é o valor numérico 0

Para awk, torna-se:

nl = $( wc -l $NF)
nl = $ ( 0 - 0 )

O que se torna apenas $0 , toda a entrada de linha, que é (para a simples descoberta acima) apenas o nome do arquivo.

Assim, todos os itens acima só imprimirão o nome do arquivo (bem, tecnicamente, toda a linha).

    
por 28.07.2017 / 02:39
1

Use "weak quotes" em vez de 'strong quotes' para que a expansão de subshell ocorra em um script awk , mas fazer isso no seu exemplo não seria uma implementação particularmente valiosa. Também parece fantasticamente feio:

$ awk "END { print \"$(echo hello)\"} " < /dev/null
hello
    
por 27.07.2017 / 22:35

Tags