Làm thế nào để vim thuyền viết với thủ thuật sudo xông hoạt động?


1412

Nhiều người trong số các bạn có thể đã thấy lệnh cho phép bạn viết trên một tệp cần có quyền root, ngay cả khi bạn quên mở vim bằng sudo:

:w !sudo tee %

Vấn đề là tôi không hiểu chính xác những gì đang xảy ra ở đây.

Tôi đã tìm ra điều này: wlà cho điều này

                                                        *:w_c* *:write_c*
:[range]w[rite] [++opt] !{cmd}
                        Execute {cmd} with [range] lines as standard input
                        (note the space in front of the '!').  {cmd} is
                        executed like with ":!{cmd}", any '!' is replaced with
                        the previous command |:!|.

vì vậy nó vượt qua tất cả các dòng như đầu vào tiêu chuẩn.

Phần !sudo teegọi teevới đặc quyền quản trị viên.

Để tất cả có ý nghĩa, %nên xuất tên tệp (làm tham số cho tee), nhưng tôi không thể tìm thấy tài liệu tham khảo về trợ giúp cho hành vi này.

tl; dr ai đó có thể giúp tôi mổ xẻ lệnh này không?


5
@Nathan: Sẽ :w !sudo cat > %không hoạt động tốt, và không gây ô nhiễm đầu ra tiêu chuẩn?
Bjarke Freund-Hansen

51
@bjarkef - không, điều đó không hiệu quả. Trong trường hợp đó, sudođược áp dụng cho cat, nhưng không được >, vì vậy nó không được phép. Bạn có thể thử chạy toàn bộ lệnh trong một lớp con sudo, như :w !sudo sh -c "cat % > yams.txt", nhưng điều đó cũng không hoạt động, bởi vì trong lớp con, %là con số không; bạn sẽ bỏ trống nội dung của tập tin của bạn.
Nathan Long

3
Tôi chỉ muốn thêm rằng sau khi gõ lệnh đó, một thông báo cảnh báo có thể xuất hiện. Nếu vậy, nhấn L. Sau đó, bạn sẽ được yêu cầu nhấn enter. Làm và cuối cùng bạn sẽ có tập tin của bạn được lưu.
pottaofiumara

9
@NathanLong @knittl: :w !sudo sh -c "cat >%"thực sự hoạt động tốt như là sudo tee %vì Vim thay thế tên tệp cho %trước khi nó được chuyển sang subshell. Tuy nhiên, không ai trong số họ làm việc nếu tên tệp có khoảng trắng trong đó; bạn phải làm :w !sudo sh -c "cat >'%'"hoặc :w !sudo tee "%"để sửa nó.
Han Seoul-Oh

1
Lưu bằng cách sử dụng: W và tải lại tệp: lệnh W: thực hiện ': im lặng w! Sudo tee%> / dev / null' | :biên tập!
Diego Roberto Dos Santos

Câu trả lời:


1612

Trong :w !sudo tee %...

% có nghĩa là "tập tin hiện tại"

Như eugene y đã chỉ ra , %thực sự có nghĩa là "tên tệp hiện tại", được truyền cho teenó để biết tệp nào sẽ ghi đè lên.

(Trong lệnh thay thế, nó hơi khác nhau, như :help :%các chương trình, đó là equal to 1,$ (the entire file)(nhờ @Orafu để chỉ ra rằng điều này không đánh giá vào tên tập tin) Ví dụ. :%s/foo/barNghĩa là " trong file hiện hành , thay thế xuất hiện của foovới bar." Nếu bạn nổi bật một số văn bản trước khi nhập :s, bạn sẽ thấy các dòng được tô sáng %thay thế cho phạm vi thay thế của bạn.)

:w không cập nhật tập tin của bạn

Một phần khó hiểu của thủ thuật này là bạn có thể nghĩ :wlà sửa đổi tệp của mình, nhưng thực tế không phải vậy. Nếu bạn mở và sửa đổi file1.txt, sau đó chạy :w file2.txt, nó sẽ là "lưu dưới dạng"; file1.txtsẽ không được sửa đổi, nhưng nội dung bộ đệm hiện tại sẽ được gửi đến file2.txt.

Thay vì file2.txt , bạn có thể thay thế một lệnh shell để nhận nội dung bộ đệm . Ví dụ, :w !catsẽ chỉ hiển thị nội dung.

Nếu Vim không chạy với quyền truy cập sudo, nó :wkhông thể sửa đổi tệp được bảo vệ, nhưng nếu nó chuyển nội dung bộ đệm vào trình bao, một lệnh trong trình bao có thể được chạy bằng sudo . Trong trường hợp này, chúng tôi sử dụng tee.

Hiểu về tee

Đối với tee, hình ảnhtee lệnh dưới dạng ống hình chữ T trong tình huống đường ống bash bình thường: nó hướng đầu ra đến (các) tệp được chỉ định và cũng gửi nó đến đầu ra tiêu chuẩn , có thể được bắt bằng lệnh đường ống tiếp theo.

Ví dụ, trong ps -ax | tee processes.txt | grep 'foo', danh sách các quy trình sẽ được ghi vào một tệp văn bản được chuyển qua grep.

     +-----------+    tee     +------------+
     |           |  --------  |            |
     | ps -ax    |  --------  | grep 'foo' |
     |           |     ||     |            |
     +-----------+     ||     +------------+
                       ||   
               +---------------+
               |               |
               | processes.txt |
               |               |
               +---------------+

(Sơ đồ được tạo bằng Asciiflow .)

Xem teetrang người đàn ông để biết thêm.

Tee như một hack

Trong tình huống câu hỏi của bạn mô tả, sử dụng teelà một hack vì chúng tôi bỏ qua một nửa những gì nó làm . sudo teeghi vào tệp của chúng tôi và cũng gửi nội dung bộ đệm đến đầu ra tiêu chuẩn, nhưng chúng tôi bỏ qua đầu ra tiêu chuẩn . Chúng ta không cần phải chuyển bất cứ điều gì cho một lệnh đường ống khác trong trường hợp này; chúng tôi chỉ sử dụng teenhư một cách khác để viết một tập tin và để chúng tôi có thể gọi nó với sudo.

Làm cho thủ thuật này dễ dàng

Bạn có thể thêm phần này vào .vimrcđể làm cho thủ thuật này dễ sử dụng: chỉ cần gõ :w!!.

" Allow saving of files as sudo when I forgot to start vim using sudo.
cmap w!! w !sudo tee > /dev/null %

Các > /dev/nullphần dứt khoát ném đi những đầu ra tiêu chuẩn, vì, như tôi đã nói, chúng tôi không cần phải vượt qua bất cứ điều gì khác lệnh máy.


147
Đặc biệt như ký hiệu của bạn "w !!" thật dễ nhớ sau khi sử dụng "sudo !!" trên dòng lệnh.
Aidan Kane

11
Vì vậy, điều này sử dụng teecho khả năng của nó để ghi stdin vào một tập tin. Tôi ngạc nhiên không có chương trình nào có nhiệm vụ phải làm điều đó (Tôi đã tìm thấy một chương trình mà tôi chưa bao giờ nghe gọi là chương trình spongenày). Tôi đoán việc "ghi một luồng vào một tệp" điển hình được thực hiện bởi một trình bao có sẵn. Có phải Vim !{cmd}không ngã ba vỏ ( cmdthay vào đó)? Có lẽ một cái gì đó rõ ràng hơn sẽ là sử dụng một số biến thể làm việc sh -c ">"hơn là tee.
Steven Lu

2
@Steven Lu: spongelà một phần của moreutilsgói trên hầu hết các bản phân phối ngoại trừ các bản phân phối dựa trên Debian. moreutilscó một số công cụ khá đẹp tương đương với các công cụ phổ biến hơn như xargstee.
Thụy Sĩ

10
Làm thế nào để mở rộng bí danh này để bảo vim tự động tải nội dung tệp đã thay đổi vào bộ đệm hiện tại? Nó hỏi tôi về nó, làm thế nào để tự động hóa nó?
Zlatko

10
@ user247077: Trong trường hợp đó, catchạy bằng root và đầu ra được chuyển hướng bởi shell, không chạy dưới quyền root. Nó giống như echo hi > /read/only/file.
WhyNotHugo

99

Trong dòng lệnh được thực hiện, %là viết tắt của tên tệp hiện tại . Điều này được ghi lại trong :help cmdline-special:

In Ex commands, at places where a file name can be used, the following
characters have a special meaning.
        %       Is replaced with the current file name.

Như bạn đã tìm ra, :w !cmdchuyển nội dung của bộ đệm hiện tại sang một lệnh khác. Điều gì teelàm là sao chép đầu vào tiêu chuẩn vào một hoặc nhiều tệp và cả đầu ra tiêu chuẩn. Do đó, :w !sudo tee % > /dev/nullghi hiệu quả nội dung của bộ đệm hiện tại vào tệp hiện tại trong khi đang root . Một lệnh khác có thể được sử dụng cho việc này là dd:

:w !sudo dd of=% > /dev/null

Là một phím tắt, bạn có thể thêm ánh xạ này vào .vimrc:

" Force saving files that require root permission 
cnoremap w!! w !sudo tee > /dev/null %

Với cách trên, bạn có thể gõ :w!!<Enter>để lưu tệp dưới dạng root.


1
Thú vị, :help _%mang đến những gì bạn đã nhập, nhưng :help %đưa ra khóa khớp nối. Tôi đã không nghĩ sẽ thử tiền tố gạch dưới, đó có phải là một kiểu mẫu trong tài liệu vim không? Có điều gì 'đặc biệt' khác để thử khi tìm kiếm sự giúp đỡ không?
David Pope

2
@David: helpLệnh nhảy đến một thẻ. Bạn có thể thấy các thẻ có sẵn với :h help-tags. Bạn cũng có thể sử dụng hoàn thành dòng lệnh để xem các thẻ phù hợp: :h cmdline<Ctrl-D>(hoặc :h cmdline<Tab>nếu bạn đặt wildmodetương ứng)
Eugene Yarmash

1
Tôi đã phải sử dụng cmap w!! w !sudo tee % > /dev/nulltrong tệp .vimrc của mình để thực hiện công việc này. Là %đặt sai trong câu trả lời ở trên? (Không có chuyên gia vim ở đây.)
dmmfll

@DMfll Vâng, đúng vậy. Lệnh trong câu trả lời sẽ dẫn đến kết quả sudo tee > /dev/null /path/to/current/filekhông thực sự có ý nghĩa. (Sẽ chỉnh sửa điều đó)
jazzpi

1
@jazzpi: Bạn đã sai. Shell không thực sự quan tâm nơi dòng lệnh bạn thực hiện chuyển hướng tệp.
Eugene Yarmash

19

Điều này cũng hoạt động tốt:

:w !sudo sh -c "cat > %"

Điều này được lấy cảm hứng từ bình luận của @Nathan Long.

THÔNG BÁO :

"phải được sử dụng thay 'vì bởi vì chúng tôi muốn %được mở rộng trước khi chuyển sang shell.


11
Trong khi điều này có thể hoạt động, nó cũng cung cấp quyền truy cập sudo cho nhiều chương trình (sh và cat). Các ví dụ khác có thể an toàn hơn bằng cách thay thế teebằng /usr/bin/teeđể ngăn chặn các cuộc tấn công sửa đổi PATH.
idbrii

18

:w - Viết một tập tin.

!sudo - Gọi lệnh shell sudo.

tee- Đầu ra của lệnh write (vim: w) được chuyển hướng bằng tee. % Không là gì ngoài tên tệp hiện tại, tức là /etc/apache2/conf.d/mediawiki.conf. Nói cách khác, lệnh tee được chạy dưới quyền root và nó lấy đầu vào tiêu chuẩn và ghi nó vào một tệp được biểu thị bằng%. Tuy nhiên, điều này sẽ nhắc tải lại tệp một lần nữa (nhấn L để tải các thay đổi trong chính vim):

liên kết hướng dẫn


9

Câu trả lời được chấp nhận bao gồm tất cả, vì vậy tôi sẽ chỉ đưa ra một ví dụ khác về lối tắt mà tôi sử dụng cho bản ghi.

Thêm nó vào etc/vim/vimrc(hoặc ~/.vimrc):

  • cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit!

Ở đâu:

  • cnoremap: nói với vim rằng phím tắt sau sẽ được liên kết trong dòng lệnh.
  • w!!: các phím tắt chính nó.
  • execute '...': một lệnh thực thi chuỗi sau.
  • silent!: chạy nó âm thầm
  • write !sudo tee % >/dev/null: câu hỏi OP, đã thêm một chuyển hướng tin nhắn NULLđể thực hiện một lệnh sạch
  • <bar> edit!: thủ thuật này là anh đào của bánh: nó cũng gọi editlệnh để tải lại bộ đệm và sau đó tránh các thông báo như bộ đệm đã thay đổi . <bar>là cách viết ký hiệu ống để tách hai lệnh ở đây.

Hy vọng nó giúp. Xem thêm cho các vấn đề khác:


im lặng! đã vô hiệu hóa lời nhắc mật khẩu để bạn không nhìn thấy nó
Andy Ray

7

Tôi muốn đề xuất một cách tiếp cận khác cho "Oups tôi quên viết sudotrong khi mở tệp của mình" :

Thay vì nhận một permission deniedvà phải gõ :w!!, tôi thấy thanh lịch hơn khi có một vimlệnh có điều kiện sudo vimnếu chủ sở hữu tệp là root.

Điều này dễ thực hiện (thậm chí có thể có các triển khai thanh lịch hơn, rõ ràng tôi không phải là một bash-guru):

function vim(){
  OWNER=$(stat -c '%U' $1)
  if [[ "$OWNER" == "root" ]]; then
    sudo /usr/bin/vim $*;
  else
    /usr/bin/vim $*;
  fi
}

Và nó hoạt động thực sự tốt.

Đây là một bashcách tiếp cận tập trung hơn mộtvim vì vậy không phải ai cũng có thể thích nó.

Tất nhiên:

  • có những trường hợp sử dụng nó sẽ thất bại (khi chủ sở hữu tệp không rootnhưng yêu cầusudo , nhưng dù sao chức năng cũng có thể được chỉnh sửa)
  • nó không có nghĩa gì khi sử dụng vimđể đọc - chỉ một tệp (theo như tôi quan tâm, tôi sử dụng tailhoặc catcho các tệp nhỏ)

Nhưng tôi thấy điều này mang lại trải nghiệm người dùng dev tốt hơn nhiều , đó là điều mà IMHO có xu hướng bị lãng quên khi sử dụng bash. :-)


5
Chỉ cần lưu ý rằng điều này là rất ít tha thứ. Cá nhân tôi, hầu hết những sai lầm của tôi là những sai lầm ngu ngốc. Vì vậy, tôi thích ngẩng cao đầu khi tôi làm điều gì đó có thể vừa ngu ngốc vừa có kết quả. Tất nhiên đây là một vấn đề ưu tiên, nhưng leo thang đặc quyền không phải là một hành động có lương tâm. Ngoài ra: Nếu bạn gặp phải điều này thường xuyên như vậy để thực hiện ": w !!" đủ bận tâm để âm thầm tự động sudo (nhưng chỉ khi chủ sở hữu = root); bạn có thể muốn kiểm tra quy trình làm việc hiện tại của bạn.
Payne

Nhận xét thú vị, mặc dù người ta sẽ có ý thức bởi vì khi tập tin đang được mở khi rootmật khẩu được truy vấn.
Augustin Riedinger

Ah! Có sự khác biệt. Nó phụ thuộc vào việc người dùng sudo có cài đặt "NOPASSWD" hay không.
Payne

Sau đó, có NOPASSWD điều ít tha thứ hơn ... :)
Augustin Riedinger

4

CHO NEOVIM

Do sự cố với các cuộc gọi tương tác ( https://github.com/neovim/neovim/issues/1716 ), tôi đang sử dụng điều này cho neovim, dựa trên câu trả lời của Tiến sĩ Beco:

cnoremap w!! execute 'silent! write !SUDO_ASKPASS=`which ssh-askpass` sudo tee % >/dev/null' <bar> edit!

Điều này sẽ mở một hộp thoại bằng cách ssh-askpassyêu cầu mật khẩu sudo.

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.