Bagaimana cara menggabungkan setiap dua baris menjadi satu dari baris perintah?


151

Saya memiliki file teks dengan format berikut. Baris pertama adalah "KEY" dan baris kedua adalah "VALUE".

KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

Saya membutuhkan nilai di baris yang sama dengan kunci. Jadi hasilnya akan terlihat seperti ini ...

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

Akan lebih baik jika saya bisa menggunakan pembatas seperti $atau ,:

KEY 4048:1736 string , 3

Bagaimana cara menggabungkan dua baris menjadi satu?


Ada banyak cara untuk melakukan ini! Saya telah melakukan bangku kecil dengan pr, paste, awk, xargs, seddanpure bash ! ( xargsapakah lebih lambat, lebih lambat dari pada bash !)
F. Hauri

Jawaban:


182

awk:

awk 'NR%2{printf "%s ",$0;next;}1' yourFile

perhatikan, ada baris kosong di akhir output.

sed:

sed 'N;s/\n/ /' yourFile

Tidak berfungsi dengan keluaran berwarna. Saya mencoba segalanya pada T&J ini dan tidak ada yang berhasil ketika output berwarna ansi. Diuji pada Ubuntu 13.04
Leo Gallucci

1
@elgalu: Karena warna ANSI hanyalah sekelompok kombinasi karakter melarikan diri. Lakukan hexedit pada output seperti itu, untuk melihat apa yang Anda miliki.
not2qubit

7
Solusi awk ini dapat pecah jika printfstring ekspansi seperti %sditemukan di dalam $0. Kegagalan itu bisa dihindari seperti ini:'NR%2{printf "%s ",$0;next;}1'
ghoti

9
Karena sangat sulit untuk google, apa artinya 1setelah kurung kurawal tutup?
erikbwork


243

paste bagus untuk pekerjaan ini:

paste -d " "  - - < filename

10
Saya pikir ini adalah solusi terbaik yang disajikan, meskipun tidak menggunakan sed atau awk. Pada input yang merupakan jumlah ganjil dari garis, solusi awk Kent melewatkan baris baru akhir, solusi sednya melewatkan garis akhir secara keseluruhan, dan solusi saya mengulangi baris terakhir. pasteDi sisi lain, berperilaku sempurna. +1.
ghoti

8
Saya sering menggunakan cuttetapi selalu lupa paste. Ini batu untuk masalah ini. Saya perlu menggabungkan semua baris dari stdin dan melakukannya dengan mudah paste -sd ' ' -.
Clint Pachl

4
Sederhana dan indah!
krlmlr

8
begitu -berarti stdin, begitu paste - -berarti membaca dari stdin, lalu membaca dari stdin, Anda dapat menumpuk sebanyak mungkin dari yang Anda inginkan.
ThorSummoner

1
Ya, @ThorSummoner ... Saya harus menempelkan setiap tiga baris menjadi satu baris dan melakukan paste - - - dan itu bekerja dengan sempurna.
Daniel Goldfarb

35

Alternatif untuk sed, awk, grep:

xargs -n2 -d'\n'

Ini yang terbaik ketika Anda ingin bergabung dengan garis N dan Anda hanya perlu ruang terbatas keluaran.

Jawaban asli saya adalah xargs -n2yang memisahkan pada kata-kata daripada garis. -ddapat digunakan untuk membagi input dengan sembarang karakter tunggal.


4
Ini adalah metode yang bagus, tetapi bekerja pada kata-kata, bukan garis. Untuk membuatnya bekerja pada baris, bisa menambahkan-d '\n'
Don Hatch

2
Wow, saya xargspengguna biasa tapi tidak tahu ini. Tip yang bagus.
Sridhar Sarnobat

1
Aku suka ini. Begitu bersih.
Alexander Guo

28

Ada lebih banyak cara untuk membunuh anjing daripada menggantung. [1]

awk '{key=$0; getline; print key ", " $0;}'

Masukkan pembatas apa pun yang Anda suka di dalam tanda kutip.


Referensi:

  1. Awalnya "Banyak cara untuk menguliti kucing", dikembalikan ke ekspresi yang lebih tua dan berpotensi berasal yang juga tidak ada hubungannya dengan hewan peliharaan.

Saya suka solusi ini.
luis.espinal

5
Sebagai pemilik kucing, saya tidak menghargai humor semacam ini.
witkacy26

4
@ witkacy26, Ekspresi yang disesuaikan menurut perhatian Anda.
ghoti

Saya suka solusi aneh ini, tetapi saya tidak mengerti cara kerjanya: S
Rubendob

@Rubendob - awk membaca setiap baris input, dan menempatkannya dalam variabel $0. The getlineperintah juga meraih "berikutnya" garis masukan dan tempat-tempat itu di $0. Jadi pernyataan pertama mengambil baris pertama, dan perintah cetak menggabungkan apa yang disimpan dalam variabel keydengan string yang mengandung koma, bersama dengan baris yang diambil menggunakan getline. Lebih jelas? :)
ghoti

12

Inilah solusi saya di bash:

while read line1; do read line2; echo "$line1, $line2"; done < data.txt

11

Meskipun tampaknya solusi sebelumnya akan berhasil, jika terjadi anomali tunggal dalam dokumen, hasilnya akan hancur berkeping-keping. Di bawah ini sedikit lebih aman.

sed -n '/KEY/{
N
s/\n/ /p
}' somefile.txt

3
Kenapa lebih aman? Apa yang /KEY/harus dilakukan Apa yang pdilakukan di akhir?
Stewart

yang /KEY/pencarian untuk sejalan dengan KEY. yang pmencetak hasilnya. ini lebih aman karena hanya menerapkan operasi pada baris dengan KEYdi dalamnya.
minghua

11

Inilah cara lain dengan awk:

awk 'ORS=NR%2?FS:RS' file

$ cat file
KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

$ awk 'ORS=NR%2?FS:RS' file
KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

Seperti yang ditunjukkan oleh Ed Morton dalam komentar, lebih baik menambahkan kawat gigi untuk keamanan dan paritas untuk portabilitas.

awk '{ ORS = (NR%2 ? FS : RS) } 1' file

ORSsingkatan dari Output Record Separator. Apa yang kami lakukan di sini adalah menguji suatu kondisi menggunakan NRyang menyimpan nomor baris. Jika modulo dariNR adalah nilai sebenarnya (> 0) maka kita mengatur Output Field Separator ke nilai FS(Field Separator) yang secara default adalah spasi, selain itu kita menetapkan nilai RS(Record Separator) yang merupakan baris baru.

Jika Anda ingin menambahkan ,sebagai pemisah kemudian gunakan yang berikut:

awk '{ ORS = (NR%2 ? "," : RS) } 1' file

1
Jelas pendekatan yang tepat jadi +1 tapi saya bertanya-tanya kondisi apa yang sedang dievaluasi untuk meminta tindakan default mencetak catatan. Apakah tugas itu berhasil? Apakah ini sederhana ORSdan itu sedang diperlakukan truekarena ORS mendapatkan nilai yang bukan nol atau string nol dan awks menebak dengan benar bahwa itu harus menjadi sengatan daripada perbandingan numerik? Apakah ini sesuatu yang lain? Saya benar-benar tidak yakin dan saya akan menuliskannya sebagai awk '{ORS=(NR%2?FS:RS)}1' file. Saya tanda kurung ekspresi ternary untuk memastikan portabilitas juga.
Ed Morton

1
@ EdMorton Ya, saya baru saja melihat beberapa upvotes pada jawaban ini akan memperbaruinya untuk memasukkan kawat gigi untuk keselamatan. Akan menambahkan parens juga.
jaypal singh

7

"ex" adalah editor baris skrip yang berada dalam keluarga yang sama dengan sed, awk, grep, dll. Saya pikir mungkin itu yang Anda cari. Banyak klon / penerus vi modern juga memiliki mode vi.

 ex -c "%g/KEY/j" -c "wq" data.txt

Ini mengatakan untuk setiap baris, jika cocok dengan "KEY" melakukan j oin dari baris berikut. Setelah itu perintah Rampungkan (terhadap semua baris), mengeluarkan w ritus dan q uit.


4

Jika Perl adalah opsi, Anda dapat mencoba:

perl -0pe 's/(.*)\n(.*)\n/$1 $2\n/g' file.txt

Apakah -0diperintahkan untuk mengatur pemisah rekaman ( $/)ke nol, sehingga kita dapat menjangkau beberapa baris dalam pola yang cocok. Halaman ini agak terlalu teknis bagi saya untuk mencari tahu apa artinya dalam praktik.
Sridhar Sarnobat

4

Anda dapat menggunakan awk seperti ini untuk menggabungkan 2 pasang garis:

awk '{ if (NR%2 != 0) line=$0; else {printf("%s %s\n", line, $0); line="";} } \
     END {if (length(line)) print line;}' flle

4

Solusi lain menggunakan vim (hanya untuk referensi).

Solusi 1 :

Buka file dalam vim vim filename, lalu jalankan perintah:% normal Jj

Perintah ini sangat mudah dimengerti:

  • %: untuk semua baris,
  • normal: jalankan perintah normal
  • Jj: jalankan perintah Join, lalu lompat ke bawah garis

Setelah itu, simpan file dan keluar dengan :wq

Solusi 2 :

Jalankan perintah di shell vim -c ":% normal Jj" filename,, lalu simpan file dan keluar bersama :wq.


Juga norm!lebih kuat kalau normal-kalau Jsudah dipetakan ulang. +1 untuk solusi vim.
qeatzy

@ qeatzy Terima kasih telah mengajari saya itu. Sangat senang mengetahuinya. ^ _ ^
Jensen

3

Anda juga dapat menggunakan perintah vi berikut:

:%g/.*/j

Atau bahkan :%g//jkarena semua yang Anda butuhkan adalah pertandingan untuk bergabung akan dieksekusi, dan null string masih regex valid.
ghoti

1
@ ghoti, Dalam Vim, saat menggunakan adil //, pola pencarian sebelumnya akan digunakan sebagai gantinya. Jika tidak ada pola sebelumnya, Vim hanya melaporkan kesalahan dan tidak melakukan apa pun. Solusi Jdamian bekerja setiap saat.
Tzunghsing David Wong

1
@TzunghsingDavidWong - itu adalah penunjuk yang bagus untuk pengguna vim. Mudah bagi saya, baik pertanyaan maupun jawaban ini tidak menyebutkan vim.
ghoti

3

Sedikit variasi pada jawaban glenn jackman menggunakan paste: jika nilai untuk -dopsi pembatas berisi lebih dari satu karakter, pastesiklus melalui karakter satu per satu, dan dikombinasikan dengan -sopsi terus melakukan itu sambil memproses file input yang sama.

Ini berarti bahwa kita dapat menggunakan apa pun yang ingin kita miliki sebagai pemisah ditambah urutan pelarian \nuntuk menggabungkan dua garis sekaligus.

Menggunakan koma:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string,1
KEY 4192:1349 string,1
KEY 7329:2407 string,2
KEY 0:1774 string,1

dan tanda dolar:

$ paste -s -d '$\n' infile
KEY 4048:1736 string$3
KEY 0:1772 string$1
KEY 4192:1349 string$1
KEY 7329:2407 string$2
KEY 0:1774 string$1

Apa yang tidak bisa dilakukan adalah menggunakan pemisah yang terdiri dari beberapa karakter.

Sebagai bonus, jika pastePOSIX compliant, ini tidak akan mengubah baris baru dari baris terakhir dalam file, jadi untuk file input dengan jumlah baris ganjil seperti

KEY 4048:1736 string
3
KEY 0:1772 string

paste tidak akan menempel pada karakter pemisahan pada baris terakhir:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string

1
nawk '$0 ~ /string$/ {printf "%s ",$0; getline; printf "%s\n", $0}' filename

Ini berbunyi sebagai

$0 ~ /string$/  ## matches any lines that end with the word string
printf          ## so print the first line without newline
getline         ## get the next line
printf "%s\n"   ## print the whole line and carriage return

1

Dalam kasus di mana saya perlu menggabungkan dua baris (untuk pemrosesan lebih mudah), tetapi memungkinkan data melewati spesifik, saya menemukan ini berguna

data.txt

string1=x
string2=y
string3
string4
cat data.txt | nawk '$0 ~ /string1=/ { printf "%s ", $0; getline; printf "%s\n", $0; getline } { print }' > converted_data.txt

output kemudian terlihat seperti:

convert_data.txt

string1=x string2=y
string3
string4

1

Pendekatan lain menggunakan vim adalah:

:g/KEY/join

Ini berlaku a join(ke baris di bawahnya) untuk semua baris yang memiliki kata KEYdi dalamnya. Hasil:

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

0

Cara paling sederhana ada di sini:

  1. Hapus baris genap dan tulis di beberapa file temp 1.
  2. Hapus baris ganjil dan tulis di beberapa file temp 2.
  3. Gabungkan dua file dalam satu dengan menggunakan perintah tempel dengan -d (berarti hapus spasi)

sed '0~2d' file > 1 && sed '1~2d' file > 2 && paste -d " " 1 2

0
perl -0pE 's{^KEY.*?\K\s+(\d+)$}{ $1}msg;' data.txt > data_merged-lines.txt

-0melahap seluruh file alih-alih membacanya baris demi baris;
pEmembungkus kode dengan lingkaran dan mencetak hasilnya, lihat detail di http://perldoc.perl.org/perlrun.html ;
^KEYcocokkan "KEY" di awal baris, diikuti oleh pertandingan yang tidak serakah dari apa pun ( .*?) sebelum urutan

  1. satu atau lebih ruang \s+apa pun termasuk jeda baris;
  2. satu atau lebih digit (\d+)yang kami tangkap dan kemudian masukkan kembali sebagai $1;

diikuti oleh akhir baris $.

\Kdengan mudah mengecualikan semua yang ada di sisi kirinya dari pergantian jadi { $1}hanya menggantikan 1-2 urutan, lihat http://perldoc.perl.org/perlre.html .


0

Solusi yang lebih umum (memungkinkan lebih dari satu jalur tindak lanjut untuk digabungkan) sebagai skrip shell. Ini menambahkan garis di antara masing-masing, karena saya membutuhkan visibilitas, tetapi itu mudah diatasi. Contoh ini adalah di mana baris "kunci" berakhir: dan tidak ada baris lain yang melakukannya.

#!/bin/bash
#
# join "The rest of the story" when the first line of each   story
# matches $PATTERN
# Nice for looking for specific changes in bart output
#

PATTERN='*:';
LINEOUT=""
while read line; do
    case $line in
        $PATTERN)
                echo ""
                echo $LINEOUT
                LINEOUT="$line"
                        ;;
        "")
                LINEOUT=""
                echo ""
                ;;

        *)      LINEOUT="$LINEOUT $line"
                ;;
    esac        
done

-1

Coba baris berikut:

while read line1; do read line2; echo "$line1 $line2"; done <old.txt>new_file

Letakkan pembatas di antara keduanya

"$line1 $line2";

mis. jika pembatas adalah |, maka:

"$line1|$line2";

Jawaban ini tidak menambahkan apa pun yang tidak disediakan dalam jawaban Hai Vu yang diposting 4 tahun sebelum Anda.
fedorqui 'SO berhenti merugikan'

Saya setuju sebagian, saya mencoba menambahkan penjelasan dan lebih umum. Ini tidak akan mengedit file lama juga. Terima kasih atas saran Anda
Suman

-2

Anda bisa menggunakan xargsseperti ini:

xargs -a file

% cat> file abc% xargs -a file abc% Bekerja untuk saya
RSG

Itu melakukan sesuatu, ya, tapi bukan apa yang diminta OP. Secara khusus, ia bergabung sebanyak mungkin garis. Anda sebenarnya bisa mendapatkan apa yang Anda inginkan xargs -n 2tetapi jawaban ini tidak menjelaskan ini sama sekali.
tripleee
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.