Gabungkan dua file baris demi baris dengan simbol pipa tiga pembatas "|||"

14

Saya memiliki dua file paralel dengan jumlah baris yang sama dalam dua bahasa dan berencana untuk menggabungkan kedua file ini baris demi baris dengan pembatas |||. Misalnya, kedua file tersebut adalah sebagai berikut:

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

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

Output yang diharapkan adalah seperti ini:

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.

Saya mencoba pasteperintah seperti:

paste -d "|||" fileA fileB

Tetapi output yang dikembalikan hanya mengandung satu pipa seperti:

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

Apakah ada cara untuk memisahkan setiap pasangan garis dengan pipa babat |||?

Mengerut
sumber
8
paste -d '|||' fileA - - fileB < /dev/null
Stéphane Chazelas
5
offtopic, tetapi terjemahan Anda tidak benar;) "Ich habe Durst" = Saya ini, "Ich bin neu" = Saya baru ... tidak selalu berarti Anda bodoh. ... kalau-kalau Anda benar-benar belajar bahasa Jerman ...
dave_alcarin
@ StéphaneChazelas Thx, tapi output saya masih hanya mengandung satu pipa ...
Frown
@dave_alcarin Dank sehr!
Mengerutkan

Jawaban:

20

Dengan tempel POSIX :

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

pasteakan menggabungkan baris yang sesuai dari semua file input. Di sini kita memiliki enam file fileA,, empat file dummy dari standar dalam -, dan fileB.

Daftar pembatas termasuk spasi, tiga pipa dan spasi dalam urutan yang akan digunakan secara pastemelingkar.

Untuk baris pertama dari enam file, fileAakan digabungkan dengan file dummy pertama (yang bukan apa-apa, terima kasih kepada no-op: operator), menghasilkan line1-fileA<space>.

File dummy pertama akan digabungkan dengan yang kedua dengan pipa, menghasilkan line1-fileA |, kemudian file dummy kedua dengan file dummy ketiga, menghasilkan line1-fileA ||, file dummy ketiga dengan file dummy keempat, menghasilkan line1-fileA |||.

Dan file dummy keempat dengan fileB, menghasilkan line1-fileA ||| line1-fileB.

Langkah-langkah itu akan diulang untuk semua lini, memberi Anda hasil yang diharapkan.


Penggunaannya :|adalah untuk mengetik kurang, dan terutama digunakan dalam shell interaktif. Dalam skrip, Anda harus menggunakan:

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

untuk mencegah subshell dari yang melahirkan.

cuonglm
sumber
1
+1 untuk :|. alternatif cerdas untuk</dev/null
cas
4
... dan +1 untuk penggunaan cerdas 4 file dummy dari input standar dengan - - - -, tetapi lain kali Anda bahkan dapat menulis beberapa baris untuk penjelasan :)
Hastur
Terima kasih, tapi saya masih mendapatkan output dengan satu pipa ...
Frown
@hui, apakah Anda menjalankan perintah persis seperti yang diberikan termasuk semua tanda hubung dan karakter spasi? Apa sistem operasi Anda?
Stéphane Chazelas
:|paste -d '|' fileA - - fileBmemberikan versi yang lebih benar tanpa pembatas ruang.
Pål GD
7

Yah, ini tidak menggunakan sed, awk, atau grep, tetapi Anda dapat melakukannya dengan cukup mudah di bash. Perintahnya adalah:

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

Masalah dengan paste adalah pembatas adalah karakter tunggal. Anda juga bisa memasukkan satu karakter dan menggunakan sed untuk mengubahnya, tetapi itu akan menjadi jenis kesalahan-rawan jika karakter sudah muncul di file input.

pengguna3188445
sumber
2
Solusi Anda tidak akan berfungsi jika garis mengandung karakter garis miring terbalik, atau mulai dengan tanda hubung. Anda ingin menggunakan IFS=sebelum masing-masing read. Anda dapat melakukannya dengan mudah paste. Lihat jawaban saya , dan juga yang ini untuk melihat mengapa harus menghindari menggunakan whileloop di skrip shell.
cuonglm
Ini berfungsi untuk file saya. Banyak terima kasih !!!
Mengerutkan
5

Versi awk (GNU)

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

Dengan getlineperintah di awk, Anda dapat mengatur $0(semua variabel untuk kolom) dari catatan input berikutnya, jika getline < "filename"Anda mengatur berikutnya $0dari file yang ditentukan.

getline <"file" Tetapkan $ 0 dari catatan file selanjutnya; atur NF.


Mengapa upaya Anda tidak berhasil seperti yang Anda harapkan? Dari man pastekita bisa membaca

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

tetapi menggunakan pembatas satu untuk setiap kolom .

Jadi perintah
paste -d '|*|*' fileA fileB fileA fileBmemberi saya garis

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


Sebuah sedsolusi yang saya sarankan untuk menghindari bahkan jika dekat dengan upaya asli Anda, karena patch perilaku yang diperoleh untuk tujuan awal Anda:

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

Untuk menghindari karena Anda mengganti setiap pola |dengan yang baru |||, tetapi Anda harus mengasumsikan bahwa simbol pipa ( |) tidak ada dalam data Anda , kalau tidak Anda harus berurusan dengan kasus khusus dan membuat kode yang lebih kompleks untuk menghindari efek samping.


Varian dengan konstruk Here String [ 1 ]<<<

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

Anda menetapkan 5 pembatas dengan -d ' ||| '(spasi, |, |, |, spasi) dan 4 file dummy ( - - - -) yang akan mengambil data dari string kosong ''.


Diuji pada GNU Awk 4.0.1, tempel (GNU coreutils) 8.21 dan sed (GNU sed) 4.2.2

Cepat
sumber
Terima kasih, perintah awk berhasil!
Mengerutkan
1
Sama sama. Memperbarui jawaban dengan menambahkan sedcontoh untuk menghindari (:-)) dan lebih banyak komentar.
Hastur
4

Jika Anda ingin menghindari keajaiban dan drama pembatas melingkar dan file dummy, Anda bisa menambahkan pembatas Anda ke satu file sebelum menempelkannya:

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

memberi

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.
snth
sumber
Saya suka ini untuk kesederhanaan. Saya percaya maksud Anda "prepend", bukan "append". Checkout jawaban awur Hastur untuk versi awk ini.
Wildcard
Anda harus mengubah proses substitusi ke pipa, jadi Anda tidak akan memiliki batas untuk jumlah shell yang mendukungnya.
cuonglm
@ Kartu Memori ya, awali, tapi saya akan menulis ulang untuk menambahkan filea. Saya pikir awk sedikit berlebihan untuk ini.
snth
@cuonglm benar, tapi saya ingin menghindari pipa untuk kejelasan. Saya merasa pipa akan membuatnya mulai terlihat seperti file dummy, tetapi Anda benar
snth
0

Anda bisa melakukannya dengan python juga dengan cara ini.

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