Linux環境でVI/VIMでファイルを編集するときに保存する権限のない解決方法(一般ユーザー)
Linux環境では、VI/VIMコマンドを直接使用して変更権限のないファイルを編集する場合、保存するときに保存操作ができないことをユーザーに提示します。一般的な解決策は、ファイルを閉じて再びsudo権限で開いて編集してから保存することです(ユーザーがsudo権限を持っている場合)。実は、VI/VIMモードでは簡単なコマンドで、現在のファイルを閉じずにファイルを保存する目的を達成できます。
方法1
について!sudo tee%>/dev/nullこのコマンドの説明は次のとおりです。
このコマンドは、現在のファイル(%)をstdinとしてsudo teeコマンドに渡して実行します。
方法2
Linuxで働いている友人は、Vimでファイルを編集したとき、実行:wq保存が終了し、突然エラーが発生しました。
E45: 'readonly' option is set (add ! to override)
これはファイルが読み取り専用であることを示しており、ヒントに従って、追加します!強制保存::w!,次のようなエラーが発生しました。
"readonly-file-name" E212: Can't open file for writing
ファイルが存在するのに、なぜプロンプトが開かないのですか?この間違いは何を表しているのだろうか。ドキュメントの表示:help E 212:
For some reason the file you are writing to cannot be created or overwritten.
The reason could be that you do not have permission to write in the directory
or the file name is not valid.権限がない可能性があるからだ。このファイルを編集するにはroot権限が必要ですが、現在ログインしているのは普通のユーザーだけで、編集する前にsudoを使ってVimを起動するのを忘れたので、保存に失敗しました。そこで、修正が失われないように、別の一時ファイルtemp-file-nameとして保存してからVimを終了し、sudo mv temp-file-name readonly-file-nameを実行して元のファイルを上書きするしかありません。
しかし、このような操作は煩雑すぎる。また、このファイルを一時保存したいだけで、変更を続ける必要がある場合は、履歴の編集、bufferステータスなど、Vimの作業状態を維持したい場合は、どうすればいいですか?Vimを終了しないでroot権限を取得してこのファイルを保存できますか?
ソリューション
*: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 |:!|. The default [range] for the ":w" command is the whole buffer (1,$)
答えはいいです。このようなコマンドを実行すればいいです。
: w !sudo tee % | | | | :[range]w[rite] [++opt] !{cmd}
:w !sudo tee %
次に、このコマンドがなぜ機能するのかを分析します。まず、ドキュメントを表示します:help:w、下にスクロールすると、次のように表示されます。
この使用方法を前のコマンドに対応して、以下のようにします。
range
range
Vimで外部コマンドを実行
sudo tee %
:!{cmd}
:!ls:r !pwdまたは:r!ls
このとき、すべてのコンテンツがVimに読み込まれ、Vimを終了せずにコマンドを実行し、コピーしてVimに貼り付けられます。これにより、Vimは終了することなくshellを自由に操作できます。
コマンドの別の表示形式
前のドキュメントを参照してください。
Execute {cmd} with [range] lines as standard input
したがって、実際には、wは現在のファイルを本当に保存していません。実行:w new-file-nameのように、現在のファイルの内容を別のnew-file-nameのファイルに保存します。ここでは、保存ではなく、別の名前で保存します。現在のドキュメントの内容を後述するcmdの標準入力に書き込んでからcmdを実行するので、コマンド全体を同じ機能を持つ通常のshellコマンドに変換できます。$ cat readonly-file-name | sudo tee %
これで「正常」に見えます。その中でsudoはよく理解して、rootに切り替えて後のコマンドを実行することを意味して、teeと%は何ですか?
%の意味
:help cmdline-special
In Ex commands, at places where a file name can be used, the following
characters have a special meaning. These can also be used in the expression
function expand() |expand()|.
% Is replaced with the current file name. *:_%* *c_%*
cmdsudo tee readonly-file-name
$ cat readonly-file-name | sudo tee readonly-file-name
注意:別の場所でもよく%を使います。間違いありません。置き換えます。しかし、そこでは%の役割が異なります。実行:help:%ドキュメントを表示します。
Line numbers may be specified with: *:range* *E14* *{address}*
{number} an absolute line number
...
% equal to 1,$ (the entire file) *:%*
置換では、%はファイル名ではなくファイル全体を表すことを意味します。したがって、コマンド:%s/old/new/gでは、ファイル名のoldをnewに置き換えるのではなく、ドキュメント全体のoldをnewに置き換えることを示します。
teeの役割
man page

ls -l
readonly-file-name
tee
/dev/null
コマンド実行後上記のコマンドを実行すると、次のプロンプトが表示されます。
W12: Warning: File "readonly-file-name" has changed and the buffer was changed in Vim as well
See ":help W12" for more info.[O]K, (L)oad File:
Vimはファイルの更新を求め、ファイルを確認するか再ロードするかを尋ねます。履歴の編集、bufferなどのVimの作業状態を維持し、元に戻すなどの操作を継続できるため、Oを直接入力することをお勧めします。Lを選択すると、ファイルが新しいファイルで開き、すべての作業状態が失われ、元に戻すことができず、bufferの内容も空になります。
より単純なシナリオ:マッピング
上記の方法は、文章の最初に提起する問題を非常に完璧に解決したが、結局、コマンドはまだ少し長いので、長い列のコマンドを入力するたびに、簡単なコマンドにマッピングすることができる.vimrc:
" Allow saving of files as sudo when I forgot to start vim using sudo.
cmap w!! w !sudo tee > /dev/null %
これで、簡単な運転:w!!できます。コマンド後半>/dev/nullは、標準出力の内容を明示的に破棄するために機能する前に説明した。もう一つの考え方
これで、完璧だがtrickyの案が完成した。なぜ次のようなより一般的なコマンドを使わないのかと聞かれるかもしれません。これはもっと理解しやすくて、もっと簡単ではありませんか?
:w !sudo cat > %
リダイレクトの問題
前のように同じ機能のshellコマンドに変換できることを分析します。
$ cat readonly-file-name | sudo cat > %
このコマンドは少しも問題がないように見えますが、実行すると別のエラーが発生します。
/bin/sh: readonly-file-name: Permission denied
shell returned 1
これはどういうことですか。sudoをつけたのに、どうして権限がないとヒントを与えたの?リダイレクトはshellが実行するため、すべてのコマンドが開始される前にshellがリダイレクト操作を実行するため、sudoの影響を受けず、現在のshell自体も一般ユーザーとして起動し、このファイルを書く権限もないため、上記のエラーが発生しました。
リダイレクトスキーム
tee
sudo
shell
:w !sudo sh -c 'cat > %'
しかし、このように実行すると、一重引用符が存在するため、Vimでは%は展開されず、そのままshellに渡されるが、shellでは1つの%がnilに相当するため、ファイルはnilにリダイレクトされ、すべての内容が失われ、ファイルの保存に失敗する。
%が展開されていないためにエラーが発生した場合は、一重引用符'を二重引用符"に変更してもう一度試してみます。