Hợp nhất hai tập tin theo từng dòng với ký hiệu đường ống ba dấu phân cách | |


14

Tôi có hai tệp song song có cùng số dòng trong hai ngôn ngữ và dự định hợp nhất hai tệp này theo dòng với dấu phân cách |||. Ví dụ, hai tệp như sau:

Tập tin:

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

Tệp 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.

Sản lượng dự kiến ​​là như thế này:

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.

Tôi đã thử pastelệnh như:

paste -d "|||" fileA fileB

Nhưng đầu ra được trả lại chỉ chứa một ống như:

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

Có cách nào để tách từng cặp đường bằng ống tripe |||không?


8
paste -d '|||' fileA - - fileB < /dev/null
Stéphane Chazelas

5
không chính thức, nhưng bản dịch của bạn không chính xác;) "Ich habe Durst" = Tôi là người này, "Ich bin neu" = Tôi là người mới ... không nhất thiết có nghĩa là bạn dại dột. ... chỉ trong trường hợp bạn thực sự học tiếng Đức ...
dave_alcarin

@ StéphaneChazelas Thx, nhưng đầu ra của tôi vẫn chỉ chứa một ống ...
Nhíu mày

@dave_alcarin Dank sehr!
Nhíu mày

Câu trả lời:


20

Với dán POSIX :

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

pastesẽ nối các dòng tương ứng của tất cả các tệp đầu vào. Ở đây chúng tôi có sáu tệp, fileAbốn tệp giả từ tiêu chuẩn trong -fileB.

Danh sách các dấu phân cách bao gồm một khoảng trắng, ba đường ống và một khoảng trắng theo thứ tự đó sẽ được sử dụng theo pastevòng tròn.

Đối với dòng đầu tiên gồm sáu tệp, fileAsẽ được nối với tệp giả đầu tiên (không có gì, cảm ơn toán tử no-op :), sản xuất line1-fileA<space>.

Tệp giả thứ nhất sẽ được nối với ống thứ hai bằng một đường ống, tạo ra line1-fileA |, sau đó tệp giả thứ hai với tệp giả thứ ba, tạo ra line1-fileA ||, tệp giả thứ ba với tệp giả thứ ba, tạo ra line1-fileA |||.

Và các tập tin giả thứ tư với fileB, sản xuất line1-fileA ||| line1-fileB.

Những bước đó sẽ được lặp lại cho tất cả các dòng, cung cấp cho bạn kết quả mong đợi.


Việc sử dụng :|là để gõ ít hơn và chủ yếu sử dụng trong vỏ tương tác. Trong một tập lệnh, bạn nên sử dụng:

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

để ngăn chặn một subshell khỏi sinh sản.


1
+1 cho :|. thay thế thông minh cho</dev/null
cas

4
... và +1 cho việc sử dụng thông minh 4 tệp giả từ đầu vào tiêu chuẩn - - - -, nhưng lần sau bạn thậm chí có thể viết một vài dòng để giải thích :)
Hastur

Thx, nhưng tôi vẫn nhận được đầu ra với một ống ...
Nhíu mày

@ Hui, bạn đã chạy lệnh chính xác như được đưa ra bao gồm tất cả các dấu gạch ngang và ký tự khoảng trắng? Hệ điều hành của bạn là gì?
Stéphane Chazelas

:|paste -d '|' fileA - - fileBcung cấp phiên bản chính xác hơn mà không có dấu phân cách không gian.
Pål GD

7

Chà, điều này không sử dụng sed, awk hoặc grep, nhưng bạn có thể làm điều đó khá dễ dàng trong bash. Lệnh là:

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

Vấn đề với dán là dấu phân cách là một ký tự đơn. Bạn cũng có thể chèn một ký tự đơn và sử dụng sed để chuyển đổi nó, nhưng đó sẽ là loại dễ bị lỗi nếu ký tự đã xuất hiện trong tệp đầu vào.


2
Giải pháp của bạn sẽ không hoạt động nếu dòng chứa bất kỳ ký tự dấu gạch chéo ngược hoặc bắt đầu bằng dấu gạch ngang. Bạn muốn sử dụng IFS=trước mỗi read. Bạn có thể dễ dàng làm điều đó với paste. Xem câu trả lời của tôi , và cũng có một này để xem lý do tại sao nên tránh sử dụng whilevòng lặp trong shell script.
cuonglm

Nó hoạt động cho tập tin của tôi. Nhiều Thx !!!
Nhíu mày

5

Phiên bản awk (GNU)

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

Với getlinelệnh in awk, bạn có thể đặt $0(tất cả các biến cho các cột) từ bản ghi đầu vào tiếp theo, nếu getline < "filename"bạn đặt tiếp theo$0 từ tệp đã chỉ định.

getline <"file" Đặt $ 0 từ bản ghi tệp tiếp theo; đặt NF.


Tại sao nỗ lực của bạn không hoạt động như bạn mong đợi? Từ man pastechúng ta có thể đọc

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

nhưng nó sử dụng một dấu phân cách cho mỗi cột .

Vì vậy, lệnh
paste -d '|*|*' fileA fileB fileA fileBcho tôi các dòng như

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...


Một sedgiải pháp mà tôi đề nghị nên tránh ngay cả khi gần với nỗ lực ban đầu của bạn, bởi vì nó vá hành vi thu được vào mục đích ban đầu của bạn:

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

Để tránh vì bạn thay thế từng mẫu |bằng mẫu mới |||, nhưng bạn phải giả sử rằng ký hiệu ống ( |) không có trong dữ liệu của bạn , nếu không bạn phải xử lý các trường hợp đặc biệt và tạo mã phức tạp hơn để tránh tác dụng phụ.


Một biến thể với cấu trúc Here String [ 1 ]<<<

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

Bạn đặt 5 dấu phân cách với -d ' ||| '( dấu cách , |, |, |, dấu cách) và 4 tệp giả ( - - - -) sẽ lấy dữ liệu từ chuỗi trống ''.


Đã thử nghiệm trên GNU Awk 4.0.1, dán (GNU coreutils) 8.21 và sed (GNU sed) 4.2.2


Thx, lệnh awk hoạt động!
Nhíu mày

1
Không có chi. Đã cập nhật câu trả lời thêm một sedví dụ để tránh (:-)) và thêm nhận xét.
Hastur

4

Nếu bạn muốn tránh sự kỳ diệu và kịch tính của các dấu phân cách tròn và các tệp giả, bạn chỉ có thể nối thêm dấu phân cách của bạn vào một tệp trước khi dán chúng:

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

cho

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.

Tôi thích điều này vì đơn giản. Tôi tin rằng bạn có nghĩa là "trả trước", chứ không phải "nối thêm". Thanh toán câu trả lời awk của Hastur cho phiên bản awk này.
tự đại diện

Bạn nên thay đổi quy trình thay thế thành một đường ống, vì vậy bạn sẽ không có giới hạn về số lượng đạn hỗ trợ.
cuonglm

@Wildcard có, trả trước, nhưng tôi sẽ viết lại để nối vào filea. Tôi nghĩ awk là một chút quá mức cho việc này.
snth

@cuonglm đúng, nhưng tôi muốn tránh đường ống cho rõ ràng. Tôi cảm thấy một đường ống sẽ làm cho nó bắt đầu trông giống như các tập tin giả, nhưng bạn đã đúng
snth

0

bạn cũng có thể làm điều đó với 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.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.