Как отформатировать цикл bash с помощью sed, awk, grep и wc

Как отформатировать цикл bash с помощью sed, awk, grep и wc

Итак, у меня есть текстовый файл, из которого мне нужно извлечь определенные строки и посчитать, сколько раз встречается число в определенном столбце. У меня около 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, вообще не пытаться.

Связанный контент