web-dev-qa-db-ja.com

「Sudoで書く」というトリックはどのように機能しますか?

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 誰かが私を助けてくれるでしょうか?

1271
Doppelganger

:w !Sudo tee %...

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

eugene yが指摘した のように、%は確かに「現在のファイル名」を意味します。 Vimでこれを使用するもう1つの方法は、置換コマンドです。たとえば、:%s/foo/barは、「現在のファイルで、fooの出現をbarに置き換えます」を意味します。 :sを入力する前にテキストを強調表示すると、強調表示された行が置換範囲として%の代わりになることがわかります。

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

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

file2.txtの代わりに、Shellコマンドを代用してバッファーの内容を受信できます。たとえば、:w !catはコンテンツを表示するだけです。

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

ティーを理解する

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

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

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

Asciiflow で作成された図。)

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

ハックとしてのティー

あなたの質問が説明する状況では、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部分explicitlyは標準出力を破棄します。先ほど言ったように、別のパイプコマンドには何も渡す必要がないからです。 。

1485
Nathan Long

実行されたコマンドラインでは、%現在のファイル名を表します。これは :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つ以上のファイルにコピーし、さらに標準出力にもコピーします。したがって、:w !Sudo tee % > /dev/nullは、現在のバッファの内容を現在のファイルrootの間に効率的に書き込みます。これに使用できる別のコマンドは dd です。

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

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

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

上記で、あなたはrootとしてファイルを保存するために:w!!<Enter>をタイプすることができます。

93
Eugene Yarmash

これもうまく機能します。

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

これは、@ Nathan Longのコメントに影響されています。

_注意_

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

18
feihu

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

!Sudo - Shell Sudoコマンドを呼び出します。

tee - teeを使用してリダイレクトされたwrite(vim:w)コマンドの出力。 %は現在のファイル名、すなわち/etc/Apache2/conf.d/mediawiki.confに他なりません。つまり、teeコマンドはrootとして実行され、標準入力を受け取り、それを%で表されるファイルに書き込みます。しかし、これは再びファイルをリロードするよう促します(Lを押して変更をvim自身にロードします):

チュートリアルリンク

16
kev

受け入れられた答えはそれをすべてカバーするので、私はレコードのために、私が使う shortcut のもう一つの例をあげるでしょう。

それをあなたの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コマンドも呼び出します。 <bar>は、ここで2つのコマンドを分けるためのpipeシンボルの書き方です。

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

5
Dr Beco

"ファイルを開いているときにSudoname__を書くのを忘れたOups"への別のアプローチを提案したいのですが issue:

permission deniedを受け取って:w!!と入力する代わりに、ファイル所有者がvimname__の場合はSudo vimを実行する条件付きrootname__コマンドを使用するほうがよりエレガントです。

これは実装が簡単です(もっと洗練された実装さえあるかもしれません。私は明らかにbash-guruではありません)。

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

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

これはbashname __-よりもvimname __を中心としたアプローチであるため、誰もが気に入らない可能性があります。

もちろん:

  • それが失敗するユースケースがあります(ファイル所有者がrootname__ではないがSudoname__を必要とするが、関数はとにかく編集することができる場合)
  • ファイルの読み取り専用にvimname__を使用する場合は意味がありません(私の知る限りでは、小さいファイルにはtailname__またはcatname__を使用します)。

しかし、これははるかに優れた devユーザーエクスペリエンス をもたらすことがわかりました。これは、bashname__を使用するときにIMHOが忘れがちなことです。 :-)

3