用定界符三重管道符号“ |||”逐行合并两个文件


14

我有两个并行文件,两种语言的行数相同,并计划使用定界符逐行合并这两个文件|||。例如,两个文件如下:

档案A:

1Mo 1,1 I love you.
1Mo 1,2 I like you.
Hi 1,3 I am hungry.
Hi 1,4 I am foolish.

档案B:

1Mo 1,1 Ich liebe dich.
1Mo 1,2 Ich mag dich.
Hi 1,3 Ich habe Durst.
Hi 1,4 Ich bin neu.

预期的输出是这样的:

1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. ||| Hi 1,4 Ich bin neu.

我尝试了以下paste命令:

paste -d "|||" fileA fileB

但是返回的输出仅包含一个管道,例如:

1Mo 1,1 I love you. |1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. |1Mo 1,2 Ich mag dich.

有什么办法可以用肚管分开每对线|||吗?


8
paste -d '|||' fileA - - fileB < /dev/null
斯特凡Chazelas

5
offtopic,但您的翻译不正确;)“ Ich habe Durst” =我很卑鄙,“ Ich bin neu” =我是新来的...不一定意味着您很愚蠢。...以防万一您实际上正在学习德语...
dave_alcarin 2015年

@StéphaneChazelasThx,但我的输出仍然只包含一个管道...
皱眉2015年

@dave_alcarin Dank sehr!
皱眉

Answers:


20

使用POSIX粘贴

:|paste -d ' ||| ' fileA - - - - fileB

paste将连接所有输入文件的相应行。在这里,我们有六个文件,fileA从标准的四个空文件-,和fileB

定界符列表包括一个空格,三个管道和一个按顺序使用的空格,将paste循环使用。

对于六个文件的第一行,fileA将与第一个虚拟文件(这没什么,要感谢no-op:运算符)串联在一起,产生line1-fileA<space>

第一个虚拟文件将通过管道与第二个虚拟文件连接,生成line1-fileA |,然后第二个虚拟文件与第三个虚拟文件生成line1-fileA ||,第四个虚拟文件与第四个虚拟文件生成line1-fileA |||

然后用第四个伪文件fileB产生line1-fileA ||| line1-fileB

所有行都会重复执行这些步骤,从而为您提供预期的结果。


使用的目的:|是减少打字的次数,主要用于交互式外壳程序中。在脚本中,应使用:

</dev/null paste -d ' ||| ' fileA - - - - fileB

以防止生成子外壳。


1
为+1 :|</dev/null
cas的

4
...和+1可以从标准输入中聪明地使用4个伪文件- - - -,但下一次您甚至可以写几行来解​​释:)
Hastur

Thx,但我仍然可以通过一根管道获得输出...
皱眉

@hui,您是否完全按照给定的命令(包括所有破折号和空格字符)运行命令?您的操作系统是什么?
斯特凡Chazelas

:|paste -d '|' fileA - - fileB给出没有空格分隔符的更正确版本。
2015年

7

嗯,这不使用sed,awk或grep,但是您可以在bash中轻松完成它。该命令是:

(while IFS= read -r a <&3 && IFS= read -r b <&4; do echo "$a ||| $b"; done) 3<fileA 4<fileB

粘贴的问题是分隔符是单个字符。您也可以插入一个字符,并使用sed对其进行转换,但是如果该字符已经出现在输入文件中,则将容易出错。


2
如果行包含任何反斜杠字符或以破折号开头,则您的解决方案将不起作用。您想IFS=在每个之前使用read。您可以轻松地做到这一点paste。请参阅我的答案,以及答案,以了解为什么应避免while在shell脚本中使用循环。
cuonglm

它适用于我的文件。多谢!!!
皱眉

5

awk(GNU)版本

awk '{printf ("%s ||| ", $0); getline < "fileB"; print $0 }' fileA

使用中的getline命令,如果您从指定文件中设置下一个输入记录,则可以从下一个输入记录awk设置$0(列的所有变量)。getline < "filename"$0

getline <“ file”从文件的下一个记录设置$ 0; 设置NF。


为什么您的尝试没有按照您的预期进行?从man paste我们可以阅读

-d, --delimiters=LIST
     reuse characters from LIST instead of TABs

但是它对每列使用一个分隔符

所以命令
paste -d '|*|*' fileA fileB fileA fileB给我几行

Hi 1,3 I am hungry.|Hi 1,3 Ich habe Durst.*Hi 1,3 I am hungry.|Hi 1,3 Ich...
Hi 1,4 I am foolish.|Hi 1,4 Ich bin neu.*Hi 1,4 I am foolish.|Hi 1,4 Ich...


一个sed解决方案,我建议,以避免哪怕接近原来的尝试,因为它的补丁所获得的行为,你的初衷:

 paste -d '|' fileA fileB | sed 's/|/|||/g'

为避免|使用新模式替换每个模式|||但必须假定|数据中不存在管道符号(),否则必须处理特殊情况并使代码更复杂以避免副作用。


具有Here String [ 1 ]构造的变体<<<

 paste -d ' ||| ' fileA - - - - fileB  <<< ''

您使用-d ' ||| '(space,|,|,|,space)设置了5个定界符,并使用4个虚拟文件(- - - -)来从空字符串中获取数据''


在GNU Awk 4.0.1上测试,粘贴(GNU coreutils)8.21和sed(GNU sed)4.2.2


Thx,awk命令有效!
皱眉

1
别客气。更新了答案,添加了一个sed示例,以避免(:-))和更多注释。
Hastur

4

如果要避免圆形定界符和伪文件的魔幻和戏剧性,可以在粘贴它们之前将定界符附加到一个文件中:

paste <(sed 's/$/ |||/' filea) fileb

1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. |||    Hi 1,4 Ich bin neu.

为简单起见,我喜欢这个。我相信您的意思是“前置”,而不是“附加”。Checkout Hastur的awk版本对此的awk答案。
2015年

您应该将进程替换更改为管道,这样就不会限制支持它的shell数量。
cuonglm 2015年

@Wildcard是的,在前面,但是我将其重写以追加到Filea。我认为awk对此有点矫kill过正。
snth

@cuonglm是正确的,但是为了清晰起见,我想避免使用管道。我觉得一个管道将使其开始看起来像假的文件,但你是正确的
snth

0

您也可以通过这种方式在python中进行操作。

lines1 = [ line.rstrip() for line in open("file1") ]
lines2 = [ line.rstrip() for line in open("file2") ]
for i in xrange((len(lines1))): print lines1[i] + " ||| " + lines2[i]
... 
1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. ||| Hi 1,4 Ich bin neu.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.