Imprimindo uma nova linha usando o awk

1

Eu tenho uma carga de arquivos em que preciso remover linhas específicas e, em seguida, colocar os dados que levei em uma planilha. Um exemplo seria meu arquivo mostra:

Name: w

Age: x

Height: y

Weight: z

Eu só quero a idade, altura e peso, então eu corri primeiro:

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

Por causa do número de arquivos, minha saída agora parece

Age 1

Height 1

Weight 1

Age 2

Height 2

Weight 2

etc...

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

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

Mas se apenas imprime como o arquivo de saída original. Como faço para alterá-lo assim depois de ter feito todos os Age, então ele encontra os de altura?

EDITAR:

Minha saída desejada é para o arquivo ser

Age 1

Age 2

Height 1

Height 2

Weight 1

Weight 2

etc..

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

    
por Kaish 24.06.2014 / 12:03

5 respostas

1

O awk só executa o arquivo uma vez por padrão, executando todos os blocos em ordem, e é por isso que ele fornece a saída que você obteve. Você pode obter o comportamento desejado usando uma matriz para salvar as linhas como você vai, enquanto ainda apenas o processamento do arquivo 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 em filter.awk e 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 ages e heights e salvar cada linha correspondente neles. AgeIndex detém até que ponto a matriz está fazendo. No final, estamos imprimindo todas as linhas que salvamos (e uma nova linha extra como você quer), primeiro todas as idades, depois todas as alturas.

As matrizes armazenarão o arquivo inteiro na memória até o final, portanto, se o seu arquivo for particularmente grande, você terá que trocar o uso da memória pelo tempo de passar pelo arquivo inteiro mais de uma vez. Neste ponto, é essencialmente o mesmo que um programa em qualquer outro idioma - se você não tem nenhum motivo particular para usar o awk, você pode preferir outro idioma. Para ser honesto, acho que recomendo isso - o awk não está te comprando muito aqui.

    
por 24.06.2014 / 12:12
1

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
    
por 24.06.2014 / 12:14
0

python soluçã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 usei hash usando a primeira coluna e coloquei em uma lista. espero que isso ajude alguém :)

    
por 24.06.2014 / 12:21
0

Suponho que as linhas em branco não fazem parte do seu arquivo real ou que, pelo menos, você não se importa 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

Os itens acima pesquisarão Age ou Height ou Weight em todos os arquivos cujo nome termina em txt no diretório atual ( *txt ). O -w significa "corresponde somente palavras inteiras" (para que Age não corresponda Ageing por exemplo), o -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 -E ativa expressões regulares estendidas, o que nos dá | para OR.

OBSERVAÇÃO: Se, por algum motivo, você realmente quiser a linha em branco extra entre cada entrada (que não é o que seu comando grep produziria), você poderá 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

Em qualquer caso, mesmo que sort não o corte para você, eu faria esse tipo de coisa em Perl, não em awk (isto é, supondo que você queira as linhas extras em branco que, mais uma vez, você provavelmente não):

$ 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 através de head -n -2 para se livrar das duas últimas linhas em branco se você não quiser.

    
por 24.06.2014 / 14:13
0

Você pode usar sort :

grep -E 'Age|Height|Weight' [input file] | sort > output.txt
    
por 15.04.2016 / 10:58

Tags