vimの「sudoで書き込む」トリックはどのように機能しますか?


1412

sudoでvimを開くのを忘れた場合でも、root権限が必要なファイルに書き込むことができるコマンドは、おそらく多くの人が見たことがあるでしょう。

:w !sudo tee %

ここで何が起こっているのか正確にはわかりません。

私はすでにこれを理解しています: wこれは

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

そのため、すべての行を標準入力として渡します。

!sudo tee一部には、呼び出しをtee管理者権限で。

すべてが意味をなすために、%はファイル名を(のパラメーターとしてtee)出力する必要がありますが、この動作のヘルプに関する参照が見つかりません。

tl; dr誰かがこのコマンドを分析するのを手伝ってくれませんか?


5
@ネイサン::w !sudo cat > %同様に機能せず、標準出力を汚染しませんか?
Bjarke Freund-Hansen 2011

51
@bjarkef-いいえ、それは機能しません。その場合、sudoはに適用されますがには適用されcatないため>、許可されません。コマンド全体をのようなsudoサブシェルで実行することもできますが:w !sudo sh -c "cat % > yams.txt"、サブシェルで%はnil であるため、これも機能しません。ファイルの内容を空白にします。
ネイサンロング

3
そのコマンドを入力した後に警告メッセージが表示される場合があります。その場合は、Lを押します。次に、Enterキーを押すように求められます。実行すると、最終的にファイルが保存されます。
pablofiumara 2013年

9
@NathanLong @knittl:サブシェルに到達する前にVimがファイル名を置換するため:w !sudo sh -c "cat >%"、実際には同様に機能sudo tee %します%。ただし、ファイル名にスペースが含まれている場合、どちらも機能しません。あなたがするか、:w !sudo sh -c "cat >'%'"それ:w !sudo tee "%"を修正する必要があります。
Han Seoul-Oh

1
:Wを使用して保存し、ファイルをリロードします。コマンドW:execute ':silent w!sudo tee%> / dev / null' | :編集!
ディエゴロベルト

回答:


1612

:w !sudo tee %...

% 「現在のファイル」を意味します

以下のようユージンyは指摘し%実際に渡され、「現在のファイル名」、意味はtee、それが上書きするファイルを知っているように。

(置換コマンドでは、少し異なります。:help :%示されているように、これはequal to 1,$ (the entire file)(これがファイル名として評価されないことを指摘してくれた@Orafuに感謝します。)たとえば、:%s/foo/bar現在のファイルで、の出現に置き換えます意味foobarます。と入力する前にテキストを入力する:sと、強調表示された行が%置換範囲の代わりに使用されることがわかります。)

:w ファイルを更新していません

このトリックの混乱する部分の1つは、 :w、ファイルを変更しているが、そうではありません。を開いて変更file1.txtしてから実行:w file2.txtすると、「名前を付けて保存」されます。file1.txt変更されませんが、現在のバッファの内容はに送信されfile2.txtます。

の代わりに file2.txtシェルのコマンドでバッファの内容を受け取ることができます。たとえば:w !cat、コンテンツだけを表示します。

Vimがsudoアクセスで実行:wされなかった場合、保護されたファイルを変更することはできませんが、バッファーの内容をシェルに渡すと、シェルのコマンド sudoで実行できます。この場合、を使用しますtee

Tシャツについて

teeteeは、通常のbashパイピング状況でコマンドをT字型パイプとしてます。指定されたファイルに出力を送信し、次のパイプコマンドでキャプチャできる標準出力に送信します

例えば、ではps -ax | tee processes.txt | grep 'foo'、プロセスのリストをテキストファイルに書き込まれますに渡しますgrep

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

Asciiflowで作成された図。)

詳細については、teemanページ参照してください。

ハックとしてのティー

あなたの質問が説明している状況では、使用することteeはハックです。なぜなら、それが行うことの半分を無視しているからですsudo teeファイルに書き込み、バッファの内容を標準出力に送信しますが、標準出力は無視します。この場合、パイプで連結された別のコマンドに何も渡す必要はありません。teeファイルを書き込む別の方法として使用しているので、で呼び出すことができますsudo

このトリックを簡単に

これをに追加して、.vimrcこのトリックを使いやすくすることができます:w!!。タイプするだけです。

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

私が言ったように、パイプで連結された別のコマンドに何も渡す必要がないため、この> /dev/null部分は明示的に標準出力を破棄します。


147
特にあなたの表記「w !!」のように 「sudo !!」を使うと覚えやすい コマンドラインで。
Aidan Kane

11
したがって、これはteestdinをファイルに書き込む機能を使用します。それを行うのが仕事のプログラムがないことに驚いています(これを実行するという、聞いたことのないプログラムが呼び出されspongeたことを発見しました)。典型的な「ファイルへのストリームの書き込み」は、組み込みのシェルによって実行されると思います。Vim !{cmd}はシェルをフォークしませんcmdか(代わりにフォークしますか)?おそらく、より明白なものは、sh -c ">"ではなくの動作するバリアントを使用することですtee
Steven Lu

2
@Steven Lu:Debianベースのディストリビューションを除くほとんどすべてのディストリビューションspongemoreutilsパッケージの一部です。およびのmoreutilsようなより一般的なツールと同等のかなり良いツールがいくつかxargsありteeます。
スイスの

10
このエイリアスを展開して、変更されたファイルの内容を現在のバッファーに自動的にロードするようにvimに指示する方法は?それを私に尋ねます、それを自動化する方法は?
Zlatko、

10
@ user247077:その場合、catルートとして実行され、出力はシェルとしてリダイレクトされます。シェルはルートとして実行されません。と同じecho hi > /read/only/fileです。
WhyNotHugo

99

実行されたコマンドラインで%は、現在のファイル名を表します。これは次のドキュメントに記載されてい: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.

すでにわかったよう:w !cmdに、現在のバッファーの内容を別のコマンドにパイプします。どのようなtee行いは、1つのまたは複数のファイルにし、標準出力にも標準入力をコピーしています。したがって、rootである間:w !sudo tee % > /dev/null、現在のバッファの内容を現在のファイルに効率的に書き込みます。これに使用できる別のコマンドは次のとおりです。dd

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

ショートカットとして、このマッピングを以下に追加できます.vimrc

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

上記のように入力:w!!<Enter>すると、ファイルをルートとして保存できます。


1
興味深いことに、:help _%入力した内容が表示されますが、中:help %括弧に一致するキーが表示されます。私はアンダースコアの接頭辞を試そうとは思わなかったでしょう、それはvimドキュメントのある種のパターンですか?ヘルプを探すときに他に試す特別なことはありますか?
David Pope

2
@David:helpコマンドはタグにジャンプします。使用可能なタグはで確認できます:h help-tags。コマンドライン補完を使用して、一致するタグを表示することもできます:h cmdline<Ctrl-D>(または:h cmdline<Tab>wildmode適宜設定した場合)
Eugene Yarmash

1
cmap w!! w !sudo tee % > /dev/nullこれを機能させるには、.vimrcファイルで使用する必要がありました。された%上記の答えに置き忘れ?(vimエキスパートはここにいません。)
dmmfll 2015年

@DMfllはい、そうです。答えのコマンドは、sudo tee > /dev/null /path/to/current/file実際には意味をなさない結果になります。(編集する
予定

1
@jazzpi:あなたは間違っている。シェルは実際には、コマンドラインのどこでファイルリダイレクトを実行するかを気にしません。
Eugene Yarmash

19

これもうまくいきます:

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

これは@Nathan Longのコメントに触発されました。

通知

"シェルに渡す前に展開し'たいので、代わりに使用する必要があります%


11
これはうまくいくかもしれませんが、複数のプログラム(shとcat)へのsudoアクセスも提供します。他の例は、交換することにより、より安全である可能性がtee/usr/bin/teePATH-変更攻撃を防ぐために。
idbrii 2014

18

:w -ファイルを書き込みます。

!sudo -シェルのsudoコマンドを呼び出します。

tee-teeを使用してリダイレクトされた書き込み(vim:w)コマンドの出力。%は、現在のファイル名、つまり/etc/apache2/conf.d/mediawiki.confにすぎません。つまり、teeコマンドはrootとして実行され、標準入力を受け取り、%で表されるファイルに書き込みます。ただし、これにより、ファイルを再度再読み込みするように求められます(Lを押して、vim自体の変更を読み込みます)。

チュートリアルリンク


9

受け入れられた答えはそれをすべてカバーしているので、ショートカットの別の例を挙げます、記録のために使用。

あなたのetc/vim/vimrc(または~/.vimrc)に追加してください:

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

どこ:

  • cnoremapvimに伝えますコマンドラインで次のショートカットを関連付けることをます。
  • w!!:ショートカット自体。
  • execute '...':次の文字列を実行するコマンド。
  • silent!:静かに実行する
  • write !sudo tee % >/dev/null:OPの質問、NULLクリーンなコマンドを作成するためにメッセージのリダイレクトを追加
  • <bar> edit!:このトリックはケーキのチェリーです:editバッファーをリロードし、バッファーが変更さなどのメッセージを回避するコマンドも呼び出します。ここでは、2つのコマンドを区切るパイプシンボル<bar>を記述する方法を示します。

それが役に立てば幸い。他の問題についても参照してください。


サイレント!パスワードプロンプトを無効にして、表示されないようにする
Andy Ray

7

「ファイルを開いているときに書き込むのを忘れ」という別のアプローチを提案したいsudo

代わりに受け取るのpermission deniedと、入力した:w!!、私はそれがよりエレガントな条件持って見つけvimないコマンドsudo vimファイルの所有者である場合をroot

これは簡単に実装できます(よりエレガントな実装もあるかもしれませんが、私は明らかにbash-guruではありません)。

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

そして、それは本当にうまくいきます。

これは1 bashつよりも中心的なアプローチであるvimため、誰もがそれを好むとは限りません。

もちろん:

  • 失敗するユースケースがあります(ファイル所有者がそうではないrootが、必要sudoであるが、関数はとにかく編集できる場合)
  • vimファイルを読み取り専用で使用する場合は意味がありません(私に関する限り、使用tailするのcatは小さなファイルです)

しかし、これにより、はるかに優れた開発者ユーザーエクスペリエンスがもたらされます。これは、IMHOを使用するときに忘れがちなものですbash。:-)


5
ただし、これは許容度がはるかに低いことに注意してください。個人的に、私の過ちのほとんどは愚かな過ちです。だから、私は愚かで結果的なものかもしれない何かをしているときに頭を上げるのを好む。これはもちろん好みの問題ですが、特権の昇格は良心的な行為であると考えられています。また、「:w !!」を作成するほど頻繁にこれを経験する場合 sudoを静かに自動実行するのに十分な煩わしさ(ただし、owner = rootの場合のみ)。現在のワークフローを確認することをお勧めします。
Payne

興味深いコメントですが、ファイルが開かれているrootときにパスワードが照会されるため、意識する必要があります。
Augustin Riedinger、

ああ!違いがあります。sudoユーザーに「NOPASSWD」が設定されているかどうかによって異なります。
Payne、

次に、NOPASSWDを使用する
許容範囲が狭くなり

4

NEOVIMの場合

インタラクティブコールの問題のため(https://github.com/neovim/neovim/issues/1716)のため、Beco博士の回答に基づいて、これをneovimに使用しています。

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

これによりssh-askpass、sudoパスワードの入力を求めるダイアログが開きます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.