比较两个文本文件打印结果在新标题中

时间:2014-12-19 18:39:07

标签: perl awk ksh

好的,我会重新更新

我有2个文件--File1.txt,File2.txt

File1是基本模板

File2具有状态结果

FILE1.TXT

N1,N2,N3,N4,N5,N6
XX,ZZ,XC,EE,RR,BB
XC,CF,FG,RG,GH,GH

FILE2.TXT

DF,GH,MH,FR,FG,GH,NA
XX,ZZ,XC,EE,RR,BB,OK

下面的命令会比较两个文件中的第1列(如果匹配),然后从file2中的第7个单元格中检索该值,并将file1.txt作为最后一列附加到新标题中。

如果没有找到NA则更新。

使用的命令:

awk -F  '
  FNR==NR { a[$1]=$7; next }
  FNR==1  { print $0; len=length($0); next }
  {
    printf $0
    cont=(($1 in a) ? ","a[$1] : ",NA")
    for ( i=length($0)+1; i<=len-length(cont); i++)
      printf " " 
    print cont
  }
'  file2.txt file1.txt > tmp &&

第1天 - 在命令

之后运行
N1,N2,N3,N4,N5,N6,D1
XX,ZZ,XC,EE,RR,BB,OK
XC,CF,FG,RG,GH,GH,NA

第2天 - 在命令

之后运行
N1,N2,N3,N4,N5,N6,D1,D2
XX,ZZ,XC,EE,RR,BB,OK,OK
XC,CF,FG,RG,GH,GH,NA,NA

在第3天,我在File1底部插入了一个新行

N1,N2,N3,N4,N5,N6,D1,D2
XX,ZZ,XC,EE,RR,BB,OK,OK
XC,CF,FG,RG,GH,GH,NA,NA
DM,LC,VF,GR,GH,ES

现在当我在Day3上运行命令时,我需要像这样的输出

N1,N2,N3,N4,N5,N6,D1,D2,D3
XX,ZZ,XC,EE,RR,BB,OK,OK,OK
XC,CF,FG,RG,GH,GH,NA,NA,NA
DM,LC,VF,GR,GH,ES,,,NA

1 个答案:

答案 0 :(得分:1)

这个awk脚本似乎可以完成这项任务:

awk -F, '
BEGIN   { OFS = FS }
FNR==NR { a[$1] = $7; next }
FNR==1  { n1 = n = NF + 1; $n = "D" (n-6); print; next }
        { $n1 = ($1 in a) ? a[$1] : "NA"; print }
' file2.txt file1.txt

OFS是输出字段分隔符; FS是(输入)字段分隔符。两者都通过,选项设置为-F,FS,通过赋值设置为OFS。这样可以轻松获得输出中正确数量的字段。 awk没有运算符的字符串连接,以"D" (n-6)为例,有点奇怪;你已经习惯了它,直到某一点,但它仍然看起来有点奇怪。

实施例

示例运行使用具有概要的程序ow

ow file cmd …args…

它通过cmd …args…写入临时文件来保留文件的内容,如果命令成功(退出状态0)并且输出不为空,则它会保留原始文件的副本,忽略许多信号,然后将临时输出复制到原始信号上并清理。它非常有用 - 底部的代码。这就是我测试的方式。显然,我可以使用tmp=$(mktemp tmp.XXXXXX); awk … file1.txt > $tmp; mv $tmp file1.txt代替,或者沿着那些方向使用。但是,由于我有ow,我使用它。

$ cat file1.txt
N1,N2,N3,N4,N5,N6
XX,ZZ,XC,EE,RR,BB
XC,CF,FG,RG,GH,GH
$ ow file1.txt awk -F, '
> BEGIN   { OFS = FS }
> FNR==NR { a[$1] = $7; next }
> FNR==1  { n1 = n = NF + 1; $n = "D" (n-6); print; next }
>         { $n1 = ($1 in a) ? a[$1] : "NA"; print }
> ' file2.txt file1.txt
$ cat file1.txt
N1,N2,N3,N4,N5,N6,D1
XX,ZZ,XC,EE,RR,BB,OK
XC,CF,FG,RG,GH,GH,NA
$ ow file1.txt awk -F, '
> BEGIN   { OFS = FS }
> FNR==NR { a[$1] = $7; next }
> FNR==1  { n1 = n = NF + 1; $n = "D" (n-6); print; next }
>         { $n1 = ($1 in a) ? a[$1] : "NA"; print }
> ' file2.txt file1.txt
$ cat file1.txt
N1,N2,N3,N4,N5,N6,D1,D2
XX,ZZ,XC,EE,RR,BB,OK,OK
XC,CF,FG,RG,GH,GH,NA,NA
$ echo DM,LC,VF,GR,GH,ES >> file1.txt
$ ow file1.txt awk -F, '
> BEGIN   { OFS = FS }
> FNR==NR { a[$1] = $7; next }
> FNR==1  { n1 = n = NF + 1; $n = "D" (n-6); print; next }
>         { $n1 = ($1 in a) ? a[$1] : "NA"; print }
> ' file2.txt file1.txt
$ cat file1.txt
N1,N2,N3,N4,N5,N6,D1,D2,D3
XX,ZZ,XC,EE,RR,BB,OK,OK,OK
XC,CF,FG,RG,GH,GH,NA,NA,NA
DM,LC,VF,GR,GH,ES,,,NA
$

请注意,当您分配给$ii大于NF时,NF会增加,并且所有丢失的字段都会创建为空字段。

此脚本的第一个工作版本具有更复杂的逻辑,循环创建空字段,但由于awk将自动执行此操作,因此脚本大大简化。您经常会发现,通过一些时间和关注,可以简化和清理初始解决方案。

然而,指出这段代码非常信任可能也是相关的。它不能确保file2.txt中恰好有7个字段。它不会检查file1.txt中的每一行是否与文件中的第一行具有相同数量的字段,或者恰好是6个字段。如果您提供了棘手的数据,那么您将获得棘手的数据 - 历史悠久的GIGO原则:Garbage In, Garbage Out

ow

:   "@(#)$Id: ow.sh,v 1.6 2005/06/30 18:14:08 jleffler Exp $"
#
#   Overwrite file
#   From: The UNIX Programming Environment by Kernighan and Pike
#   Amended: remove PATH setting; handle file names with blanks.

case $# in
0|1)    echo "Usage: $0 file command [arguments]" 1>&2
    exit 1;;
esac

file="$1"
shift
new=${TMPDIR:-/tmp}/ovrwr.$$.1
old=${TMPDIR:-/tmp}/ovrwr.$$.2

trap "rm -f '$new' '$old' ; exit 1" 0 1 2 15

if "$@" >"$new"
then
    cp "$file" "$old"
    trap "" 1 2 15
    cp "$new" "$file"
    rm -f "$new" "$old"
    trap 0
    exit 0
else
    echo "$0: $1 failed - $file unchanged" 1>&2
    rm -f "$new" "$old"
    trap 0
    exit 1
fi

将日期而不是Dn添加到标题

  

awk是否可以在标题中打印日期而不是 D1

如果您想添加当前日期,则有两个主要选项。一,使用GNU gawk(通常也安装为awk),然后time functions使其变得简单。如果失败,awk -v date=$(date +'%Y-%m-%d') -F, …将系统命令日期格式设为值,并将其作为变量日期传递给awk脚本,然后您可以将其打印到所需的位置。如果您希望传入任意日期,则第二种机制是要使用的机制。

awk -F, -v date=$(date +'%Y-%m-%d') '
BEGIN   { OFS = FS }
FNR==NR { a[$1] = $7; next }
FNR==1  { n1 = n = NF + 1; $n = date; print; next }
        { $n1 = ($1 in a) ? a[$1] : "NA"; print }
' file2.txt file1.txt

这迫使今天的日期进入命令。您也可以前瞻性或回顾性地做事,例如:

tmp=$(mktemp coladd.XXXXXXXXX)
trap "rm -f $tmp; exit 1" 0 1 2 3 13 15

for dd in $(seq 1 31)
do
    awk -F, -v date="2014-12-$dd" '
    BEGIN   { OFS = FS }
    FNR==NR { a[$1] = $7; next }
    FNR==1  { n1 = n = NF + 1; $n = date; print; next }
            { $n1 = ($1 in a) ? a[$1] : "NA"; print }
    ' file2.txt file1.txt > $tmp
    mv $tmp file1.txt
done

鉴于这种额外的灵活性,我建议使用外部定义的日期而不是GNU的内部日期操作函数,但是YMMV