Ubah urutan baris dalam file

11

Saya mencoba mengubah urutan garis dalam pola tertentu. Bekerja dengan file dengan banyak baris (mis. 99 baris). Untuk setiap tiga baris, saya ingin baris kedua menjadi baris ketiga, dan baris ketiga menjadi baris kedua.

CONTOH.

1- Masukan:

gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
...

2- Output:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
...
Annick Raymond
sumber

Jawaban:

12

Menggunakan awkdan integer matematika:

awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay } }' /path/to/input

Operator modulus melakukan pembagian integer dan mengembalikan sisanya, jadi untuk setiap baris, ia akan mengembalikan urutan 1, 2, 0, 1, 2, 0 [...]. Mengetahui itu, kita hanya menyimpan input pada garis di mana modulus adalah 2 untuk nanti - jadi, setelah mencetak input ketika itu nol.

DopeGhoti
sumber
Kami memiliki kekurangan kecil di sini. Lihat jawaban saya, bagian perbaikan kecil
Sergiy Kolodyazhnyy
Terima kasih atas tangkapan yang bagus; Saya telah memasukkan perbaikan ke jawaban saya dalam bentuk NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti
23
$ seq 9 | sed -n 'p;n;h;n;G;p'
1
3
2
4
6
5
7
9
8

Yaitu, pmematahkan garis saat ini, mendapatkan yang next, hlama itu, mendapatkan yang next, Gdan garis ditahan (menambahkannya ke ruang pola) dan pmelempar ruang pola 2-garis dengan baris ketiga dan kedua ditukar.

Stéphane Chazelas
sumber
3

Pendekatan awk lain :

awk '{print $0; if ((getline L2)>0 && (getline L3)>0){ print L3 ORS L2 }}' file

Hasil:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

  • (getline L2)>0 && (getline L3)>0- ekstrak 2 catatan berikutnya jika ada

  • setiap catatan ke-2 dan ke-3 ditugaskan L2dan L3masing-masing variabel

RomanPerekhrest
sumber
1
Saya mengasumsikan variabel-variabel tersebut dimulai dengan huruf L (huruf kecil). Mereka adalah pilihan yang buruk untuk dibaca karena mereka terlihat seperti angka untuk dua belas dan tiga belas. Pilihan yang lebih baik mungkin line2, dll.
Dijeda sampai pemberitahuan lebih lanjut.
@DennisWilliamson, diubah menjadi huruf besar
RomanPerekhrest
1

Menggunakan perldan skrip pendek:

user@pc:~$ cat input.txt 
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

user@pc:~$ perl -ne '$l2=<>; $l3=<>; print $_,$l3,$l2;' input.txt 
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

Script memproses seluruh file, untuk setiap baris (disimpan dalam $_) itu akan mendapatkan dua baris berikutnya ( $l2dan $l3) dan mencetaknya dalam urutan yang diminta: line1, line3, line2.

Frank Förster
sumber
1

Salah satu caranya bisa sebagai berikut:

sed -e '
   /\n/s/\(.*\)\(\n\)\(.*\)/\3\2\1/;//b
   $!N;$q;N;                            # load up the pattern space with 3 lines provided eof not reached
   P;D;                                 # first just print the first line then interchange the two and print them
' yourfile

Kalau tidak,

perl -ne 'print $_, reverse scalar <>, scalar <>' yourfile

Hasil

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

sumber
1

Mengapa tidak membuat loop sementara? Dalam bentuk yang diperluas:

( while read a
  do
    read b
    read c
    echo "$a"
    echo "$c"
    echo "$b"
  done
) < input.txt

Dalam "format satu baris":

( while read a ; do read b ; read c ; echo "$a" ; echo "$c" ; echo "$b" ; done) < input.txt

Output:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
Stephen Quan
sumber
1

Perl

perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt

Idenya di sini adalah bahwa kita menggunakan operator modulo %dengan $.variabel nomor baris , untuk mencari tahu mana yang pertama, mana yang setiap detik, dan mana yang setiap baris ke-3. Untuk setiap sisa baris ke-3 adalah 0, sedangkan untuk setiap baris ke-1 dan ke-2 akan memiliki angka yang sesuai.

Uji:

$ cat input.txt                                                                                                          
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

$ perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt                                    
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

Perbaikan kecil

Pendekatan dengan menyimpan baris kedua ke dalam variabel memiliki kelemahan. Bagaimana jika baris terakhir adalah yang "kedua", yaitu untuk nomor baris yang tersisa adalah 2? Kode asli dalam jawaban saya dan DopeGhoti tidak akan dicetak My dog is orangejika kami meninggalkan baris terakhir. Perbaikan untuk itu dalam kedua kasus adalah dengan menggunakan END{}blok kode, dengan membatalkan variabel sementara setelah pencetakan. Dengan kata lain:

$ awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay;delay=""}END{print delay}' input.txt

dan

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s}' input.txt 

Dengan cara ini, kode ini akan berfungsi untuk jumlah baris yang berubah-ubah dalam file, bukan hanya yang dapat dibagi 3.

Perbaikan tambahan untuk masalah yang disebutkan dalam komentar

Dalam kasus awk, jika baris terakhir dalam file menghasilkan output 1 untuk $. % 3, kode sebelumnya memiliki masalah mengeluarkan baris baru kosong karena pencetakan tanpa syarat END{print delay}, karena printfungsi yang disebutkan dalam komentar selalu menambahkan baris baru ke variabel apa pun yang beroperasi. Dalam hal perlversi masalah ini tidak terjadi, karena dengan fungsi -neflags printtidak menambahkan baris baru.

Meskipun demikian, perbaikan dalam kasus awk adalah untuk membuat bersyarat, seperti yang disebutkan oleh Dope Ghoti dalam komentar adalah untuk memverifikasi panjang variabel sementara. Versi perl dari perbaikan yang sama adalah:

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt 
Sergiy Kolodyazhnyy
sumber
1
Perbaikan Anda memiliki kelemahan kecil sendiri karena akan menambahkan baris kosong output untuk file dengan jumlah baris 'salah'. Saya telah memperbaikinya dalam penggabungan peningkatan Anda dalam jawaban saya dengan (untuk awk) NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti
1
@DopeGhoti Masalah ini tidak terjadi dengan perl, karena cetak perl dengan -nebendera tidak menghasilkan baris baru. Memang mencetak, tapi itu adalah string nol, tidak ada baris baru. Nontheless, saya telah menambahkan penyebutan masalah dan perbaikan yang sama ke dalam jawaban saya. Terima kasih!
Sergiy Kolodyazhnyy
1

Vim

Tidak cocok untuk file yang panjang, tetapi tetap berguna jika Anda hanya mengedit file dan ingin, misalnya, untuk memesan ulang beberapa bait yaml.

Rekam pertama makro:

gg qq j ddp j q

Dan kemudian ulangi beberapa kali yang diinginkan:

@q @q @q ...

Atau misalnya saja

3@q

Penjelasan:

  • gg - pergi ke baris pertama
  • qq - mulai merekam makro
  • j - pergi ke baris kedua
  • ddp - menukar baris kedua dan ketiga
  • j - pergi ke baris keempat, yaitu ke yang pertama dari tiga baris berikutnya
  • q - berhenti merekam
  • @ q - memutar ulang makro sekali
  • 3 @ q - memutar ulang makro tiga kali
Edheldil
sumber
1
Alih-alih mengulang secara manual @q @q @q, mungkin dilakukan dengan cara ini 3@q- ulangi tiga kali. 100@q- ulangi makro 100 kali.
MiniMax
0

Pemakaian: ./shuffle_lines.awk input.txt

Periksa shebang #!/usr/bin/awk -f, karena awklokasi mungkin berbeda pada sistem Anda.

#!/usr/bin/awk -f

{
    if ((NR + 1) % 3 == 0) {
        buffer = $0;
    } else if (NR % 3 == 0) {
        print $0 ORS buffer;
        buffer = "";
    } else {
        print;
    }
}
MiniMax
sumber