Unix 如何将comm命令的输出保存到3个单独的文件中?

Unix 如何将comm命令的输出保存到3个单独的文件中?,unix,sed,comm,Unix,Sed,Comm,该问题有一个建议使用comm命令执行任务的提示: comm -12 1.sorted.txt 2.sorted.txt 这显示了两个文件共有的行(-1抑制仅在第一个文件中的行,-2抑制仅在第二个文件中的行,只保留两个文件共有的行作为输出)。正如文件名所示,输入文件必须按顺序排序 在回答该问题时,询问: 如何将输出放在不同的文件中 为了澄清,我问: 如果您只希望文件1中的行在一个文件中,文件2中的行在另一个文件中,两个文件中的行在第三个文件中,那么(前提是文件中的所有行都不以选项卡开头),您可以

该问题有一个建议使用
comm
命令执行任务的提示:

comm -12 1.sorted.txt 2.sorted.txt
这显示了两个文件共有的行(
-1
抑制仅在第一个文件中的行,
-2
抑制仅在第二个文件中的行,只保留两个文件共有的行作为输出)。正如文件名所示,输入文件必须按顺序排序

在回答该问题时,询问:

如何将输出放在不同的文件中

为了澄清,我问:

如果您只希望文件1中的行在一个文件中,文件2中的行在另一个文件中,两个文件中的行在第三个文件中,那么(前提是文件中的所有行都不以选项卡开头),您可以使用
sed
将输出拆分为三个文件

已确认的用户BAP:

这正是我所要求的。你能举个例子吗


答案相对冗长,会破坏另一个问题答案的简单性(用大量信息淹没它),因此,我在这里单独提出了这个问题,并给出了答案。

使用
sed
的基本解决方案依赖于这样一个事实:
comm
只输出第一个文件中没有前缀的行;它输出仅在第二个文件中找到的具有单个选项卡的行;它输出两个文件中的行,并带有两个选项卡

它还依赖于的
w
命令来写入文件

给定文件
1.sorted.txt
包含:

1.line-1
1.line-2
1.line-4
1.line-6
2.line-2
3.line-5
1.line-3
2.line-1
2.line-2
2.line-4
2.line-6
3.line-5
/^\t\t/ {
    s///
    w file.3
    d
}
/^\t/ {
    s///
    w file.2
    d
}
/^[^\t]/ {
    w file.1
    d
}
文件
2.sorted.txt
包含:

1.line-1
1.line-2
1.line-4
1.line-6
2.line-2
3.line-5
1.line-3
2.line-1
2.line-2
2.line-4
2.line-6
3.line-5
/^\t\t/ {
    s///
    w file.3
    d
}
/^\t/ {
    s///
    w file.2
    d
}
/^[^\t]/ {
    w file.1
    d
}
comm 1.sorted.txt 2.sorted.txt
的基本输出为:

1.line-1
1.line-2
        1.line-3
1.line-4
1.line-6
        2.line-1
                2.line-2
        2.line-4
        2.line-6
                3.line-5
给定一个包含以下内容的文件
script.sed

1.line-1
1.line-2
1.line-4
1.line-6
2.line-2
3.line-5
1.line-3
2.line-1
2.line-2
2.line-4
2.line-6
3.line-5
/^\t\t/ {
    s///
    w file.3
    d
}
/^\t/ {
    s///
    w file.2
    d
}
/^[^\t]/ {
    w file.1
    d
}
您可以运行如下所示的命令,并获得如下所示的所需输出:

$ comm 1.sorted.txt 2.sorted.txt | sed -f script.sed
$ cat file.1
1.line-1
1.line-2
1.line-4
1.line-6
$ cat file.2
1.line-3
2.line-1
2.line-4
2.line-6
$ cat file.3
2.line-2
3.line-5
$
该脚本的工作原理是:

  • 匹配以2个制表符开头的行,删除制表符,将该行写入
    文件.3
    ,并删除该行(因此忽略脚本的其余部分)
  • 匹配以1个制表符开头的行,删除制表符,将该行写入
    文件.2
    ,并删除该行(因此忽略脚本的其余部分)
  • 匹配不以制表符开头的行,将该行写入
    文件.1
    ,然后删除该行
  • 第3步中的匹配和删除操作更多地是为了对称性;可以省略它们(只留下
    w file.1
    ),这个脚本也可以使用。但是,请参见下面的
    script3.sed
    ,了解保持对称性的进一步理由

    如前所述,这需要GNU
    sed
    ;BSD
    sed
    无法识别
    \t
    转义。显然,文件可以用实际的制表符代替
    \t
    符号来编写,然后BSD
    sed
    就可以使用脚本了

    可以让它在命令行上工作,但它很灵活(这是出于礼貌)。使用Bash,您可以编写:

    $ comm 1.sorted.txt 2.sorted.txt |
    > sed -e $'/^\t\t/  { s///\n w file.3\n d\n }' \
    >     -e $'/^\t/    { s///\n w file.2\n d\n }' \
    >     -e $'/^[^\t]/ {        w file.1\n d\n }'
    $
    
    它在一个单独的
    -e
    选项中写入
    script.sed的三个“段落”中的每一个。
    w
    命令很繁琐;在脚本的同一行上,它需要文件名,并且只需要文件名,因此在脚本中的文件名之后使用
    \n
    。有大量的空间可以消除,但对称性更清楚的布局显示。使用
    -f script.sed
    文件可能更简单-这当然是一种值得了解的技术,因为它可以避免
    sed
    脚本必须在单引号、双引号和反引号上操作时出现问题,这使得在Bash命令行上编写脚本变得困难

    最后,如果这两个文件可以包含以制表符开头的行,那么这种技术需要更多的蛮力才能工作。一种变体解决方案利用Bash在文件中的行之前添加前缀,然后后处理
    sed
    脚本在写入输出文件之前删除前缀

    script3.sed
    (用最多8个空格替换制表符)-注意,这一次第三段中需要替换
    s//
    d
    仍然是可选的,但也可以包括在内):

    和命令行:

    $ comm <(sed 's/^/X/' 1.sorted.txt) <(sed 's/^/X/' 2.sorted.txt) |
    > sed -f script3.sed
    $
    

    $commcomm+awk解决方案:

    复杂的示例文件:

    1.txt

    1. line-1 with spaces (                 |   | here
    1.line-2
    1.line-4    with tabs > 
     1.line-6
    2.line-2
            3.line-5 (tabs)
    
    1.line-3
      2.line-1 with spaces
    2.line-2
    2.line-4
        2.line-6 with tabs
            3.line-5 (tabs)
    

    2.txt

    1. line-1 with spaces (                 |   | here
    1.line-2
    1.line-4    with tabs > 
     1.line-6
    2.line-2
            3.line-5 (tabs)
    
    1.line-3
      2.line-1 with spaces
    2.line-2
    2.line-4
        2.line-6 with tabs
            3.line-5 (tabs)
    

    工作:

    comm -12 1.txt 2.txt > file-common 
    awk 'NR==FNR{ a[$0];next }!($0 in a){ print $0 > "file"ARGIND-1 }' file-common 1.txt 2.txt
    
    • comm-12 1.txt 2.txt>文件公共
      -将公共行保存到
      文件公共
      文件

    • awk…
      -将
      1.txt
      2.txt
      特有的行分别打印到文件
      file1
      file2


    查看结果:

    head file*
    ==> file1 <==
    1. line-1 with spaces (                 |   | here
    1.line-2
    1.line-4    with tabs > 
     1.line-6
    
    ==> file2 <==
    1.line-3
      2.line-1 with spaces
    2.line-4
        2.line-6 with tabs
    
    ==> file-common <==
    2.line-2
            3.line-5 (tabs)
    
    头文件*
    ==>文件1
    1.6行
    
    ==>file2文件的共同贡献。但愿我们还有这样的文件。为BSD sed用户添加一件事。如果您正好在FreeBSD中,则您的
    /bin/sh
    而不是bash的Almquist shell包含类似于bash的C样式引用。如果我在开始处(以及单词之间)添加多个制表符/空格,这似乎不起作用到lines@RomanPerekhrest:您是说使用
    script3.sed
    的变量和流程替换在数据行中有前导制表符或多个空格或制表符时不起作用吗?如果是这样,我想看看样本数据。请通过电子邮件发送给我-查看我的个人资料。一个可能的问题是,
    sort
    comm
    对于数据按顺序排序意味着什么没有达成一致意见。您可能需要在环境中设置
    LANG=C
    ,才能使其正常工作。@JonathanLeffler,请参见此屏幕截图。在顶部窗口-您的sed脚本内容中,在底部窗口-区域设置中,2个文件内容和最终命令结果请注意