Làm thế nào để đảo ngược mỗi 4 dòng?


13

Trước hết, đây là bài đăng đầu tiên của tôi ở đây, tôi chỉ muốn nói rằng tôi đã thấy VIM là một công cụ tuyệt vời và diễn đàn ở đây rất hữu ích trong việc tìm câu trả lời cho câu hỏi, với rất nhiều người hữu ích cung cấp hỗ trợ vô giá. Tôi vẫn còn rất mới với VIM, gần như mọi thứ tôi đã học về nó đều đến từ đây.

Câu hỏi của tôi là: Tôi biết cách đảo ngược TẤT CẢ các dòng trong một tệp (: g / ^ / m0 trong số các cách khác), nhưng có cách nào để đảo ngược mỗi 4 dòng trong một tệp, để

line1
line2
line3
line4
line5
line6
line7
line8
...

trở thành

line4
line3
line2
line1
line8
line7
line6
line5
...

Bạn có thể giả định rằng sẽ luôn có một bội số chính xác của 4 dòng trong các tệp đó.


2
Giới thiệu về "ps" của bạn: nếu bạn chèn 4 khoảng trắng phía trước một dòng thì nó được hiểu là mã (bạn có thể chọn văn bản và sử dụng các nút định dạng ở trên cùng). Bạn có thể tìm thêm chi tiết về biểu tượng thẩm vấn ở trên cùng .
mMontu

Câu trả lời:


15

Lệnh được :Reversegiải thích tại Vim Wiki có thể được sử dụng cho việc này (bạn có thể đưa nó vào .vimrcđể biến nó thành vĩnh viễn):

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

Sau đó, bạn có thể ghi lại một macro để chạy lệnh trên mỗi bốn dòng:

qmV3j:Reverse<cr>4jq
1000@m

Giải trình:

  • qm: 'q' ở chế độ bình thường bắt đầu (và dừng) ghi macro trong một thanh ghi đã cho (đăng ký 'm', trong trường hợp này, nhưng nó có thể là bất kỳ chữ cái nào khác)
  • V: vào chế độ trực quan và chọn dòng hiện tại
  • 3j: mở rộng lựa chọn trực quan đến 3 dòng tiếp theo
  • :Reverse<cr>: chạy lệnh Reverse trên các dòng đã chọn ( <cr>ở đây là viết tắt của enterkhóa)
  • 4j: đi đến các dòng không thay đổi tiếp theo
  • q: dừng ghi macro
  • 1000@m: chạy macro được ghi tại đăng ký 'm' 1000 lần (bạn có thể tăng số này nếu tệp của bạn lớn hơn 4000 dòng)

Biên tập:

Như đã đề cập trong các nhận xét, bạn có thể sử dụng macro đệ quy thay vì sử dụng số đếm:

qmV3j:Reverse<cr>4jq
qM@mq
@m
  • qM: nếu thanh ghi được chỉ định qlà chữ hoa, macro sẽ được thêm vào thanh ghi (cũng hữu ích khi bạn nhận ra khi bạn bỏ lỡ các bước cuối cùng trên macro phức tạp)
  • @m: chạy macro khi đăng ký m
  • q: dừng nối thêm macro

Mặc dù nó được tạo dưới dạng macro, bạn có thể tạo một lệnh cho việc này nếu đó là một nhiệm vụ phổ biến trong quy trình làm việc của bạn:

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: định nghĩa một hàm mới
  • let reg_m = @m: lưu nội dung hiện tại của thanh ghi m
  • let @m = "<c-r><c-r>m": chèn macro vào thanh ghi m- lưu ý đó <c-r>là ký hiệu vim cho Ctrl+ rvà bạn nên nhập cái này, không sao chép / dán, vì vậy dòng của bạn sẽ tương tự let @m = 'V3j:Reverse^M4j@m'và nó sẽ chứa một ký tự đặc biệt (^ M)
  • normal! @m: lệnh bình thường chạy đối số của nó vì nó đã được gõ trong chế độ bình thường, vì vậy nó sẽ chạy macro đệ quy
  • let @m = reg_m: khôi phục nội dung của thanh ghi m, vì vậy bạn không cần phải nhớ rằng thanh ghi này đang được sử dụng trên chức năng này và tránh sử dụng nó

  • command! Reverse4 call Reverse4(): tạo một lệnh mới cho chức năng này

Tùy thuộc vào nhu cầu của bạn, bạn có thể nâng cao nó, ví dụ: truyền một đối số cho lệnh và hàm để nó hoạt động cho bất kỳ số dòng nào thay vì được cố định trong các nhóm 4 dòng.


Thay vì 1000@qhack, bạn cũng có thể sử dụng macro đệ quy : qmqqm ... @mq@m.
Doorknob

Khi tôi cố chạy "qmV3j: Reverse <cr> 4jq", nó báo "E492: Không phải là lệnh biên tập". Tôi phải làm gì đó sai. Nhân tiện, tôi đang sử dụng - và chỉ mới sử dụng - GVIM. Tôi nên đã đề cập rằng ở nơi đầu tiên.
couldwasiereisawelba

À, đừng bận tâm. Tôi đã tìm ra nó - Tôi không phải gõ ":" trước phần "qmV3j: Reverse <cr> 4jq". Nó đã làm việc! Cảm ơn bạn rất nhiều, mMontu. Nếu tôi có thể hỏi - làm cách nào để bao gồm lệnh ": Reverse" trong .vimrc của tôi?
couldwasiereisawelba

3
@ablewasiereisawelba Để cung cấp lệnh vĩnh viễn, chỉ cần viết dòng command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohlở đâu đó trong vimrc của bạn.
saginaw

@saginaw Ồ, tôi không nhận ra điều đó đơn giản. Cảm ơn bạn.
couldwasiereisawelba

9

Như với tất cả các hành động lặp đi lặp lại , nếu bạn có thể làm điều gì đó một lần với các thao tác chỉnh sửa cơ bản, thì bạn có thể dễ dàng thực hiện nhiều lần bằng cách ghi một macro.

(Trong trường hợp này tôi sẽ sử dụng macro đệ quy, nhưng bạn chỉ có thể ghi lại một macro không đệ quy và phát lại nhiều lần bằng cách gõ @@hoặc sử dụng số đếm.)

ggqqqqqddjjpkddkkPjddpjj@qq@q

Hỏng

  1. gg: Di chuyển đến đầu tập tin.
  2. qqq: Xóa sổ đăng ký q. Chúng ta sẽ thấy lý do tại sao điều này là cần thiết trong bước 5.
  3. qq: Bắt đầu ghi một macro để đăng ký q
  4. ddjjpkddkkPjddpjj: Một loạt các hoạt động sắp xếp lại bốn dòng đầu tiên chỉ bằng cách xóa và dán chúng bằng tay. (Điều này có thể nhìn phức tạp ở cái nhìn đầu tiên, nhưng không bị lừa gạt Đây là công cụ rất cơ bản mà bạn muốn đã học được trong 5 phút đầu tiên, hay như vậy của. vimtutor: j, k, dd, p, P)
  5. @q: Gọi macro! Tại thời điểm này, đăng ký qkhông chứa gì (vì chúng tôi đã xóa nó trong bước 2), vì vậy sẽ không có gì xảy ra.
  6. q: Dừng ghi macro.
  7. @q: Phát lại macro đệ quy nhiều lần nếu cần.

NB Ở trên sẽ không tạo ra kết quả mong muốn nếu tệp chứa một số dòng không chia hết cho bốn. Để dừng macro sớm nếu không còn bốn dòng, chúng tôi cần thực hiện lệnh chế độ thông thường Vim sẽ thất bại, trước khi chúng tôi bắt đầu áp dụng các chỉnh sửa trong bước 4. Chúng tôi có thể thực hiện việc này bằng cách cố gắng di chuyển xuống một dòng ba lần (và sau đó sao lưu) trước khi chúng ta bắt đầu di chuyển các dòng xung quanh:jjj3k

Như vậy:

ggqqqqqjjj3kddjjpkddkkPjddpjj@qq@q

Tại sao bạn rõ ràng xóa đăng ký q? Nếu bạn chỉ ghi vào đó, bất cứ thứ gì trong đó cũng sẽ bị ghi đè ....
Wildcard

4
@Wildcard Bởi vì đó là một macro đệ quy, lần đầu tiên bạn gọi nội dung đăng ký q, nó phải trống, nếu không nó sẽ làm rối các phiên bản của bạn.
saginaw

Rất đẹp. Tôi đã thử điều này một cách tương tác mà không cố gắng nhập chuỗi chính xác của lệnh của bạn và sử dụng:qqqggqqjjjkddkkPjddjpkddkkPjjjj@qq@q
Wildcard

1
@ablewasiereisawelba where I can just use the one-line custom command each time I need to do this- lưu ý rằng bạn không phải tạo lại macro mỗi khi bạn cần sử dụng nó, vì có thể lưu trữ nó dưới dạng hàm / lệnh trên vimrc của bạn.
mMontu

1
@ablewasiereisawelba Tôi rất vui vì bạn thấy "Chỉnh sửa" hữu ích! Vì đây là bài đăng đầu tiên của bạn ở đây nên có thể bạn không biết về cách thông báo StackExchange hoạt động: khi bạn đăng bình luận, tác giả của câu trả lời / câu hỏi nhận được thông báo; những người khác sẽ chỉ nhận được thông báo nếu bạn bao gồm @ <nick>. Do đó, chỉ Rich nhận được thông báo về tin nhắn cuối cùng của bạn.
mMontu

7

Bạn cũng có thể làm điều này với một Exlệnh sử dụng sednhư một bộ lọc bên ngoài:

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

Phiên bản này sẽ bỏ qua (xóa) bất kỳ dòng bổ sung nào ngoài bội số 4. Để giữ bộ cuối cùng dưới 4 dòng (đảo ngược), sử dụng:

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

%đây có nghĩa là "Mỗi dòng trong bộ đệm."

Các !phương tiện lệnh "Chạy lệnh sau đây với các dòng quy định như đầu vào, và thay thế các dòng chỉ định với đầu ra của lệnh." (Nó được gọi là bộ lọc; rất tiện cho những việc như sắp xếp, ví dụ: :%!sortsẽ sắp xếp tất cả các dòng trong tệp của bạn; :2,8!sortsẽ sắp xếp các dòng 2-8, v.v.)

sedlà công cụ chỉnh sửa luồng và được tìm thấy trên tất cả các hệ thống giống Unix. Các khái niệm chính sedđược sử dụng ở đây là "không gian mẫu" (theo mặc định chỉ lần lượt chứa từng dòng đầu vào) và "giữ không gian" (là nơi bạn có thể dán thêm văn bản trong khi sử dụng sedđể lưu nó trong khi xử lý khác dòng đầu vào).

-nlà một tùy chọn cho sedlệnh để loại bỏ các hành động mặc định của nó là in không gian mẫu (vì trong trường hợp này chúng tôi chỉ muốn in khi chúng tôi nói rõ ràng như vậy.)

$ptrong sedlệnh có nghĩa là "Nếu bạn đang ở dòng cuối cùng của sedđầu vào, hãy in (không gian mẫu)."

h có nghĩa là "dán nội dung hiện tại của 'không gian mẫu' vào 'không gian giữ', ghi đè lên bất cứ thứ gì ở đó."

n có nghĩa là "thay thế nội dung của 'không gian mẫu' bằng dòng tiếp theo từ đầu vào."

G có nghĩa là "nối vào 'không gian mẫu': một dòng mới theo sau là nội dung của 'không gian giữ'."

Kết hợp tất cả lại với nhau, sedlệnh lưu trữ bốn dòng đầu ra, đảo ngược chúng khi nó lưu trữ chúng, và sau đó in chúng. Các $plệnh được thêm vào trong phiên bản thứ hai đảm bảo rằng nếu đạt đến dòng cuối cùng của tệp khác với nhiều dòng 4 thì các dòng vẫn được in.


Đối với phương pháp tương tác thay thế, vẫn không sử dụng bất kỳ tính năng cụ thể nào của Vim và cũng không sử dụng bộ lọc bên ngoài:

:4

để đi đến dòng thứ tư.

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

để đảo ngược bốn dòng trước (1-4) và để con trỏ của bạn trên dòng 8.

.m -4di chuyển dòng hiện tại đến ngay sau dòng bốn dòng trở lại (để lại con trỏ trên dòng di chuyển).

+3m .di chuyển dòng có 3 dòng sau dòng hiện tại, ngay sau dòng hiện tại, để con trỏ trên dòng di chuyển. +2m .Tất nhiên hoạt động như nhau.

+5 đặt con trỏ năm dòng xuống từ vị trí của nó.

Lặp lại như mong muốn.

Trong Vim bạn có thể lặp lại toàn bộ lệnh này với @:, sau đó lặp lại lần nữa với @@. Trong POSIX vihoặc exbạn sẽ cần chèn :.m -4 | +3m . | +2m . | +5 dưới dạng một dòng văn bản, xóa nó vào một bộ đệm có tên (thanh ghi), và sau đó thực hiện bộ đệm có tên (thanh ghi) đó.

Vì vậy, trong exchế độ, chỉ đảo ngược các dòng tương tác chỉ sử dụng các tính năng do POSIX chỉ định và bắt đầu bằng 17 dòng văn bản:

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

Đọc thêm:


Bạn có thể giải thích thêm một chút giải pháp của bạn?
vappolinario

3
@vappolinario, tôi đã thêm một số giải thích. cái đó có giúp ích không? :)
tự đại diện

Wow, câu trả lời rất dài. :) Tôi thực sự có sed, nhưng tôi chỉ sử dụng nó với một tập lệnh đơn giản mà tôi tìm thấy ở đâu đó - có lẽ trên bảng này - đó là một giải pháp cho một vấn đề tôi gặp phải. Tuy nhiên, khi tôi cố chạy dòng đề xuất của bạn, nó nói "'sed' không được nhận dạng là một lệnh nội bộ hoặc bên ngoài, chương trình có thể hoạt động hoặc tệp bó." Tôi không chắc mình có cần sed trong thư mục vim hay không.
couldwasiereisawelba

1
@ablewasiereisawelba, nếu bạn đang chạy trên hộp Windows và không sử dụng Cygwin (hoặc MobaXterm hoặc tương tự), bạn có thể thử phương pháp khác tôi đã đưa ra: 4G:.m -4 | +3m . | +2m . | +5<Enter>@:sau đó @@lặp lại cho đến khi tệp của bạn được xử lý hoàn toàn. Tuy nhiên, tôi khuyên bạn nên cài đặt MobaXterm. :)
tự đại diện

@Wildcard Ồ được rồi, tôi đang kiểm tra MobaXterm ngay bây giờ. :)
couldwasiereisawelba

7
:g/^/exe 'm .-' . substitute(line('.') % 4, '^0$', '4', '')

Tiếng Anh đơn giản: Đối với mỗi dòng, di chuyển dòng hiện tại lên lnum % 4, trừ khi lnum % 4 == 0, trong trường hợp đó, di chuyển dòng hiện tại lên 4.

Ngoài ra, để đảo ngược mọi ndòng, thay thế các 4lệnh trong lệnh trên bằng n.


2
Lệnh một dòng rất đẹp. Cảm ơn bạn, djjcast.
couldwasiereisawelba 2/2/2016
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.