awk chamadas do sistema para o linux - usando regex estendido ao chamar o shell do awk

1

É útil poder chamar comandos do sistema a partir do awk. No entanto, se você tentar usar o regex estendido do shell, verá que ele não funciona.

Isso acontece porque o awk chama / bin / sh em vez de / bin / bash como você esperaria no linux atualmente.

Como é possível obter o regex estendido para funcionar ao chamar o sistema do awk?

    
por Peter Brooks 26.10.2017 / 14:39

2 respostas

2

Não vejo por que você precisa fazer tanto no bash quando o awk é perfeitamente capaz:

BEGIN {
    filename[0]="/media/Pan/test-data/The_long_file.gz";
    filename[1]="/media/Pan/test-data/The_long_file";

    for (n=0; n<2; n++) {
        print "Contents  of file: " filename[n];

        if (filename[n] ~ /\.gz$/) {
            command = "gunzip --to-stdout " filename[n]
            while (( command | getline file_contents ) > 0 ) {
                print file_contents
            }
            close(command)
        }
        else {
            while (( getline line < filename[n]) > 0 ) {
                print line
            }
        }
    }
}
    
por 26.10.2017 / 16:00
2

É útil poder chamar comandos do sistema a partir do awk. No entanto, se você tentar usar o regex estendido do shell, verá que ele não funciona.

Isso acontece porque o awk chama / bin / sh em vez de / bin / bash como você esperaria no linux atualmente.

Existe uma solução que não é muito confusa. Se você precisar ler informações de vários arquivos, alguns dos quais são compactados, alguns dos quais não são, você pode usar expressões regulares estendidas no awk assim:

BEGIN   {
        filename[0]="/media/Pan/test-data/The_long_file.gz";
        filename[1]="/media/Pan/test-data/The_long_file";
        for ( n=0;n<2;n++)
                {
                print "Contents  of file: " filename[n];
                command="exec /bin/bash -c \"[[ \"" filename[n] "\" =~ .gz ]] \
                &&gunzip --to-stdout " filename[n] "\
                ||cat " filename[n] "\"";
                while (( command | getline file_contents ) > 0 )
                        print file_contents;
                }
        }

Este exemplo lista o conteúdo do mesmo arquivo / media / Pan / teste-dados / The_long_file duas vezes, uma vez para a versão compactada, uma vez para o texto simples.

Para testar o acima, copie-o para test.awk, crie dois arquivos, um compactado, um descompactado e coloque seus nomes no nome de arquivo [0] e [1], e execute-o:

awk -f test.awk </dev/null

O exemplo em si não é muito útil, eu sei, mas os caracteres de escape e aspas estão todos nos lugares certos, e a substituição de / bin / sh por / bin / bash funciona.

Espero que isso salve a pessoa o tempo que levei para obter a sintaxe correta.

O código acima resolve o problema causado por awk calling / bin / sh usando exec para substituir / bin / sh. O código que é passado para o shell é:

 exec /bin/bash -c "[[ \"filename\" =~ .gz ]] &&gunzip --to-stdout filename ||cat filename"

O código que o bash executa é:

 [[ "filename" =~ .gz ]] &&gunzip --to-stdout filename ||cat filename

A expressão regular estendida acima verifica se "filename" corresponde à expressão ".gz". Em caso afirmativo, executa o gunzip. Se isso não acontecer, simplesmente gatos o arquivo. Você poderia melhorar a expressão regular substituindo "." com ".", por isso só corresponde a um ".", e adicionando um "$", por isso só coincide com ele no final da linha - Eu não fiz isso para preservar a clareza.

    
por 26.10.2017 / 14:39