chamada de sistema awk com efeito invertido

2

Eu tenho um arquivo de dados com vários blocos de dados entre palavras-chave específicas ( DATA , END ). Eu estou usando awk para extrair os blocos de dados em arquivos separados, com base em um nome de arquivo retirado do referido bloco. Como alguns blocos de dados compartilham o mesmo nome, estou renomeando cada arquivo de saída com um inteiro crescente se o arquivo (" blockname ") já existir:

#cat input.file
useless stuff1
DATA blockname1
data1
data1
END
useless stuff2
DATA blockname2
data2
data2
END
useless stuff3
DATA blockname1
data3
data3
END
useless stuff4

O esperado seria três arquivos de saída blockname1 , blockname2 e blockname1_1 (observe como o último arquivo tem um número inteiro atribuído a ele)

#cat blockname1
DATA blockname1
data1
data1
END

(os outros de acordo ...)

Agora, o script a seguir funciona como eu quero:

awk 'BEGIN { FS=" +" } ; \
       /DATA/,/END/ \
               { if ( $1 ~ /DATA/ ) \
                       { block=$2 ; i=0 ; file=block ;\
                               while ( system("test ! -e " file ) ) \
                                       { i++ ; file=block"_"i ; print file } \
                       } ; \
               print $0 > file \
              } ' \ 
input.file

Meu problema é o loop while e sua chamada de sistema:

Eu esperava que system("test -e " file) fosse TRUE quando o file existisse e ser FALSE se file ainda não existisse, ou seja, o loop while só comece a ser executado se file estiver presente e quebrar se (o novo) file ainda não existe.

No entanto, se eu usar system("test -e " file) (e torná-lo detalhado com print file ), eu tenho um loop infinito com o mesmo nome aumentando o sufixo inteiro e o oposto system("test !-e " file) dá o resultado desejado.

Então, isso se comporta exatamente ao contrário do que eu esperava.

    
por Fiximan 16.11.2016 / 18:51

3 respostas

2

OK, imaginei: o problema está nas diferentes definições do que é TRUE e FALSE entre o status de saída de test e a condição while do loop em awk .

Um comando test postivo resulta em um código de saída de 0 para TRUE e um negativo em 1 para FALSE.

No entanto, em awk , o loop while interpreta 0 como FALSE e 1 como TRUE, portanto, exatamente a definição oposta.

Como exemplo:

awk '{ while ( 0 ) ; { print "0" } }' file

não produzirá nenhuma saída, enquanto

awk '{ while (1) ; { print "1" } }' file

imprime 1 s infinito.

A melhor prática é, portanto, ser explícita em tal combinação

while ( system("command") == 0 )

ou

while ( system("command") == 1 )

respectivamente.

Então, no meu caso

while ( system("test -e " file ) == 0 ) 

mostra o comportamento esperado.

    
por 16.11.2016 / 19:15
1

awk system() retorna um status de saída do comando executado - 0 para sucesso e! = 0 se não for sucesso. Por exemplo, você pode tentar executar:

v = system("date");

v será 0

se você executar:

v = system("dat");

v pode ser 127 ou um valor diferente de 0, o erro retornado do sistema operacional se o comando dat estiver ausente ou não for encontrado.

    
por 16.11.2016 / 19:19
1

Se eu entendi você, o objetivo é extrair o conteúdo de input.file em vários arquivos evitando perder blocos com o mesmo nome.

Se esse for o caso e, se o diretório de destino estiver sempre vazio antes da extração, haverá uma solução melhor (e mais rápida):

awk '
/DATA/{
    block=$2;
    n = blocks[block]++;
    file=block (n? "_" n: "");  
}
/DATA/,/END/{
    print > file
}' input.file

Desta forma o awk não precisa executar um novo shell N vezes apenas para testar se o arquivo existe.

Notas:

  • Não há necessidade do bloco BEGIN, porque o separador de campos do awk é já espaços.
  • Não há necessidade de '\' no final das linhas porque a aspa simples já é multilinha.
por 16.11.2016 / 20:33