我有一个像这样的简单命令
grep 'X' results.dat | awk '{print $NF}' > Y.dat
我想循环这个命令Xs从第 1 栏以及相应的伊苏从第2栏同一文件的例如。名称
NAMES 文件的格式为
C11-C12 p01
C13-C14-C17 P02
etc ..
所以循环中的前两个步骤应该是这样的
grep 'C11-C12' results.dat | awk '{print $NF}' > p01.dat
grep 'C13-C14-C17' results.dat | awk '{print $NF}' > p02.dat
答案1
不需要在 shell 中循环的解决方案:
awk 'pass==1 { Xpatt[NR] = $1; Yfile[NR] = $2 ".dat"; printf "" > Yfile[NR] }
pass==2 {
for (i in Xpatt) {
if ($0 ~ Xpatt[i]) print $NF > Yfile[i]
}
}' pass=1 NAMES pass=2 results.dat
首先,
awk
允许您在程序之后将变量赋值指定为命令行参数,与文件名混合,而不是使用-v
.它们在命令行中的位置所建议的处理序列中的点处执行。所以,在上面的命令中,pass
设置为 1,- 文件
NAMES
已处理, pass
设置为 2,然后- 文件
results.dat
已处理。
pass=1
我想我可以用 a-v
或 在一个块中设置BEGIN
。我使用
pass
变量来告诉我正在读取哪个文件。这通常是通过NR
与进行比较来完成的FNR
,但如果文件为空,则可能会导致错误指示。(严格来说,我认为这个脚本应该检查两个文件是否为空,因为在这种情况下,没有任何工作要做。)
- 当
pass==1
(我们正在读取文件)时,保存该文件第 1 列和第 2 列(和)NAMES
中的 X 和 Y 值(图案和文件名) 。创建输出文件 ( ),因为如果我们不在此处执行此操作,我们将不会获得文件中不存在的模式的(空)输出文件。 (如果您同意,请省略该声明。)$1
$2
Yfile[NR]
results.dat
printf
- 当
pass==2
(我们正在读取results.dat
文件)时,循环遍历文件中的模式NAMES
并将与模式匹配的每一行的最后一个单词打印到相应的文件中 - 即相当于 OP 的grep X … | awk '{print $NF}' > Y.dat
命令。
答案2
重击解决方案:
while read X Y remainder || [[ -n ${Y} ]]; do
awk -v X="$X" '$0 ~ $X {print $NF}' results.dat > "$Y".dat
done < NAMES
通常,
while IFS="q" read X Y remainder; do ...; done < NAMES
将迭代 中的行NAMES
。它将根据IFS
(内部字段分隔符)的值分隔每行中的值。在此示例中,IFS
设置为字母q
。IFS
默认为空白(空格字符、制表符或换行符)。第一个字段分配给变量X
,第二个字段分配给Y
,该行的其余部分分配给remainder
。也可以看看:将文件中的列读取到单独的变量中(Unix.SE)。
在上面的解决方案中,
IFS
没有指定,因为我认为您的字段已经以空格分隔。注意:如果文件中的字段
NAMES
包含反斜杠,则需要使用 来read -r
防止read
将反斜杠解释为转义序列。该
... remainder || [[ -n ${remainder} ]]
部分处理两件事:任何额外的字段(如果有)都存储在remainder
;并处理输入文件的最后一行不以换行符结尾的情况\n
(read
遇到 EOF 时返回非零退出代码)。也可以看看:逐行读取文件并将值分配给变量(所以)。
grep
完全消除:awk -v X="$X" '$0 ~ $X {print $NF}' results.dat > "$Y".dat
.该-v
选项awk
定义可在脚本中使用的变量awk
。