繁体   English   中英

将多个awk输出语句合并为一行

[英]Combining multiple awk output statements into one line

我正在处理一些ascii文件,每个文件有35列,并且行数可变。 我需要取两列之间的差(N + 1),然后将结果放入第36列的重复的ascii文件中。然后,我需要取另一列,并将其(按行)除以列36,并将结果放入第37列中相同的重复ascii文件中。

我过去做过类似的处理,但是通过为每个awk命令输出临时文件,读取每个连续的临时文件以最终创建最终的ascii文件。 然后,我将删除临时文件。 我希望有一种比必须创建一堆临时文件简单/快速的方法。

下面是一个初始的工作处理步骤,上面的awk命令需要遵循并适应于此。 此步骤从foo.txt中获取数据,删除标题,并仅处理包含特定但变化的字符串的行。

cat foo.txt | tail -n +2 | awk '$17 ~ /^[F][0-9][0-9][0-9]$/' >> foo_new.txt

对于不同的数据文件,还有另一个处理步骤,我还需要前面讨论的2个新列。 这只是将要保存的唯一文件名附加到新ascii文件中每一行的最后一列。 实际上,此命令处于循环中,输入文件不尽相同,但是我在这里已对其进行了简化。

cat foo.txt | tail -n +2 | awk -v fname="$fname" '{print $0 OFS fname;}' >> foo_new.txt

foo.txt文件之一的示例。

 20 0  5  F001
  4 2  3  F002
 12 4  8  F003
100 10 29 O001

下面是所需的示例foo_new.txt。 从awk请求的2列输出(后2列)。 在此示例中,第5列是第3列与第2列加1的差。第6列是第1列除以第5列的结果。

 20 0  5  F001 6  3.3
  4 2  3  F002 2  2.0
 12 4  8  F003 5  2.4

对于第二个示例foo_new.txt。 最后一列是fname的示例。 这些是在shell脚本中计算的,并传递给awk。 我不在乎第7列(fname)中的结果是在最后还是在第4列和第5列之间,只要它与其他awk语句相符即可。

 20 0  5  F001 6  3.3 C1
  4 2  3  F002 2  2.0 C2
 12 4  8  F003 5  2.4 C3

到目前为止最好的运气是,但是不幸的是,这产生的文件首先是原始输出,然后是其下面的附加输出。 我想将添加的输出附加为列(#5和#6)。

cat foo.txt | tail -n +2 | awk '$17 ~ /^[F][0-9][0-9][0-9]$/' >> foo_new.txt
cat foo_new.txt | awk '{print $4=$3-$2+1, $5=$1/($3-$2+1)}' >> foo_new.txt

我需要取两列之间的差(N + 1),然后将结果放入第36列的重复的ascii文件中。然后,我需要取另一列,并将其(按行)除以列36,并将结果放入第37列中相同的重复ascii文件中。

就是这样:

awk -vN=9 -vanother_column=10 '{ v36 = $N - $(N+1); print $0, v36, $another_column / v36 }' input_file.tsv

我猜您的文件有一些“标题” /特殊的“第一行”,因此,如果它是第一行,则保留它:

awk ... 'NR==1{print $0, "36_header", "37_header"} NR>1{ ... the script above ... }`

从您提供的示例脚本的前3列中,将N替换为2 ,将another_column替换为1 ,我们得到以下脚本:

# recreate input file
cat <<EOF |
20 0  5
4 2  3
12 4  8
100 10 29
EOF
tr -s ' ' | 
tr ' ' '\t'  > input_file.tsv


awk -vOFS=$'\t' -vIFS=$'\t' -vN=2 -vanother_column=1 '{ tmp = $(N + 1) - $N; print $0, tmp, $another_column / tmp }' input_file.tsv

它将输出:

20  0   5   5   4
4   2   3   1   4
12  4   8   4   3
100 10  29  19  5.26316

这样的脚本:

awk -vOFS=$'\t' -vIFS=$'\t' -vN=2 -vanother_column=1 '{ tmp = $(N + 1) - $N + 1; print $0, tmp, sprintf("%.1f", $another_column / tmp) }' input_file.tsv

我认为得到的输出更接近您想要的:

20  0   5   6   3.3
4   2   3   2   2.0
12  4   8   5   2.4
100 10  29  20  5.0

而且我猜想那(N+1)意思是“两列之间加1的差”。

考虑具有这样的标题行的输入文件data (紧密基于您的最小示例):

Col1 Col2 Col3 Col4
 20 0  5  F001
  4 2  3  F002
 12 4  8  F003
100 10 29 O001

您希望输出包含第5列,即$3 - $2 + 1的值(第$3 - $2 + 1列减去第2列加1),以及第6列,即第1列的值除以第5列(在第1位小数)输出),以及基于传递给脚本的变量fname的文件名,但每行都有唯一的值。 而且,您只需要第4列与F和3位数字匹配的行,并且您想跳过第一行。 全部可以直接用awk编写:

awk -v fname=C '
NR == 1                     { next }
$4 ~ /^F[0-9][0-9][0-9]$/   { c5 = $3 - $2 + 1
                              c6 = sprintf("%.1f", $1 / c5)
                              print $0, c5, c6, fname NR
                            }' data

您也可以将其写在一行上:

awk -v fname=C 'NR==1{next} $4~/^F[0-9][0-9][0-9]$/ { c5=$3-$2+1; print $0,c5,sprintf("%.1f",$1/c5), fname NR }' data

输出为:

 20 0  5  F001 6 3.3 C2
  4 2  3  F002 2 2.0 C3
 12 4  8  F003 5 2.4 C4

显然,您可以更改文件名,以通过分别使用counter++++counter代替print语句中的NR来使计数器从0或1开始,并且可以将其用前导零或sprintf()格式化。 sprintf()再次。 如果要删除每个文件的第一行而不是仅删除第一个文件,请将NR == 1条件改为FNR == 1

注意,这不需要cat foo.txt | tail -n +2提供的预处理cat foo.txt | tail -n +2 cat foo.txt | tail -n +2

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM