Bagaimana cara kerja trik "menulis dengan sudo"?

1412

Banyak dari Anda mungkin telah melihat perintah yang memungkinkan Anda untuk menulis pada file yang memerlukan izin root, bahkan ketika Anda lupa untuk membuka vim dengan sudo:

:w !sudo tee %

Masalahnya adalah saya tidak mengerti apa yang sebenarnya terjadi di sini.

Saya sudah memikirkan ini: wuntuk ini

                                                        *: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 |:!|.

sehingga melewati semua baris sebagai input standar.

Bagian ini !sudo teememanggil teedengan hak administrator.

Agar semua masuk akal, %harus menampilkan nama file (sebagai parameter untuk tee), tetapi saya tidak dapat menemukan referensi tentang bantuan untuk perilaku ini.

tl; dr Bisakah seseorang membantu saya membedah perintah ini?

Serupa
sumber
5
@ Nathan: Tidak akan :w !sudo cat > %bekerja juga, dan tidak mencemari output standar?
Bjarke Freund-Hansen
51
@jarkef - tidak, itu tidak berhasil. Dalam hal itu, sudoditerapkan ke cat, tetapi tidak untuk >, jadi itu tidak diperbolehkan. Anda dapat mencoba menjalankan seluruh perintah dalam sebuah subkulit sudo, seperti :w !sudo sh -c "cat % > yams.txt", tetapi itu tidak akan berhasil, karena dalam subkulit, %adalah nihil; Anda akan mengosongkan isi file Anda.
Nathan Long
3
Saya hanya ingin menambahkan bahwa setelah mengetik perintah itu, pesan peringatan mungkin muncul. Jika demikian, tekan L. Lalu, Anda akan diminta untuk menekan enter. Lakukan dan akhirnya file Anda akan disimpan.
pablofiumara
9
@NathanLong @knittl: :w !sudo sh -c "cat >%"benar-benar berfungsi dengan baik sudo tee %karena Vim mengganti nama file %sebelum ia sampai ke subkulit. Namun, tak satu pun dari mereka bekerja jika nama file memiliki spasi di dalamnya; Anda harus melakukan :w !sudo sh -c "cat >'%'"atau :w !sudo tee "%"memperbaikinya.
Han Seoul-Oh
1
Simpan menggunakan: W dan memuat kembali file: perintah W: jalankan ': silent w! Sudo tee%> / dev / null' | : edit!
Diego Roberto Dos Santos

Jawaban:

1612

Dalam :w !sudo tee %...

% berarti "file saat ini"

Seperti yang ditunjukkan eugene , %memang berarti "nama file saat ini", yang diteruskan ke teesehingga ia tahu file mana yang akan ditimpa.

(Dalam perintah substitusi, ini sedikit berbeda; seperti yang :help :%ditunjukkan, itu equal to 1,$ (the entire file)(terima kasih kepada @Orafu untuk menunjukkan bahwa ini tidak mengevaluasi ke nama file). Misalnya, :%s/foo/barberarti " dalam file saat ini , ganti kemunculan foodengan dengan bar." Jika Anda menyorot beberapa teks sebelum mengetik :s, Anda akan melihat bahwa garis yang disorot menggantikan %sebagai rentang substitusi Anda.)

:w tidak memperbarui file Anda

Salah satu bagian yang membingungkan dari trik ini adalah Anda mungkin berpikir :wmemodifikasi file Anda, tetapi sebenarnya tidak. Jika Anda membuka dan memodifikasi file1.txt, lalu berlari :w file2.txt, itu akan menjadi "save as"; file1.txttidak akan diubah, tetapi konten buffer saat ini akan dikirim ke file2.txt.

Alih-alih file2.txt, Anda bisa mengganti perintah shell untuk menerima konten buffer . Contohnya,:w !cat hanya akan menampilkan konten.

Jika Vim tidak dijalankan dengan akses sudo, itu :wtidak dapat memodifikasi file yang dilindungi, tetapi jika ia meneruskan konten buffer ke shell, perintah di shell dapat dijalankan dengan sudo . Dalam hal ini, kami menggunakantee .

Memahami tee

Adapun tee, gambar teeperintah sebagai pipa berbentuk T dalam situasi bash piping yang normal: ia mengarahkan output ke file yang ditentukan dan juga mengirimkannya ke output standar , yang dapat ditangkap oleh perintah pipa berikutnya.

Misalnya, dalam ps -ax | tee processes.txt | grep 'foo', daftar proses akan ditulis ke file teks dan diteruskan ke grep.

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

(Diagram dibuat dengan Asciiflow .)

Lihat teehalaman manual untuk info lebih lanjut.

Tee sebagai peretasan

Dalam situasi yang dijelaskan oleh pertanyaan Anda, menggunakan teeadalah peretasan karena kami mengabaikan setengah dari fungsinya . sudo teemenulis ke file kami dan juga mengirim konten buffer ke output standar, tetapi kami mengabaikan output standar . Kita tidak perlu meneruskan apa pun ke perintah pipa lain dalam kasus ini; kami hanya menggunakan teesebagai cara alternatif untuk menulis file dan sehingga kami dapat menyebutnya dengan sudo.

Membuat trik ini mudah

Anda dapat menambahkan ini ke Anda .vimrcuntuk membuat trik ini mudah digunakan: cukup ketik :w!!.

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

The > /dev/nullBagian eksplisit membuang output standar, karena, seperti yang saya katakan, kita tidak harus lulus apapun untuk perintah pipa lain.

Nathan Long
sumber
147
Terutama seperti notasi Anda "w !!" yang sangat mudah diingat setelah menggunakan "sudo !!" di baris perintah.
Aidan Kane
11
Jadi ini digunakan teekarena kemampuannya untuk menulis stdin ke file. Saya terkejut tidak ada program yang tugasnya melakukan itu (saya menemukan program yang belum pernah saya dengar spongeyang melakukan ini). Saya kira khas "tulis aliran ke file" dilakukan oleh shell built-in. Apakah Vim !{cmd}tidak membayar cangkang ( cmdsebagai gantinya)? Mungkin sesuatu yang lebih jelas adalah menggunakan beberapa varian kerja sh -c ">"daripada tee.
Steven Lu
2
@ Sebelas Lu: spongeadalah bagian dari moreutilspaket di hampir setiap distribusi kecuali distro berbasis Debian. moreutilsmemiliki beberapa alat yang cukup bagus yang setara dengan alat yang lebih umum seperti xargsdan tee.
Swiss
10
Bagaimana memperluas alias ini juga memberi tahu vim untuk secara otomatis memuat konten file yang diubah ke buffer saat ini? Ia meminta saya untuk itu, bagaimana mengotomatiskannya?
Zlatko
10
@ user247077: Dalam hal ini, catberjalan sebagai root, dan output diarahkan oleh shell, yang tidak berjalan sebagai root. Itu sama dengan echo hi > /read/only/file.
WhyNotHugo
99

Di baris perintah yang dieksekusi, %singkatan dari nama file saat ini . Ini didokumentasikan dalam :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.

Seperti yang sudah Anda ketahui, :w !cmdpipa konten buffer saat ini ke perintah lain. Apa yang teedilakukan adalah menyalin input standar ke satu atau lebih file, dan juga ke output standar. Oleh karena itu, :w !sudo tee % > /dev/nullsecara efektif menulis isi buffer saat ini ke file saat ini sambil menjadi root . Perintah lain yang dapat digunakan untuk ini adalah dd:

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

Sebagai pintasan, Anda dapat menambahkan pemetaan ini ke .vimrc:

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

Dengan di atas Anda bisa mengetik :w!!<Enter>untuk menyimpan file sebagai root.

Eugene Yarmash
sumber
1
Menarik, :help _%memunculkan apa yang Anda masukkan, tetapi :help %menampilkan kunci pencocokan penjepit. Saya tidak akan berpikir untuk mencoba awalan garis bawah, apakah itu semacam pola dalam dokumentasi vim? Apakah ada hal-hal 'khusus' lainnya untuk dicoba ketika mencari bantuan?
David Pope
2
@ David: helpPerintah melompat ke sebuah tag. Anda dapat melihat tag yang tersedia dengan :h help-tags. Anda juga dapat menggunakan penyelesaian baris perintah untuk melihat tag yang cocok: :h cmdline<Ctrl-D>(atau :h cmdline<Tab>jika Anda mengaturnya dengan wildmodetepat)
Eugene Yarmash
1
Saya harus menggunakan cmap w!! w !sudo tee % > /dev/nullfile .vimrc saya untuk membuat ini berfungsi. Apakah jawaban yang %salah dalam jawaban di atas? (Tidak ada pakar vim di sini.)
dmmfll
@DMfll Ya, benar. Perintah dalam jawaban akan menghasilkan sudo tee > /dev/null /path/to/current/fileyang tidak masuk akal. (Akan mengeditnya)
jazzpi
1
@jazzpi: Anda salah. Kerang sebenarnya tidak peduli di mana pada baris perintah Anda melakukan pengalihan file.
Eugene Yarmash
19

Ini juga berfungsi dengan baik:

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

Ini terinspirasi oleh komentar @Nathan Long.

MEMPERHATIKAN :

"harus digunakan alih-alih 'karena kami ingin %diperluas sebelum meneruskan ke shell.

feihu
sumber
11
Meskipun ini bisa berfungsi, ia juga memberikan akses sudo ke beberapa program (sh dan cat). Contoh lain bisa lebih aman dengan mengganti teedengan /usr/bin/teeuntuk mencegah serangan modifikasi PATH.
idbrii
18

:w - Tulis file.

!sudo - Panggil perintah sudo shell.

tee- Output dari perintah write (vim: w) dialihkan menggunakan tee. % Tidak lain adalah nama file saat ini yaitu /etc/apache2/conf.d/mediawiki.conf. Dengan kata lain perintah tee dijalankan sebagai root dan dibutuhkan input standar dan menulisnya ke file yang diwakili oleh%. Namun, ini akan meminta untuk memuat ulang file lagi (tekan L untuk memuat perubahan di vim itu sendiri):

tautan tutorial

kev
sumber
9

Jawaban yang diterima mencakup semuanya, jadi saya hanya akan memberikan contoh cara pintas lainnya yang saya gunakan, sebagai catatan.

Tambahkan ke etc/vim/vimrc(atau ~/.vimrc):

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

Dimana:

  • cnoremap: memberitahu vim bahwa pintasan berikut harus dikaitkan di baris perintah.
  • w!!: pintasan itu sendiri.
  • execute '...': perintah yang menjalankan string berikut.
  • silent!: jalankan secara diam-diam
  • write !sudo tee % >/dev/null: pertanyaan OP, menambahkan pengalihan pesan NULLuntuk membuat perintah bersih
  • <bar> edit!: Trik ini adalah cherry of the cake: ia juga memanggil editperintah untuk memuat ulang buffer dan kemudian menghindari pesan seperti buffer telah berubah . <bar>adalah cara menulis simbol pipa untuk memisahkan dua perintah di sini.

Semoga ini bisa membantu. Lihat juga untuk masalah lain:

Dr Beco
sumber
diam! menonaktifkan prompt kata sandi sehingga Anda tidak melihatnya
Andy Ray
7

Saya ingin menyarankan pendekatan lain untuk "Oups saya lupa menulis sudosaat membuka file saya" :

Alih-alih menerima permission denied, dan harus mengetik :w!!, saya merasa lebih elegan untuk memiliki vimperintah kondisional yang dilakukan sudo vimjika pemilik fileroot .

Ini semudah diimplementasikan (bahkan mungkin ada implementasi yang lebih elegan, saya jelas bukan bash-guru):

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

Dan itu bekerja dengan sangat baik.

Ini adalah pendekatan yang lebih bashterpusat daripada avim -satu jadi tidak semua orang mungkin menyukainya.

Tentu saja:

  • ada kasus penggunaan di mana ia akan gagal (ketika pemilik file tidak roottetapi membutuhkansudo , tetapi fungsi tetap dapat diedit)
  • itu tidak masuk akal ketika digunakan vimuntuk membaca-hanya file (sejauh yang saya ketahui, saya menggunakan tailatau catuntuk file kecil)

Tapi saya menemukan ini membawa pengalaman pengguna dev yang jauh lebih baik , yang merupakan sesuatu yang cenderung dilupakan IMHO ketika menggunakan bash. :-)

Augustin Riedinger
sumber
5
Perlu diketahui bahwa ini jauh lebih sedikit memaafkan. Secara pribadi, sebagian besar kesalahan saya adalah kesalahan bodoh. Jadi saya lebih suka mengangkat kepala ketika saya melakukan sesuatu yang mungkin bodoh dan konsekuensial. Ini tentu saja masalah preferensi, tetapi peningkatan hak istimewa harus menjadi tindakan yang teliti. Juga: Jika Anda sering mengalami hal ini sehingga membuat ": w !!" cukup repot untuk diam-diam otomatis sudo (tetapi hanya jika pemilik = root); Anda mungkin ingin memeriksa alur kerja Anda saat ini.
Payne
Komentar yang menarik, meskipun orang harus sadar karena ketika file sedang dibuka sebagai rootkata sandi dipertanyakan.
Augustin Riedinger
Ah! Ada perbedaannya. Itu tergantung apakah pengguna sudo telah menetapkan "NOPASSWD" atau tidak.
Payne
Kemudian memiliki NOPASSWD adalah hal yang kurang memaafkan ... :)
Augustin Riedinger
4

UNTUK NEOVIM

Karena ada masalah dengan panggilan interaktif ( https://github.com/neovim/neovim/issues/1716 ), saya menggunakan ini untuk neovim, berdasarkan jawaban Dr Beco:

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

Ini akan membuka dialog menggunakan ssh-askpassmenanyakan kata sandi sudo.

dbranco
sumber