因此,我有一个文本文件,需要从中提取特定行并计算特定列中数字出现的次数。我有大约 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
^^^这只会从output.txt中提取第6列并计算P2出现在第6列中的次数
有没有办法可以将所有这些合并到 bash/ksh 脚本中?
答案1
如果我理解正确的话:
- 您想计算几个文件(名为 *h3)的第 4,55 行和 77 行的第 6 个字段中的任何位置有多少次“P2”?
您可以使用 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
,不需要 shell 循环。或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
根本不尝试要好。