Bagaimana cara membalikkan setiap 4 baris?

13

Pertama-tama, dengan ini menjadi posting pertama saya di sini, saya hanya ingin mengatakan bahwa saya telah menemukan VIM sebagai alat yang hebat dan forum di sini sangat membantu dalam menemukan jawaban atas pertanyaan, dengan banyak orang yang membantu menyediakan bantuan yang tak ternilai. Saya masih sangat baru untuk VIM, jadi hampir semua yang saya pelajari tentang itu berasal dari sini.

Pertanyaan saya adalah: Saya tahu cara membalikkan SEMUA baris dalam file (: g / ^ / m0 antara lain), tetapi apakah ada cara untuk membalikkan setiap 4 baris dalam file, sehingga

line1
line2
line3
line4
line5
line6
line7
line8
...

menjadi

line4
line3
line2
line1
line8
line7
line6
line5
...

Anda dapat mengasumsikan bahwa akan selalu ada kelipatan tepat dari 4 baris dalam file tersebut.

ablewasiereisawelba
sumber
2
Tentang "ps" Anda: jika Anda menyisipkan 4 spasi di depan baris, itu ditafsirkan sebagai kode (Anda dapat memilih teks dan menggunakan tombol format di atas). Anda dapat menemukan rincian lebih lanjut tentang ikon interogasi di atas .
mMontu

Jawaban:

15

Perintah yang :Reversedijelaskan di Vim Wiki dapat digunakan untuk ini (Anda dapat memasukkannya ke dalam .vimrcuntuk membuatnya permanen):

command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohl

Kemudian Anda bisa merekam makro untuk menjalankan perintah di setiap empat baris:

qmV3j:Reverse<cr>4jq
1000@m

Penjelasan:

  • qm: 'q' dalam mode normal dimulai (dan berhenti) merekam makro dalam register yang diberikan (register 'm', dalam hal ini, tetapi bisa berupa huruf lain)
  • V: masuk ke mode visual dan pilih garis saat ini
  • 3j: perluas seleksi visual ke 3 baris berikutnya
  • :Reverse<cr>: jalankan perintah Reverse pada baris yang dipilih (di <cr>sini adalah enterkunci)
  • 4j: pergi ke baris tidak berubah berikutnya
  • q: berhenti merekam makro
  • 1000@m: jalankan makro yang tercatat pada register 'm' 1000 kali (Anda dapat menambah nomor ini jika file Anda lebih besar dari 4000 baris)

Edit:

Seperti yang disebutkan dalam komentar, Anda bisa menggunakan makro rekursif alih-alih menggunakan hitungan:

qmV3j:Reverse<cr>4jq
qM@mq
@m
  • qM: jika register yang ditentukan untuk qhuruf besar, makro ditambahkan ke register (yang juga berguna ketika Anda menyadari ketika Anda melewatkan langkah terakhir pada makro kompleks)
  • @m: jalankan makro saat mendaftar m
  • q: berhenti menambahkan makro

Meskipun dibuat sebagai makro, Anda bisa membuat perintah untuk ini jika itu adalah tugas umum pada alur kerja Anda:

function! Reverse4()
   let reg_m = @m
   let @m = '<c-r><c-r>m'
   normal! @m
   let @m = reg_m
endfunction
command! Reverse4 call Reverse4()
  • function! Reverse4()/ endfunction: mendefinisikan fungsi baru
  • let reg_m = @m: menyimpan isi register saat ini m
  • let @m = "<c-r><c-r>m": masukkan makro dalam register m- perhatikan bahwa itu <c-r>adalah notasi vim untuk Ctrl+ rdan Anda harus mengetik ini, bukan menyalin / menempel, sehingga baris Anda akan mirip let @m = 'V3j:Reverse^M4j@m'dan itu akan berisi karakter khusus (^ M)
  • normal! @m: perintah normal menjalankan argumennya seperti yang telah diketik dalam mode normal, sehingga akan menjalankan makro rekursif
  • let @m = reg_m: mengembalikan konten register m, jadi Anda tidak harus ingat bahwa register ini digunakan pada fungsi ini dan hindari menggunakannya

  • command! Reverse4 call Reverse4(): buat perintah baru untuk fungsi ini

Bergantung pada kebutuhan Anda, Anda dapat meningkatkannya, misalnya: meneruskan argumen ke perintah dan fungsi sehingga akan berfungsi untuk sejumlah baris alih-alih diperbaiki dalam kelompok 4 baris.

mMontu
sumber
Alih-alih 1000@qhack, Anda juga dapat menggunakan makro rekursif: qmqqm ... @mq@m.
Gagang pintu
Ketika saya mencoba menjalankan "qmV3j: Reverse <cr> 4jq", katanya "E492: Bukan perintah editor". Saya pasti melakukan sesuatu yang salah. Saya menggunakan - dan hanya pernah menggunakan - GVIM, omong-omong. Saya seharusnya menyebutkan itu sejak awal.
ablewasiereisawelba
Ah, sudahlah. Saya menemukan jawabannya - saya tidak seharusnya mengetikkan ":" sebelum bagian "qmV3j: Membalikkan <cr> 4jq". Berhasil! Terima kasih banyak, mMontu. Jika saya dapat bertanya - bagaimana cara memasukkan perintah ": Reverse" di .vimrc?
ablewasiereisawelba
3
@ablewasiereisawelba Untuk membuat perintah tersedia secara permanen, cukup tulis baris command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohldi suatu tempat di vimrc Anda.
Saginaw
@saginaw Oh, saya tidak menyadari itu sesederhana itu. Terima kasih.
ablewasiereisawelba
9

Seperti semua tindakan berulang , jika Anda dapat melakukan sesuatu sekali dengan operasi edit dasar, maka Anda dapat dengan mudah melakukannya berkali-kali dengan merekam makro.

(Dalam hal ini saya akan menggunakan makro rekursif, tetapi Anda bisa merekam yang tidak rekursif dan memutarnya berulang kali dengan memalu @@atau menggunakan hitungan.)

ggqqqqqddjjpkddkkPjddpjj@qq@q

Rusak

  1. gg: Pindah ke awal file.
  2. qqq: Hapus register q. Kita akan melihat mengapa ini perlu di langkah 5.
  3. qq: Mulai merekam makro untuk mendaftar q
  4. ddjjpkddkkPjddpjj: Serangkaian operasi yang menata ulang empat baris pertama hanya dengan menghapus dan menempelkannya secara manual. (Ini mungkin terlihat rumit pada pandangan pertama, tapi jangan tertipu ini adalah hal yang sangat mendasar bahwa Anda akan belajar dalam 5 menit atau lebih dari. vimtutor: j, k, dd, p, P)
  5. @q: Panggil makro! Pada titik ini, register qtidak mengandung apa-apa (karena kami membersihkannya pada langkah 2), jadi tidak ada yang terjadi.
  6. q: Berhenti merekam makro.
  7. @q: Putar ulang makro rekursif sebanyak yang diperlukan.

NB Di atas tidak akan menghasilkan hasil yang diinginkan jika file berisi sejumlah baris yang tidak bisa dibagi empat. Untuk menghentikan makro lebih awal jika tidak ada empat baris yang tersisa, kita perlu melakukan perintah normal-mode Vim yang akan gagal, sebelum kita mulai menerapkan suntingan di langkah 4. Kita bisa melakukan ini dengan mencoba bergerak turun garis tiga kali (dan kemudian kembali) sebelum kita mulai memindahkan garis:jjj3k

Jadi:

ggqqqqqjjj3kddjjpkddkkPjddpjj@qq@q
Kaya
sumber
Mengapa Anda secara eksplisit menghapus daftar q? Jika Anda hanya merekam ke dalamnya, apa pun yang ada di sana akan ditimpa pula ....
Wildcard
4
@Wildcard Karena ini adalah makro rekursif, pertama kali Anda memanggil konten register q, itu harus kosong, jika tidak maka akan mengacaukan edisi Anda.
Saginaw
Sangat bagus. Saya mencoba ini secara interaktif tanpa mencoba mengetik urutan yang tepat dari perintah Anda dan akhirnya menggunakan:qqqggqqjjjkddkkPjddjpkddkkPjjjj@qq@q
Wildcard
1
@ablewasiereisawelba where I can just use the one-line custom command each time I need to do this- perhatikan bahwa Anda tidak harus membuat kembali makro setiap kali Anda perlu menggunakannya, karena dimungkinkan untuk menyimpannya sebagai fungsi / perintah pada vimrc Anda.
mMontu
1
@ablewasiereisawelba Saya senang Anda menemukan "Edit" bermanfaat! Karena ini adalah posting pertama Anda di sini, mungkin Anda tidak mengetahui cara kerja notifikasi StackExchange: ketika Anda memposting komentar, pembuat jawaban / pertanyaan menerima notifikasi; orang lain hanya akan menerima pemberitahuan jika Anda menyertakan @ <nick>. Karenanya hanya Rich yang menerima pemberitahuan tentang pesan terakhir Anda.
mMontu
7

Anda juga dapat melakukan ini dengan Experintah menggunakan sedsebagai filter eksternal:

:%!sed -n 'h;n;G;h;n;G;h;n;G;p'

Versi ini akan mengabaikan (menghapus) setiap baris tambahan di luar kelipatan 4. Untuk mempertahankan set terakhir kurang dari 4 baris (terbalik), gunakan:

:%!sed -n '$p;h;n;G;$p;h;n;G;$p;h;n;G;p'

Di %sini berarti "Setiap baris dalam buffer."

The !berarti perintah "Jalankan perintah berikut dengan garis ditetapkan sebagai masukan, dan menggantikan garis-garis yang ditentukan dengan output dari perintah." (Ini disebut filter; sangat berguna untuk hal-hal seperti menyortir, misalnya, :%!sortakan mengurutkan semua baris dalam file Anda; :2,8!sortakan mengurutkan baris 2-8, dll.)

sedadalah alat editor aliran dan ditemukan di semua sistem mirip Unix. Konsep kunci yang seddigunakan di sini adalah "pola ruang" (yang secara default hanya berisi setiap baris input pada gilirannya), dan "ruang pegang" (di mana Anda dapat menempel teks tambahan saat menggunakan seduntuk menyimpannya saat memproses lainnya jalur input).

-nadalah opsi untuk sedperintah untuk menekan tindakan standar mencetak ruang pola (karena dalam hal ini kami hanya ingin mencetak ketika kami secara eksplisit mengatakannya.)

$pdalam sedperintah berarti "Jika Anda berada di baris terakhir dari sedinput, cetak (ruang pola)."

h berarti "menempelkan isi saat ini dari 'ruang pola' di 'ruang tahan', menimpa apa pun yang ada di sana."

n berarti "ganti isi dari 'ruang pola' dengan baris berikutnya dari input."

G berarti "menambahkan ke 'ruang pola': baris baru diikuti dengan isi 'ruang tahan'."

Secara keseluruhan, sedperintah menyimpan empat baris output, membalikkannya saat menyimpannya, dan kemudian mencetaknya. The $pperintah ditambahkan dalam versi kedua memastikan bahwa jika baris terakhir dari file telah dicapai selain di kelipatan dari 4 baris, baris masih dicetak.


Untuk alternatif, pendekatan interaktif masih tanpa menggunakan fitur spesifik Vim dan juga tanpa menggunakan filter eksternal:

:4

untuk menuju ke baris keempat.

:.m -4 | +3m . | +2m . | +5

untuk membalikkan empat baris sebelumnya (1-4) dan biarkan kursor Anda berada di jalur 8.

.m -4memindahkan garis saat ini tepat setelah garis empat garis kembali (meninggalkan kursor pada garis yang dipindahkan).

+3m .memindahkan garis yang 3 baris setelah garis saat ini, hanya setelah garis saat ini, meninggalkan kursor pada garis yang dipindahkan. +2m .tentu saja kerjanya sama.

+5 menempatkan kursor lima baris ke bawah dari tempatnya.

Ulangi sesuai yang diinginkan.

Di Vim Anda dapat mengulangi seluruh perintah ini dengan @:, lalu ulangi lagi dengan @@. Dalam POSIX viatau exAnda perlu memasukkan :.m -4 | +3m . | +2m . | +5 sebagai baris teks, hapus itu ke buffer bernama (register), dan kemudian jalankan buffer bernama itu (register).

Jadi dalam exmode, membalikkan garis secara interaktif hanya menggunakan fitur yang ditentukan POSIX, dan mulai dengan 17 baris teks:

Entering Ex mode.  Type "visual" to go to Normal mode.
:0a     # Append following text after "line 0" (i.e. insert at start of file).
.m -4 | +3m . | +2m . | +5
.       # End text insertion
:d k    # Delete that line to register k
line1   # This is a printout of the current line
:4      # Move to line 4
line4
:@k     # Execute register k to reverse lines 1-4
line8
:@@     # Execute register k again
line12
:@@     # Execute register k again
line16
:@@     # Execute register k again
line17
:%p     # Print the whole buffer (just to see what was done)
line4
line3
line2
line1
line8
line7
line6
line5
line12
line11
line10
line9
line16
line15
line14
line13
line17
:wq     # Save and quit

Bacaan lebih lanjut:

Wildcard
sumber
Bisakah Anda menjelaskan sedikit lebih banyak solusi Anda?
vappolinario
3
@ vappolinario, saya menambahkan beberapa penjelasan lebih lanjut. Apakah itu membantu? :)
Wildcard
Wow, jawaban yang sangat panjang. :) Sebenarnya saya punya sed, tapi saya baru saja menggunakannya dengan satu skrip sederhana yang saya temukan di suatu tempat - mungkin di papan ini - itu adalah solusi untuk masalah yang saya miliki. Namun, ketika saya mencoba menjalankan baris yang disarankan, dikatakan "'sed' tidak dikenali sebagai perintah internal atau eksternal, program yang dapat dijalankan, atau file batch." Saya tidak yakin apakah saya perlu melakukan sed di folder vim atau apa.
ablewasiereisawelba
1
@ablewasiereisawelba, jika Anda menjalankan kotak Windows dan tidak menggunakan Cygwin (atau MobaXterm atau yang serupa), Anda bisa mencoba metode lain yang saya berikan: 4G:.m -4 | +3m . | +2m . | +5<Enter>@:kemudian @@diulangi hingga file Anda sepenuhnya diproses. Saya sarankan menginstal MobaXterm. :)
Wildcard
@ Kartu Memori Oh oke, saya sedang memeriksa MobaXterm sekarang. :)
ablewasiereisawelba
7
:g/^/exe 'm .-' . substitute(line('.') % 4, '^0$', '4', '')

Bahasa Inggris Biasa: Untuk setiap baris, gerakkan garis saat ini ke atas lnum % 4, kecuali lnum % 4 == 0, dalam hal mana memindahkan garis saat ini ke atas 4.

Juga, untuk membalikkan setiap nbaris, ganti 4's di perintah di atas dengan n.

djjcast
sumber
2
Perintah satu baris yang sangat bagus. Terima kasih, djjcast.
ablewasiereisawelba