root 権限が必要なファイルを neovim や vim で編集する事があると思います。
その際、 sudoers で許可されているユーザならば
- :write ! sudo tee % > /dev/null
 
で sudo を使って root として書き込むことができます。
ですが、 neovim では
- :write ! sudo tee % > /dev/null
 
- sudo: no tty present and no askpass program specified
 
- shell returned 1
 
と言われて sudo が使えません。
解決策
以下のようなコマンドを定義することで対応できます。
このコマンドは vim と neovim の両方で利用できます。
 
vim で利用する場合は ~/.vimrc に追記します。
neovim で利用する場合は ~/.cofig/nvim/init.vim に追記します。
使い方
- $ nvim /etc/hosts
 
- root 権限が必要なファイルを開いて編集します。保存の際は
 
- :SudoWriteCurrentBuffer
 
- を実行します。
 
- パスワードを入力すれば root で書き込む事ができます。
 
 
動作確認をした環境
大元の環境(vim と neovim で動作確認済)
- OS: macOS Mojave 10.14.6 (18G95)
 
- Homebrew: 2.1.11
 
- Homebrew/homebrew-core: (git revision 883ee; last commit 2019-09-01)
 
- neovim: v0.3.8
 
- vim: 8.0.1365
 
- sudo: version 1.8.17p1
 
- Sudoers policy plugin version 1.8.17p1
 
- Sudoers file grammar version 45
 
- Sudoers I/O plugin version 1.8.17p1
 
- docker: 19.03.1, build 74b1e89
 
docker を使っていくつかの distribution でも動作を確認
- Alpine linux: 3.10.2
 
- neovim: v0.3.7
 
- vim: 8.1.1365
 
- sudo: version 1.8.27
 
- Sudoers policy plugin version 1.8.27
 
- Sudoers file grammar version 46
 
- Sudoers I/O plugin version 1.8.27
 
- CentOS: release 7.6.1810 (Core)
 
- vim: 7.4.1099
 
- sudo: version 1.8.23
 
- Sudoers policy plugin version 1.8.23
 
- Sudoers file grammar version 46
 
- Sudoers I/O plugin version 1.8.23
 
- Fedora: release 30
 
- vim: 8.1.1912
 
- sudo: version 1.8.27
 
- Sudoers policy plugin version 1.8.27
 
- Sudoers file grammar version 46
 
- Sudoers I/O plugin version 1.8.27
 
- Ubuntu: 18.04.3
 
- vim: 8.0.1453
 
- sudo: version 1.8.21p2
 
- Sudoers policy plugin version 1.8.21p2
 
- Sudoers file grammar version 46
 
- Sudoers I/O plugin version 1.8.21p2
 
- Debian: 10.0 (buster-20190812)
 
- vim: 8.1.1401
 
- sudo: version 1.8.27
 
- Sudoers policy plugin version 1.8.27
 
- Sudoers file grammar version 46
 
- Sudoers I/O plugin version 1.8.27
 
 
何故 neovim では sudo が使えないのか
ここから先は先程の Vimscript を作るに至った経緯を書きます。
興味のある方はご覧ください。
まず sudo の error message
- sudo: no tty present and no askpass program specified
 
を見ると、 tty と askpass が無いと言っています。
 
tty と askpass
実際、vim では
- : ! tty
 
- not a tty
 
- shell returned 1
 
 
と言われてしまいます。
askpass は使ったことが無いのですが、fedora の container で dnf search すると
の様に、いくつかの askpass が確認できます。のでちょっと試してみます。
-  x11-ssh-askpass 
 
-  openssh-askpass 
 
-  ksshaskpass 
 
 
という感じで全然起動してくれません。
ですがエラーの内容的に display を開こうとしているのが分かります。
なので、おそらく GUI で password を聞くような物だと思われます。
askpass を作る
askpass について調べていると、どうやら
- password を stdout に出力するもの
 
よって
- neovim 内で password を取得
 
- password を stdout に出力する
 
- password の履歴はどこにも残さない
 
 
ようなものを作れば解決できそうです。
それで完成したのが /tmp/askpass を作るこの Vimscript です。
本当はファイル名は固定でなく tempfile 的な物で対応するなど、改善点はいくつも存在するかと思います。
ですが私は Vimscript に明るくないので、"3つの条件を満たして動けばOK" という条件で作成しました。
他により良い対応方法があればご指摘頂けると幸いです。
 
その他の解決策
これを使わなかった理由としては
- docker のコンテナ内で作業をする事が多々ある
 
- init.vim を配置するスクリプトは既にある
 
- plugin を install するスクリプトはまだ無い
 
- 全コンテナに plugin を入れるのも面倒だし結構サイズがある
 
- $ du -sh ~/.config/nvim/repos
 
- 100M    /Users/atton/.config/nvim/repos
 
 
などがあります。
また、"sudo が使えるならこの際 root になってしまう" という解決策もあるのですが
- 前述した理由の後半3つ
 
- 複数人数のユーザが使う環境だと、 /root 配下に自分専用の設定を置くことになる
 
 
のでこちらも無しで。
他には、 "sudoers で NOPASSWD を設定する" という解決策もあります。
が、sudo を no password でガンガン使えば実質 root で作業しているのと変わらず、危険すぎるのでこちらも無し。
ということでこの Vimscript を書くに至った、という訳なのです。
おまけ: 環境変数の unlet
スクリプト内部では askpass の場所を示す SUDO_ASKPASS を設定している部分があります。
具体的には
- let $SUDO_ASKPASS  = s:askpass_path
 
ですね。
この環境変数を定義していると、 sudo -A した際に指定したファイルが実行されます。
 
一応、 :SudoWriteCurrentBuffer の実行後にはなるべく元の環境に戻すために
を書いていた時もありました。
ですが動作確認中、 Vim 8.0.1365 で unlet 時にエラーが出てしまいました。
 
また 、-A を指定しなければ sudo の動作は変わらないので 、残っていても無害と判断して unlet しない形になりました。
おまけ: shebang を指定しないとどうなるのか
ちなみに askpass の shebang を消すと
- sudo: unable to run /tmp/askpass: Exec format error
 
- sudo: no password was provided
 
 
と言われる為、きちんと書いておく必要があります。
おまけ: x flag を付けないとどうなるのか
setfperm(s:askpass_path, "rwxr-xr-x")  をせずに 644 のままだと
- sudo: unable to run /tmp/askpass: Permission denied
 
- sudo: no password was provided
 
と言われるので、こちらもきちんと指定しておく必要があります。
 
参考