Portanto, tenho um arquivo de texto do qual preciso extrair linhas específicas e contar o número de vezes que um número em uma coluna específica aparece. Eu tenho cerca de 100 desses arquivos. Posso fazer isso em pequenos passos, mas quero fazer isso usando bash/ksh:
foreach i *h3
sed '4p;55p;77q;d' $i >> output.txt
end
^^^^ isso apenas extrairá as linhas que preciso de cada arquivo h3
awk '{print $6}' output.txt | grep 'P2' | wc -l
^^^ isso apenas extrairá a coluna 6 de output.txt e contará o número de vezes que P2 aparece na coluna 6
Existe uma maneira de combinar tudo isso em um script bash/ksh?
Responder1
Se bem entendi:
- você deseja contar quantas vezes há "P2" em qualquer lugar dentro do 6º campo das linhas 4,55 e 77 de alguns arquivos (chamados *h3)?
Você poderia fazer isso com 1 awk:
awk '
( FNR==4 || FNR==55 || FNR==77 ) {
if ( $6 ~ "P2" ) { occurence++ }
}
END {
printf "There was: %d P2 ", occurence
printf " among the 6th field on lines 4,55 or 77 of the *h3 files\n"
}' *h3
Nota: mude $6 ~ "P2"
para $6 == "P2"
se quiser uma correspondência exata (em vez de grep, como você usou em seu próprio exemplo, para que também corresponda a: somethingP2otherthing
e suas variantes)
FNR = Número de registros do arquivo = número de linhas do arquivo atual (ou seja, começa novamente em 1 na primeira linha de cada arquivo) (Arquivo atual cujo nome também pode ser conhecido pela variável interna: FILENAME)
(NR = aqui não funcionaria, pois é o Número (total) ou Registros lidos desde o início (não desde o início do arquivo atual))
Responder2
Claro. Aqui está uma maneira
p2_count=0
for f in *h3; do
for ((n=1; n<=77; n++)); do
IFS= read -r line
if [[ $n == 4|55|77 ]]; then
echo "$line"
set -f
set -- $line
set +f
if [[ $6 == *P2* ]]; then
((p2_count++))
fi
fi
done < "$f"
done > output.txt
echo "saw P2 in 6th column $p2_count times"
Responder3
Ou usando umfestauma linha:
for i in *h3; do sed '4p;55p;77q;d' $i | awk '{print $6}' | grep 'P2'; done | wc -l
Ou mais curto usando grep -c
:
for i in *h3; do sed '4p;55p;77q;d' $i | awk '{print $6}'; done | grep -c 'P2'
Responder4
Normalmente, quando uma pergunta pergunta "como faço para processar vários arquivos de texto usandoferramentas específicasem um loop bash?", a resposta é, em parte, "Não use um loop bash, use (algumas ou todas) as próprias ferramentas". Às vezes, parte da resposta é até "Não use essas ferramentas, use-as".
O que você deseja pode ser feito awk
sozinho, sem necessidade de um shell loop. Ou sed
ou grep
ou wc
:
awk 'BEGIN {OFS="\t"}
FNR ~ /^(4|10|17)$/ && $6 ~ /P2/ {count++}
ENDFILE { print FILENAME, count; count=0 }' *h3
Observação:FIM DO ARQUIVO é específico do GNU awk
. Não funcionará com outras versões do awk
.
E esta versão também imprime um total cumulativo para todos os arquivos:
awk 'BEGIN {OFS="\t"}
FNR ~ /^(4|10|17)$/ && $6 ~ /P2/ {count++; total++}
ENDFILE { print FILENAME, count; count=0 }
END { print "---", total,"total" }' *h3
O END{}
bloco imprime o total e também faz uma tentativa grosseira de distinguir o total real de quaisquer arquivos que tenham o nome de arquivo "total". Isso é feito imprimindo ---
no primeiro campo, depois o total e depois a string total
no terceiro campo. Isso está longe de ser perfeito, mas é bom o suficiente em muitos casos. É melhor do que, tipo wc
, não tentar.