Crie uma nova coluna em cada ocorrência de um valor de linha específico usando awk [duplicate]

1

Eu tenho um arquivo de texto column.txt com duas colunas como mostrado abaixo:

  1 1.1
  2 4.0
  3 3.2
  start newset
  1 2.2
  2 6.1
  3 10.3
  4 2.1
  start newset
  1 18.2
  2 4.3

Eu preciso converter isso em várias colunas, de modo que um par de novas colunas seja criado quando iniciar o conjunto de notícias for atingido. Portanto, meu arquivo de texto de saída desejado ficaria como (eu gostaria de descartar a linha com string start newset ):

  1 1.1 1 2.2 1 18.2
  2 4.0 2 6.1 2 4.3
  3 3.2 3 10.3
        4 2.1
    
por flappingwings 20.05.2018 / 11:39

4 respostas

2

Você pode tentar este awk

awk '
    /^start newset/ {
        max = max>i ? max : i
        i = 0
        set++
        next
    }
    {
        ++i
        a[i][set] = $0
    }
    END {
        for( i=1 ; i<=max ; i++ ) { 
            for( j=1 ; j<=set ; j++ )
                b = b OFS a[i][j]
            sub( "\t" , "" , b )
            print b
            b=""
        }
    }
' set=1 OFS='\t' column.txt
    
por 20.05.2018 / 18:09
2

csplit & colar

Use csplit para dividir um arquivo em vários arquivos em um padrão. Em seguida, use paste para unir os novos arquivos.

awk 'NF' column.txt | csplit --suppress-matched -s -z -f INTERIM -n 4 - '/start newset/' '{*}' ; paste INTERIM* | expand -t 6,13 ; rm -f INTERIM*

O mesmo código, reformatado para maior clareza:

awk 'NF' column.txt | \
csplit --suppress-matched -s -z -f INTERIM -n 4 - '/start newset/' '{*}' ;

paste INTERIM* | \
expand -t 6,13 ;

rm -f INTERIM*

Descrição:

  • awk 'NF' column.txt
    Remover linhas vazias. Caso contrário, linhas vazias no arquivo de entrada colocariam separadores de colunas extras na saída.
  • csplit
    • --suppress-matched
      Não inclua linhas contendo o padrão de divisão na saída.
    • -s
      Não mostra informações resumidas sobre os arquivos de saída.
    • -z
      Não produz arquivos de saída vazios (isto é, quando duas linhas adjacentes do arquivo de entrada contêm o padrão de divisão).
    • -f INTERIM
      Os nomes de arquivos dos arquivos divididos começam com esta string.
    • -n 4
      Os nomes de arquivos dos arquivos divididos terminam com um número contendo muitos dígitos.
    • -
      Recebe entrada de STDIN , já que primeiro executamos o arquivo de entrada por meio de awk .
    • '/start newset/'
      Divida o arquivo de entrada na primeira linha que contenha esta expressão regular.
    • '{*}'
      Continue dividindo o arquivo de entrada em cada linha adicional que contém essa expressão regular.
  • paste INTERIM*
    Junte os arquivos temporários.
  • expand -t 6,13
    Ajusta o espaçamento da coluna entre os arquivos unidos (por exemplo, inicia o segundo arquivo na coluna 6 e o terceiro arquivo na coluna 13).
  • rm -f INTERIM*
    Exclua os arquivos temporários.

Exemplo de arquivo de entrada column.txt :

1 1.1
2 4.0
3 3.2
start newset
1 2.2
2 6.1
3 10.3
4 2.1
start newset
1 18.2
2 4.3

Exemplo de saída:

1 1.1 1 2.2  1 18.2
2 4.0 2 6.1  2 4.3
3 3.2 3 10.3 
      4 2.1  

É um pouco mais complicado se as linhas do arquivo de entrada e a saída final forem recuadas.

Exemplo de arquivo de entrada column.txt :

  1 1.1
  2 4.0
  3 3.2
  start newset
  1 2.2
  2 6.1
  3 10.3
  4 2.1
  start newset
  1 18.2
  2 4.3
  • Altere awk 'NF' para awk 'NF { sub(/^ +/,"",$0) ; print $0 }' para remover o recuo antes de continuar o processamento.
  • Altere expand -t 6,13 para awk '{ print " " $0 }' | expand -t 8,15 para recuar a saída.

Exemplo de saída:

  1 1.1 1 2.2  1 18.2
  2 4.0 2 6.1  2 4.3
  3 3.2 3 10.3 
        4 2.1  
    
por 21.05.2018 / 10:12
1

Tomando o caminho através de alguns arquivos temporários:

$ awk 'BEGIN { n = 1 } /^start newset/ { n++; next } { name = sprintf("tmp-%04d", n); print >name }' file

Isso não produzirá saída no terminal, mas criará arquivos chamados tmp-n , em que n é um inteiro de quatro dígitos cheio de zero maior ou igual a 1. Haverá um arquivo para cada conjunto de dados .

Então, podemos colar esses arquivos temporários juntos:

$ paste tmp-*
1 1.1   1 2.2   1 18.2
2 4.0   2 6.1   2 4.3
3 3.2   3 10.3
        4 2.1

ou com espaços como delimitadores em vez de guias:

$ paste -d ' ' tmp-*
1 1.1 1 2.2 1 18.2
2 4.0 2 6.1 2 4.3
3 3.2 3 10.3
 4 2.1

Se houver um número grande de conjuntos nos dados, haverá dois problemas com isso:

  1. Você pode ficar sem filodescriptors em awk . Isso pode ser protegido ao alterar o segundo bloco de código awk de

    { name = sprintf("tmp-%04d", n); print >name }
    

    para

    { name = sprintf("tmp-%06d", n); print >>name; close(name) }
    

    (observe também a alteração na string de formatação, para permitir números maiores)

  2. Pode haver problemas ao executar o comando paste , pois o padrão tmp-* se expande para muitos arquivos. Deixe-me saber se isso é um problema e vou classificá-lo (haverá um loop de shell que acumula o resultado adicionando colunas dos arquivos tmp-* ).

por 20.05.2018 / 11:48
0
$ awk '$1+0>=1{a[$1]=a[$1]" "$0}END{for (i in a)print a[i]}' file.txt 
 1 1.1 1 2.2 1 18.2
 2 4.0 2 6.1 2 4.3
 3 3.2 3 10.3
 4 2.1

o comando awk acima cria uma matriz chamada a e armazena / anexa os valores baseados na primeira coluna. Depois de ler o arquivo completamente, basta imprimir os valores da matriz.

step 1 : a[1] = "1 1.1"
step 2 : a[2] = "2 4.0"
step 3 : a[3] = "3 3.2"
step 4 : ignore the line # 4. because the first column is not numeric
step 5 : a[1] = "1 1.1 1 2.2"
step 6 : a[2] = "2 4.0 2 6.1".
....
...
once the file is fully procssed by awk, then just print the array values a[1],a[2],a[3]...a[n]
    
por 20.05.2018 / 11:46