Pemrosesan teks - gabungkan setiap dua baris dengan koma

35

Saya memiliki lebih dari 1000 baris dalam sebuah file. File dimulai sebagai berikut (nomor baris ditambahkan):

Station Name
Station Code
A N DEV NAGAR
ACND
ABHAIPUR
AHA
ABOHAR
ABS
ABU ROAD
ABR

Saya perlu mengonversikan ini ke file, dengan entri yang dipisahkan koma dengan menggabungkan setiap dua baris. Data akhir akan terlihat seperti

Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
...

Apa yang saya coba adalah - mencoba menulis skrip shell dan kemudian echomereka dengan koma di antaranya. Tapi saya kira satu-liner efektif sederhana akan melakukan pekerjaan di sini mungkin di sed/ awk.

Ada ide?

mtk
sumber
@ l0b0 Anda mengedit komentar OP bahwa nomor baris "hanya ada untuk penjelasan" ...
jasonwryan
@ jasonwryan Maaf, saya pikir kalimatnya ada untuk penjelasan. Kesalahan
parsing
stackoverflow.com/questions/9605232/merge-two-lines-into-one
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Jawaban:

39

Cukup gunakan cat(jika Anda suka kucing ;-)) dan paste:

cat file.in | paste -d, - - > file.out

Penjelasan: pastemembaca dari sejumlah file dan menempel bersama-sama baris yang sesuai (baris 1 dari file pertama dengan baris 1 dari file kedua dll):

paste file1 file2 ...

Alih-alih nama file, kita bisa menggunakan -(dash). pastemengambil baris pertama dari file1 (yang merupakan stdin). Kemudian, ia ingin membaca baris pertama dari file2 (yang juga stdin). Namun, karena baris pertama stdin sudah dibaca dan diproses, yang sekarang menunggu di aliran input adalah baris kedua dari stdin, yang pastedengan senang hati menempel pada baris pertama. The -dpilihan menetapkan pembatas menjadi koma daripada tab.

Atau, lakukan

cat file.in | sed "N;s/\n/,/" > file.out

PS Ya, orang dapat menyederhanakan hal di atas

< file.in sed "N;s/\n/,/" > file.out

atau

< file.in paste -d, - - > file.out

yang memiliki keuntungan karena tidak menggunakan cat.

Namun, saya tidak menggunakan idiom ini dengan sengaja , untuk alasan kejelasan - ini kurang verbose dan saya suka cat(CATS ARE NICE). Jadi tolong jangan edit.

Atau, jika Anda lebih suka menempelkan ke kucing (tempel adalah perintah untuk menggabungkan file secara horizontal, sementara kucing menggabungkannya secara vertikal), Anda dapat menggunakan:

paste file.in | paste -d, - -
Januari
sumber
Untuk menyebutkannya lagi. Nomor baris bukan merupakan bagian dari file :)
mtk
The paste perintah sempurna bekerja, bisa tolong beri sedikit penjelasan lebih lanjut tentang hal itu. Tanda hubung ???
mtk
2
Tanda hubung artinya "baca dari stdin". Jika sumber input yang sama diulang, rekatkan tahu untuk membacanya beberapa kali per baris output.
dubiousjim
@sch: cool edit, aku tidak akan menyentuhnya :-)
Januari
1
Sehubungan dengan catargumen Anda . Tidak sed "N;s/\n/,/" file.in > file.outbekerja
Bernhard
8

Jika ada orang yang mendarat di sini ingin menggabungkan semua baris ke dalam CSV one liner, coba

cat file | tr '\n' ','
Darren Weber
sumber
3
sed 'N;s/\n/,/' file

Menggunakan sed, gabung (N) setiap 2 baris, dan ganti baris baru (\ n) dengan ",".

Guru
sumber
3
paste -sd ',\n' file.in > file.out

Perhatikan juga bahwa karena kita hanya mengganti satu karakter dengan yang lain (setiap baris baru dengan koma), kita dapat mengerjakan file input yang ada:

paste -sd ',\n' file.in 1<> file.in

(tapi waspadalah itu mungkin tidak bekerja pada sistem non-Unix yang memiliki terminator CRLF (seperti yang Microsoft) yang pastemungkin ditangani oleh beberapa POSIX yang ditiru dengan cara yang bukan Unix)

Stéphane Chazelas
sumber
Apa yang 1sedang dilakukan di sini 1<>? apakah itu salah cetak?
αғsнιη
@ αғsнιη, lihat ini
iruvar
@iruvar terima kasih
αғsнιη
2

Berikut ini adalah satu-liner (meskipun berpotensi jutaan-perintah-run-er) menggunakan Bash murni:

(IFS=; while read -r name; do read -r code; printf '%s\n" "$name,$code"; done < file.in) > file.out

Saya menggunakan subkulit (paranthesis) sehingga saya tidak perlu menyimpan dan mengembalikan IFS. Yang mana yang seharusnya dilakukan agar tidak mengacaukan lingkungan pengguna jika sumbernya bersumber. Alternatif akan terjadilah bahwa IFS baru hanya untuk readseperti di IFS= read -r name, IFS= read -r code.

Fakta bahwa semua perintah dalam loop dibangun di shell membuat kinerjanya dapat diterima dan bahkan lebih cepat daripada solusi lain untuk file kecil. Tetapi banyak orang akan menganggapnya sebagai praktik yang buruk dan seseorang harus berhati-hati ketika menggeneralisasikannya dengan hal lain.

Dihapus
sumber
secara umum, yay untuk menggunakan subkulit untuk melokalisasi perubahan lingkungan. Tetapi dalam kasus ini tidak diperlukan: Anda bisa melakukannya while IFS='\n' read -r name; do IFS='\n' read -r code ... done < file.in, yang merupakan ungkapan yang sering saya lihat dalam skrip shell. The -rbendera untuk readberarti "menafsirkan karakter '\' diikuti oleh karakter 'n' dalam aliran stdin sebagai dua karakter, bukan sebagai baris baru." Bisa dibilang, mungkin lebih estetika untuk membuat subkulit seperti yang Anda lakukan daripada mengulangi IFS='\n'.
dubiousjim
@dubiousjim: Solusi -rditingkatkan secara teknis. Besar! Saya bukan penggemar gagasan melewati perubahan IFSdua kali. Kalau saya pernah menggunakan satu baca, super bagus, tetapi tidak dua kali. Tentu saja itu masalah pendapat . Menggunakan subkulit sedikit lebih dari pengetahuan Bash umum saya akan mengatakan, sehingga banyak orang akan mengalami kesulitan memahami tujuannya. Itu hal yang buruk.
Dihapus
2

Untuk rangkaian jawaban yang lengkap, awksolusi yang mungkin adalah:

awk 'NR%2==1 {printf $0","} NR%2==0 { print $0}' *file*
Bernhard
sumber
@ downvoter: Apa yang salah dengan jawaban saya yang pantas menerima downvote? Bagaimana itu bisa diperbaiki?
Bernhard
Mungkin karena malas printf? Akan gagal dalam kasus yang jarang terjadi ketika nama stasiun berisi penentu format. (Lihat pastebin.com/wgxFttrJ untuk contoh.) Tapi ini hanya tebakan, downvote bukan dari saya.
manatwork
1

Hoary berangan awkidiom tua

awk '{ORS=NR%2?",":"\n";print}' file
Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
iruvar
sumber
awk '{ORS=NR%2?",":"\n"};1'lebih pendek dan lebih banyak idiom
cuonglm
@cuonglm, saya ragu. Dalam hal ini masih satu-liner meskipun printdan maksudnya jelas. 1sama jelasnya dengan awktangan tua seperti saya tapi saya lebih sukaprint
iruvar
Ini adalah solusi sederhana pertama yang saya temukan yang mudah dikonfigurasi untuk lebih dari 2 baris. Saya bertarung sedsebentar sebelum mencari, tetapi awkmembuat menggabungkan setiap 4 baris lebih mudah. Menyelamatkan saya perjalanan ke $EDITOR!
opello
0

Mungkin dengan perl juga,

perl -pe 's/^\d+\.\s+//;$.&1?chomp:print","' file

bunga aster
sumber
0

Sebagai contoh:

seq 0 70 | xargs -L 2 | sed 's/ /,/g'

Output: (catatan: xargs -L number_of_columnsberfungsi baik dengan sebagian besar jumlah kolom tidak hanya setiap dua baris)

0,1
2,3
4,5
6,7
8,9
10,11
12,13
14,15
16,17
18,19
20,21
22,23
24,25
26,27
28,29
30,31
32,33
34,35
36,37
38,39
40,41
42,43
44,45
46,47
48,49
50,51
52,53
54,55
56,57
58,59
60,61
62,63
64,65
66,67
68,69
70
Jonunsch
sumber