Vấn đề của bạn với Vim là bạn không mò mẫm vi .
Bạn đề cập đến việc cắt yy
và phàn nàn rằng bạn gần như không bao giờ muốn cắt toàn bộ dòng. Trong các lập trình viên thực tế, chỉnh sửa mã nguồn, rất thường muốn làm việc trên toàn bộ dòng, phạm vi của dòng và khối mã. Tuy nhiên, yy
chỉ có một trong nhiều cách để đưa văn bản vào bộ đệm sao chép ẩn danh (hoặc "đăng ký" như được gọi trong vi ).
"Zen" của vi là bạn đang nói một ngôn ngữ. Ban đầu y
là một động từ. Các tuyên bố yy
là một từ đồng nghĩa cho y_
. Các y
được tăng gấp đôi lên để làm cho nó dễ dàng hơn để gõ, vì nó là một hoạt động phổ biến như vậy.
Điều này cũng có thể được thể hiện dưới dạng dd
P
(xóa dòng hiện tại và dán một bản sao vào vị trí; để lại một bản sao trong sổ đăng ký ẩn danh làm tác dụng phụ). Các y
và d
"động từ" lấy bất kỳ chuyển động như họ "đối tượng". Do đó, yW
"yank từ đây (con trỏ) đến cuối từ hiện tại / tiếp theo (lớn)" và y'a
là "yank từ đây đến dòng chứa dấu có tên ' a '."
Nếu bạn chỉ hiểu các chuyển động con trỏ lên, xuống, trái và phải cơ bản thì vi sẽ không hiệu quả hơn một bản sao "notepad" cho bạn. (Được rồi, bạn vẫn sẽ làm nổi bật cú pháp và khả năng xử lý các tệp lớn hơn mức trung bình ~ 45KB hoặc hơn; nhưng làm việc với tôi ở đây).
vi có 26 "điểm" và 26 "đăng ký." Một dấu được đặt thành bất kỳ vị trí con trỏ bằng m
lệnh. Mỗi dấu được chỉ định bởi một chữ cái viết thường. Do đó, ma
đặt dấu ' a ' cho vị trí hiện tại và mz
đặt dấu ' z '. Bạn có thể di chuyển đến dòng chứa một dấu bằng cách sử dụng lệnh '
(trích dẫn đơn). Do đó, 'a
di chuyển đến đầu dòng chứa dấu ' a '. Bạn có thể di chuyển đến vị trí chính xác của bất kỳ nhãn hiệu nào bằng lệnh `
(backquote). Do đó, `z
sẽ di chuyển trực tiếp đến vị trí chính xác của dấu ' z '.
Bởi vì đây là những "phong trào", chúng cũng có thể được sử dụng làm chủ đề cho các "tuyên bố" khác.
Vì vậy, một cách để cắt một lựa chọn văn bản tùy ý sẽ là bỏ một dấu (tôi thường sử dụng ' a ' làm dấu "đầu tiên" của mình, ' z ' làm dấu tiếp theo của tôi, ' b ' như một dấu khác và ' e ' là còn một điều nữa (tôi không nhớ là đã từng sử dụng tương tác hơn bốn nhãn hiệu trong 15 năm sử dụng vi ; người ta tạo ra các quy ước riêng về cách các nhãn hiệu và thanh ghi được sử dụng bởi các macro không làm phiền bối cảnh tương tác của một người). đến đầu kia của văn bản mong muốn của chúng tôi, chúng tôi có thể bắt đầu ở một trong hai đầu, điều đó không thành vấn đề. Sau đó, chúng tôi chỉ đơn giản có thể sử dụng d`a
để cắt hoặc y`a
sao chép. Do đó, toàn bộ quá trình có 5 lần nhấn phím (sáu nếu chúng tôi bắt đầu "chèn "chế độ và cần thiết đểEscra chế độ lệnh). Khi chúng tôi đã cắt hoặc sao chép rồi dán vào một bản sao là một lần nhấn phím : p
.
Tôi nói rằng đây là một cách để cắt hoặc sao chép văn bản. Tuy nhiên, nó chỉ là một trong số nhiều. Thường thì chúng ta có thể mô tả ngắn gọn hơn phạm vi văn bản mà không cần di chuyển con trỏ xung quanh và đánh dấu. Ví dụ: nếu tôi ở trong một đoạn văn bản, tôi có thể sử dụng {
và }
di chuyển đến đầu hoặc cuối đoạn tương ứng. Vì vậy, để di chuyển một đoạn văn bản tôi đã cắt nó bằng cách sử dụng {
d}
(3 tổ hợp phím). (Nếu tôi tình cờ đã ở dòng đầu tiên hoặc cuối cùng của đoạn thì tôi có thể chỉ cần sử dụng d}
hoặc d{
tương ứng.
Khái niệm "đoạn" mặc định cho một cái gì đó thường hợp lý theo trực giác. Do đó, nó thường hoạt động cho mã cũng như văn xuôi.
Chúng tôi thường biết một số mẫu (biểu thức chính quy) đánh dấu một đầu này hoặc đầu kia của văn bản mà chúng tôi quan tâm. Tìm kiếm tiến hoặc lùi là các chuyển động trong vi . Do đó, chúng cũng có thể được sử dụng làm "chủ thể" trong "tuyên bố" của chúng tôi. Vì vậy, tôi có thể sử dụng d/foo
để cắt từ dòng hiện tại sang dòng tiếp theo chứa chuỗi "foo" và y?bar
sao chép từ dòng hiện tại sang dòng gần đây nhất (trước đó) có chứa "thanh". Nếu tôi không muốn toàn bộ dòng, tôi vẫn có thể sử dụng các chuyển động tìm kiếm (dưới dạng câu lệnh của riêng họ), bỏ dấu (s) của tôi và sử dụng các `x
lệnh như được mô tả trước đây.
Ngoài "động từ" và "chủ ngữ" vi còn có "đối tượng" (theo nghĩa ngữ pháp của thuật ngữ này). Cho đến nay tôi chỉ mô tả việc sử dụng đăng ký ẩn danh. Tuy nhiên, tôi có thể sử dụng bất kỳ trong số 26 thanh ghi "có tên" bằng cách thêm tiền tố vào tham chiếu "đối tượng" với "
(công cụ sửa đổi trích dẫn kép). Do đó, nếu tôi sử dụng, "add
tôi sẽ cắt dòng hiện tại vào thanh ghi ' a ' và nếu tôi sử dụng "by/foo
thì tôi sẽ lấy một bản sao của văn bản từ đây sang dòng tiếp theo có chứa "foo" vào thanh ghi ' b '. Để dán từ một thanh ghi, tôi chỉ cần tiền tố dán với trình tự sửa đổi tương tự: "ap
dán một bản sao của ' a ' đăng ký '"bP
dán một bản sao từ ' b ' đến trước dòng hiện tại.
Khái niệm "tiền tố" này cũng thêm các từ tương tự của "tính từ" ngữ pháp và "trạng từ" vào ngôn ngữ thao tác văn bản của chúng tôi. "Hầu hết các lệnh (động từ) và chuyển động (động từ hoặc đối tượng, tùy theo ngữ cảnh) cũng có thể lấy tiền tố số. 3J
có nghĩa là "tham gia ba dòng tiếp theo" và d5}
có nghĩa là "xóa khỏi dòng hiện tại cho đến hết đoạn thứ năm trở xuống từ đây."
Đây là tất cả các cấp trung cấp vi . Không ai trong số đó là Vim cụ thể và có nhiều thủ thuật nâng cao hơn nhiều trong vi nếu bạn sẵn sàng tìm hiểu chúng. Nếu bạn chỉ thành thạo các khái niệm trung gian này thì có lẽ bạn sẽ thấy rằng bạn hiếm khi cần phải viết bất kỳ macro nào vì ngôn ngữ thao tác văn bản đủ ngắn gọn và biểu cảm để thực hiện hầu hết mọi thứ một cách dễ dàng bằng ngôn ngữ "bản địa" của trình soạn thảo.
Một mẫu của các thủ thuật nâng cao hơn:
Có một số :
lệnh, đáng chú ý nhất là :% s/foo/bar/g
kỹ thuật thay thế toàn cầu. (Đó không phải là nâng cao nhưng các :
lệnh khác có thể được). Toàn bộ :
các lệnh đã được kế thừa trong lịch sử bởi các hóa thân trước đó của vi là ed (trình chỉnh sửa dòng) và sau đó là các tiện ích cũ (trình soạn thảo dòng mở rộng). Trong thực tế vi được đặt tên như vậy bởi vì đó là giao diện trực quan để cũ .
:
các lệnh thường hoạt động trên các dòng văn bản. ed và ex đã được viết trong một kỷ nguyên khi màn hình thiết bị đầu cuối không phổ biến và nhiều thiết bị đầu cuối là thiết bị "teletype" (TTY). Vì vậy, thông thường là làm việc từ các bản sao in của văn bản, sử dụng các lệnh thông qua giao diện cực kỳ ngắn gọn (tốc độ kết nối phổ biến là 110 baud, hoặc, khoảng 11 ký tự mỗi giây - chậm hơn so với một người đánh máy nhanh; phiên tương tác nhiều người dùng, ngoài ra thường có một số động lực để bảo tồn giấy).
Vì vậy, cú pháp của hầu hết :
các lệnh bao gồm một địa chỉ hoặc phạm vi địa chỉ (số dòng) theo sau là một lệnh. Đương nhiên, người ta có thể sử dụng số dòng theo nghĩa đen: :127,215 s/foo/bar
để thay đổi lần xuất hiện đầu tiên của "foo" thành "thanh" trên mỗi dòng giữa 127 và 215. Người ta cũng có thể sử dụng một số chữ viết tắt như .
hoặc $
cho dòng hiện tại và dòng cuối cùng. Người ta cũng có thể sử dụng các tiền tố tương đối +
và -
để tham chiếu đến các độ lệch sau hoặc trước dòng curent tương ứng. Do đó: :.,$j
có nghĩa là "từ dòng hiện tại đến dòng cuối cùng, nối tất cả chúng thành một dòng". :%
đồng nghĩa với :1,$
(tất cả các dòng).
Các lệnh :... g
và :... v
chịu một số lời giải thích vì chúng rất mạnh mẽ. :... g
là tiền tố cho "toàn cầu" áp dụng lệnh tiếp theo cho tất cả các dòng khớp với mẫu (biểu thức chính quy) trong khi :... v
áp dụng lệnh đó cho tất cả các dòng KHÔNG khớp với mẫu đã cho ("v" từ "conVerse"). Cũng như các lệnh ex khác, chúng có thể được thêm tiền tố bằng cách tham chiếu địa chỉ / phạm vi. Do đó, :.,+21g/foo/d
có nghĩa là "xóa bất kỳ dòng nào chứa chuỗi" foo "khỏi dòng hiện tại qua 21 dòng tiếp theo" trong khi đó :.,$v/bar/d
có nghĩa là "từ đây đến cuối tệp, xóa bất kỳ dòng nào KHÔNG chứa chuỗi" thanh ".
Thật thú vị khi lệnh grep Unix phổ biến thực sự được lấy cảm hứng từ lệnh ex này (và được đặt tên theo cách mà nó được ghi lại). Các cựu lệnh :g/re/p
(grep) là cách họ ghi nhận cách "toàn cầu" "in" các dòng có chứa một "biểu thức chính quy" (tái). Khi ed và ex được sử dụng, :p
lệnh là một trong những lệnh đầu tiên mà bất kỳ ai cũng học và thường là lệnh đầu tiên được sử dụng khi chỉnh sửa bất kỳ tệp nào. Đó là cách bạn in nội dung hiện tại (thường chỉ có một trang đầy đủ tại một thời điểm sử dụng :.,+25p
hoặc một số nội dung như vậy).
Lưu ý rằng :% g/.../d
hoặc (đối tác reVerse / conVerse của nó: :% v/.../d
là các mẫu sử dụng phổ biến nhất. Tuy nhiên, có một số ex
lệnh khác đáng ghi nhớ:
Chúng ta có thể sử dụng m
để di chuyển các dòng xung quanh và j
để nối các dòng. Ví dụ: nếu bạn có một danh sách và bạn muốn tách tất cả các công cụ khớp (hoặc ngược lại KHÔNG khớp với một số mẫu) mà không xóa chúng, thì bạn có thể sử dụng một cái gì đó như: :% g/foo/m$
... và tất cả các dòng "foo" sẽ được chuyển đến cuối tập tin (Lưu ý mẹo khác về việc sử dụng phần cuối của tệp làm khoảng trống đầu). Điều này sẽ bảo toàn thứ tự tương đối của tất cả các dòng "foo" trong khi đã trích xuất chúng từ phần còn lại của danh sách. (Điều này sẽ tương đương với việc thực hiện một cái gì đó như: 1G!GGmap!Ggrep foo<ENTER>1G:1,'a g/foo'/d
(sao chép tệp vào đuôi của chính nó, lọc phần đuôi qua grep
và xóa tất cả nội dung khỏi phần đầu).
Để tham gia các dòng thường tôi có thể tìm một mẫu cho tất cả các dòng cần được nối với người tiền nhiệm của chúng (ví dụ: tất cả các dòng bắt đầu bằng "^" thay vì "^ *" trong một số danh sách dấu đầu dòng). Trong trường hợp đó tôi sẽ sử dụng: :% g/^ /-1j
(đối với mỗi dòng phù hợp, hãy đi lên một dòng và tham gia chúng). (BTW: cho các danh sách đạn cố gắng để tìm kiếm các đường đạn và tham gia tiếp theo không làm việc cho một vài lý do ... nó có thể tham gia vào một đường đạn khác, và nó sẽ không tham gia bất kỳ dòng đạn để tất cả các sự tiếp nối của nó, nó sẽ chỉ hoạt động theo cặp trên các trận đấu).
Hầu như không cần đề cập đến bạn có thể sử dụng người bạn cũ s
(thay thế) của chúng tôi với các lệnh g
và v
(toàn cầu / converse-global). Thông thường bạn không cần phải làm như vậy. Tuy nhiên, hãy xem xét một số trường hợp bạn chỉ muốn thực hiện thay thế trên các dòng khớp với một số mẫu khác. Thường thì bạn có thể sử dụng một mẫu phức tạp với các ảnh chụp và sử dụng các tham chiếu trở lại để giữ các phần của các dòng mà bạn không muốn thay đổi. Tuy nhiên, thường sẽ dễ dàng hơn để tách trận đấu khỏi sự thay thế: :% g/foo/s/bar/zzz/g
- cho mỗi dòng có chứa "foo" thay thế tất cả "thanh" bằng "zzz". (Cái gì đó như:% s/\(.*foo.*\)bar\(.*\)/\1zzz\2/g
sẽ chỉ hoạt động đối với các trường hợp "bar" được PRECEDED bởi "foo" trên cùng một dòng; nó đã quá vô duyên rồi, và sẽ phải đọc thêm để nắm bắt tất cả các trường hợp "thanh" trước "foo")
Vấn đề là có nhiều hơn chỉ là p
, s
và d
dòng trong ex
bộ lệnh.
Các :
địa chỉ cũng có thể tham khảo nhãn hiệu. Do đó, bạn có thể sử dụng: :'a,'bg/foo/j
để nối bất kỳ dòng nào chứa chuỗi foo đến dòng tiếp theo của nó, nếu nó nằm giữa các dòng giữa các dấu ' a ' và ' b '. (Có, tất cả các ex
ví dụ lệnh trước có thể được giới hạn ở các tập hợp con của dòng tệp bằng cách thêm tiền tố với các loại biểu thức địa chỉ này).
Điều đó khá mơ hồ (Tôi chỉ sử dụng một vài thứ như thế một vài lần trong 15 năm qua). Tuy nhiên, tôi sẽ tự do thừa nhận rằng tôi thường làm mọi việc lặp đi lặp lại và tương tác mà có lẽ có thể được thực hiện hiệu quả hơn nếu tôi dành thời gian để nghĩ ra câu thần chú chính xác.
Một lệnh vi hoặc ex rất hữu ích khác là :r
đọc nội dung của tệp khác. Do đó: :r foo
chèn nội dung của tệp có tên "foo" vào dòng hiện tại.
Mạnh mẽ hơn là :r!
mệnh lệnh. Điều này đọc kết quả của một lệnh. Nó giống như tạm dừng phiên vi , chạy lệnh, chuyển hướng đầu ra của nó sang tệp tạm thời, tiếp tục phiên vi của bạn và đọc nội dung từ temp. tập tin.
Mạnh hơn nữa là các lệnh !
(bang) và :... !
( ex bang). Chúng cũng thực hiện các lệnh bên ngoài và đọc kết quả vào văn bản hiện tại. Tuy nhiên, họ cũng lọc các lựa chọn văn bản của chúng tôi thông qua lệnh! Điều này chúng ta có thể sắp xếp tất cả các dòng trong tệp của mình bằng cách sử dụng 1G!Gsort
( G
là lệnh vi "goto"; nó mặc định đi đến dòng cuối cùng của tệp, nhưng có thể được thêm tiền tố bởi một số dòng, chẳng hạn như 1, dòng đầu tiên). Điều này tương đương với biến thể cũ:1,$!sort
. Nhà văn thường sử dụng !
với các tiện ích fmt hoặc gấp của Unix để cải cách hoặc lựa chọn văn bản "gói từ". Một macro rất phổ biến là{!}fmt
(định dạng lại đoạn hiện tại). Các lập trình viên đôi khi sử dụng nó để chạy mã của họ, hoặc chỉ một phần của nó, thông qua thụt lề hoặc các công cụ định dạng lại mã khác.
Sử dụng các lệnh :r!
và !
có nghĩa là bất kỳ tiện ích hoặc bộ lọc bên ngoài nào cũng có thể được coi là một phần mở rộng của trình soạn thảo của chúng tôi. Thỉnh thoảng tôi đã sử dụng chúng với các tập lệnh lấy dữ liệu từ cơ sở dữ liệu hoặc với các lệnh wget hoặc lynx đã kéo dữ liệu khỏi trang web hoặc các lệnh ssh lấy dữ liệu từ các hệ thống từ xa.
Một lệnh ex hữu ích khác là :so
(viết tắt của :source
). Điều này đọc nội dung của một tập tin như một chuỗi các lệnh. Khi bạn khởi động vi nó một cách bình thường, ngầm, thực hiện một tệp :source
trên ~/.exinitrc
(và Vim thường làm điều này trên ~/.vimrc
, đủ tự nhiên). Việc sử dụng này là bạn có thể thay đổi hồ sơ trình soạn thảo của mình một cách đơn giản bằng cách tìm nguồn cung ứng trong một tập hợp các macro, chữ viết tắt và cài đặt trình soạn thảo mới. Nếu bạn lén lút, bạn thậm chí có thể sử dụng điều này như một mẹo để lưu trữ các chuỗi lệnh chỉnh sửa cũ để áp dụng cho các tệp theo yêu cầu.
Ví dụ: tôi có một tệp bảy dòng (36 ký tự) chạy tệp qua wc và chèn một nhận xét kiểu C ở đầu tệp chứa dữ liệu đếm từ đó. Tôi có thể áp dụng "macro" đó cho một tệp bằng cách sử dụng một lệnh như:vim +'so mymacro.ex' ./mytarget
( +
Tùy chọn dòng lệnh cho vi và Vim thường được sử dụng để bắt đầu phiên chỉnh sửa tại một số dòng nhất định. Tuy nhiên, có một thực tế ít người biết là có thể theo +
bất kỳ lệnh / biểu thức ex hợp lệ nào , chẳng hạn như lệnh "nguồn" như Tôi đã thực hiện ở đây, với một ví dụ đơn giản, tôi có các tập lệnh gọi: vi +'/foo/d|wq!' ~/.ssh/known_hosts
để xóa một mục khỏi tệp lưu trữ SSH đã biết của tôi không tương tác trong khi tôi đang tạo lại một bộ máy chủ).
Thông thường việc viết "macro" như vậy dễ dàng hơn nhiều khi sử dụng Perl, AWK, sed (trên thực tế, giống như grep một tiện ích được lấy cảm hứng từ lệnh ed ).
Các @
lệnh có lẽ là mơ hồ nhất vi lệnh. Thỉnh thoảng giảng dạy các khóa quản trị hệ thống nâng cao trong gần một thập kỷ, tôi đã gặp rất ít người đã từng sử dụng nó. @
thực thi nội dung của một thanh ghi như thể đó là lệnh vi hoặc ex .
Ví dụ: Tôi thường sử dụng: :r!locate ...
để tìm một số tệp trên hệ thống của mình và đọc tên của nó vào tài liệu của tôi. Từ đó tôi xóa mọi lượt truy cập không liên quan, chỉ để lại toàn bộ đường dẫn đến tệp mà tôi quan tâm. Thay vì tốn nhiều công sức Tabqua từng thành phần của đường dẫn (hoặc tệ hơn, nếu tôi tình cờ bị kẹt trên máy mà không hỗ trợ hoàn thành Tab trong bản sao của vi ) Tôi chỉ sử dụng:
0i:r
(để biến dòng hiện tại thành một lệnh hợp lệ : r ),
"cdd
(để xóa dòng vào thanh ghi "c") và
@c
thực hiện lệnh đó.
Đó chỉ là 10 lần nhấn phím (và biểu thức thực sự "cdd
@c
là một macro ngón tay đối với tôi, vì vậy tôi có thể gõ nó gần như nhanh như bất kỳ từ sáu chữ cái thông thường nào).
Một suy nghĩ tỉnh táo
Tôi mới chỉ tìm hiểu về sức mạnh của vi và không có gì tôi mô tả ở đây thậm chí là một phần của "cải tiến" mà vim được đặt tên! Tất cả những gì tôi đã mô tả ở đây sẽ hoạt động trên mọi bản sao cũ của vi từ 20 hoặc 30 năm trước.
Có những người đã sử dụng sức mạnh của vi nhiều hơn đáng kể so với tôi.