Итак, у меня есть текстовый файл, из которого мне нужно извлечь определенные строки и посчитать, сколько раз встречается число в определенном столбце. У меня около 100 таких файлов. Я могу сделать это небольшими шагами, но хочу сделать это с помощью bash/ksh:
foreach i *h3
sed '4p;55p;77q;d' $i >> output.txt
end
^^^^это просто извлечет нужные мне строки из каждого файла h3
awk '{print $6}' output.txt | grep 'P2' | wc -l
^^^это просто извлечет столбец 6 из output.txt и подсчитает количество раз, когда P2 появляется в столбце 6
Есть ли способ объединить все это в скрипт bash/ksh?
решение1
Если я правильно понял:
- Вы хотите посчитать, сколько раз встречается «P2» в 6-м поле строк 4, 55 и 77 нескольких файлов (с именем *h3)?
Это можно сделать с помощью 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
Примечание: измените $6 ~ "P2"
на , $6 == "P2"
если вам нужно точное совпадение (вместо grep, как вы использовали в своем примере, чтобы оно также соответствовало: somethingP2otherthing
и его вариантам)
FNR = Количество записей в файле = количество строк в текущем файле (т.е. начинается снова с 1 в первой строке каждого файла) (Текущий файл, имя которого также может быть известно по внутренней переменной: FILENAME)
(NR = здесь не сработает, так как это (общее) число записей, прочитанных с начала (а не с начала текущего файла))
решение2
Конечно. Вот один из способов
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"
решение3
Или с помощьюБашодин лайнер:
for i in *h3; do sed '4p;55p;77q;d' $i | awk '{print $6}' | grep 'P2'; done | wc -l
Или короче, используя grep -c
:
for i in *h3; do sed '4p;55p;77q;d' $i | awk '{print $6}'; done | grep -c 'P2'
решение4
Обычно, когда задают вопрос «как мне обработать кучу текстовых файлов с помощьюконкретный(е) инструмент(ы)в цикле bash?», ответ, отчасти, таков: «Не используйте цикл bash, используйте (некоторые или все) сами инструменты». Иногда ответ даже может быть таким: «Не используйте эти инструменты, используйте вместо этого это».
То, что вам нужно, можно сделать и в awk
одиночку, нет необходимости в цикле оболочки. Или sed
или grep
или wc
:
awk 'BEGIN {OFS="\t"}
FNR ~ /^(4|10|17)$/ && $6 ~ /P2/ {count++}
ENDFILE { print FILENAME, count; count=0 }' *h3
Примечание:КОНЕЦФАЙЛА специфичен для GNU awk
. Он не будет работать с другими версиями awk
.
А эта версия также выводит общую сумму по всем файлам:
awk 'BEGIN {OFS="\t"}
FNR ~ /^(4|10|17)$/ && $6 ~ /P2/ {count++; total++}
ENDFILE { print FILENAME, count; count=0 }
END { print "---", total,"total" }' *h3
Блок END{}
выводит итог, а также делает грубую попытку отличить фактический итог от любых файлов, которые случайно имеют имя файла "total". Он делает это, печатая ---
в первом поле, затем итог, а затем строку total
в третьем поле. Это далеко от совершенства, но во многих случаях достаточно хорошо. Это лучше, чем, например wc
, вообще не пытаться.