
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.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
salvar cada linha correspondente neles à medida que avançamos. AgeIndex
manté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 Age
ou Height
ou Weight
em todos os arquivos cujo nome termina txt
no diretório atual ( *txt
). O -w
meio "corresponder apenas palavras inteiras" (para que Age
não corresponda, Ageing
por 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 -E
habilita 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 grep
comando 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 sort
nã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 -2
para se livrar das duas últimas linhas em branco, se não quiser.
Responder4
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 fiz hash usando a primeira coluna e coloquei em uma lista.