Imprimindo uma nova linha usando awk

Imprimindo uma nova linha usando awk

Tenho vários arquivos dos quais preciso retirar linhas específicas e depois colocar os dados retirados em uma planilha. Um exemplo seria meu arquivo mostra:

Name: w

Age: x

Height: y

Weight: z

Só quero a idade, altura e peso, então corri primeiro:

grep -E 'Age|Height|Weight' [input file] > output.txt

Devido ao número de arquivos, minha saída agora se parece com

Age 1
 
Height 1

Weight 1

Age 2

Height 2

Weight 2

etc...

O que eu quero agora é executar um script awk para que ele passe pelo meu novo arquivo output.txt e primeiro encontre cada linha com a palavra 'Idade' e a imprima. Depois de ter feito todos os 'Idade', ele então faz a altura e depois o peso. Executei o script:

awk -F"\t" '/Age/ {print} /Height/ {print}' output.txt >output2.txt

Mas se apenas imprimir como o arquivo original. Como faço para mudar isso depois de ter feito todas as idades, ele encontra as alturas?

EDITAR:

Minha saída desejada é que o arquivo seja

1 idade

2 anos

Altura 1

Altura 2

Peso 1

Peso 2

etc..

Só para esclarecer, Idade 1 é a linha com 'idade' do arquivo 1, etc.

Responder1

O awk só percorre o arquivo uma vez por padrão, executando todos os blocos em ordem, e é por isso que ele fornece a saída obtida. Você pode obter o comportamento desejado usandouma matrizpara salvar as linhas conforme você avança, enquanto ainda processa o arquivo apenas uma vez:

BEGIN {
    AgeIndex = 1
    HeightIndex = 1
}
/Age/ {
    ages[AgeIndex] = $0
    AgeIndex+=1
}
/Height/ {
    heights[HeightIndex] = $0
    HeightIndex+=1
}
END {
    for (x = 1; x < AgeIndex; x++)
        print ages[x] "\n"
    for (x = 1; x < HeightIndex; x++)
        print heights[x] "\n"
}

Salve isso em, digamos, filter.awke execute:

awk -f filter.awk output.txt > output2.txt

para obter a saída desejada:

$ awk -f filter.awk < data
Age 1

Age 2

Height 1

Height 2

O que estamos fazendo é criar dois arrays agese heightssalvar cada linha correspondente neles à medida que avançamos. AgeIndexmantém o quão longe estamos no array. No final, imprimimos cada linha que salvamos (e uma nova linha extra como você deseja), primeiro todas as idades, depois todas as alturas.

Os arrays manterão o arquivo inteiro na memória até o final, portanto, se o seu arquivo for particularmente grande, você terá que compensar o uso de memória pelo tempo necessário para percorrer o arquivo inteiro mais de uma vez. Neste ponto, é essencialmente igual a um programa em qualquer outra linguagem - se você não tiver nenhum motivo específico para usar o awk, poderá preferir outra linguagem. Para ser honesto, acho que recomendo isso - awk não está comprando muito para você aqui.

Responder2

Com gawk:

$ awk -F"\t" '
    { a[$1]++ }
    END {
        n = asorti(a,b);
        for (i = 1; i <= n; i++) {
            print b[i];
            if (i%2 == 0) {
                printf "\n";
            }
        }
    }
' output.txt
Age 1
Age 2

Height 1
Height 2

Weight 1
Weight 2

Responder3

Presumo que as linhas em branco não façam parte do seu arquivo real ou que pelo menos você não se importe com elas. Se sim, tudo que você precisa é sort:

$ cat output.txt
Age 1
Height 1
Weight 1
Age 2
Height 2
Weight 2

$ sort output.txt
Age 1
Age 2
Height 1
Height 2
Weight 1
Weight 2

No entanto, a menos que seus arquivos sejam grandes demais para serem armazenados na memória, pode ser mais simples fazer tudo em uma única etapa:

grep -whE 'Age|Height|Weight' *txt | sort > outfile

O acima irá procurar por Ageou Heightou Weightem todos os arquivos cujo nome termina txtno diretório atual ( *txt). O -wmeio "corresponder apenas palavras inteiras" (para que Agenão corresponda, Ageingpor exemplo), -hé necessário porque sem ele, o nome do arquivo é impresso junto com a linha correspondente quando mais de um arquivo de entrada é fornecido. O -Ehabilita expressões regulares estendidas que nos fornecem |OR.

OBSERVAÇÃO: Se, por algum motivo, você realmente quiser a linha extra em branco entre cada entrada (que não é o que seu grepcomando produziria), você pode adicioná-la com:

grep -whE 'Age|Height|Weight' *txt | sort | sed 's/$/\n/'

Exemplo

$ for i in {1..3}; do echo -e "Name $i\nAge $i\nHeight $i\nWeight $i" > $i.txt; done
$ for f in *txt; do echo " -- $f --"; cat $f; done
 -- 1.txt --
Name 1
Age 1
Height 1
Weight 1
 -- 2.txt --
Name 2
Age 2
Height 2
Weight 2
 -- 3.txt --
Name 3
Age 3
Height 3
Weight 3

$ grep -whE 'Age|Height|Weight' *txt | sort
Age 1
Age 2
Age 3
Height 1
Height 2
Height 3
Weight 1
Weight 2
Weight 3

De qualquer forma, mesmo que sortnão seja adequado para você, eu faria esse tipo de coisa em Perl, não awk(isso pressupõe que você queira as linhas em branco extras que, novamente, provavelmente não deseja):

$ perl -ane '$k{$F[0]}.=$_."\n" if /./; 
    END{print $k{$_},"\n" for sort keys (%k)}' output.txt 
Age 1

Age 2


Height 1

Height 2


Weight 1

Weight 2


 

Você pode passar isso head -n -2para se livrar das duas últimas linhas em branco, se não quiser.

Responder4

pythonsolução para este problema:

from collections import defaultdict
f = open("output.txt", "r")
d = defaultdict(list)
for line in f:
   line = line.strip()
   if line != '':
     arr = line.split(" ")
     d[arr[0]].append(arr[1])
print d.items()

Eu fiz hash usando a primeira coluna e coloquei em uma lista.

informação relacionada