samba(linux)とgVim(windows)でハマる

おっさんは、忘れてしまうのが異常に早いので、忘れずに備忘録。
ドキュメント書いたことも忘れてしまう。意味ないじゃ~~~ん。

閑話休題。

開発マシンを新しくCentOS7にした時、ファイル共有(Samba4)の設定で罠にはまった。
気づいたのは、CentOS7上に共有ディレクトリを設定して、Windows10の gVim(Kaoriya)でdockerの Dockerfile と docker-compose.yaml 他諸々の自動化シェルスクリプトを編集してた時に気づきました。

「あれ? (Windows10の) gVimで編集して保存(:w)したとき、groupのパーミッションに実行ビットが勝手につきやがるな・・・」

↓の図で Dockerfile のパーミッションに見慣れない(+)記号と group のパーミッションに x が付きます。。。

/etc/samba/smb.conf の設定で、create_maskとかforce create mode とかいろいろ試行錯誤するも状況に変化なし。不思議なことに メモ帳で開いて、保存すると、上記のようなことは起こらない。
Windows版のvim/gVimの時だけそうなる。。。

で、見慣れない + 記号の意味を調べると、Windowsの拡張属性?みたいなものがくっつくと + 記号が出るみたい。。。
そこで、sambaのリファレンスからそれらしい設定を見つけた。

  • map archive
  • map system
  • map hidden
  • nt acl support

DOSでおなじみ、A,H,S属性をLinux側とマッピング?するオプションなのかな?
nt acl support はそのものずばり、WindowsのACLのサポートをするかどうか。
とりあえず、これらを片っ端から no に設定して samba を再起動したら直った・・・・と思ったけど、今度は実行ビットが立ったファイルをgVim(windows)で編集して保存すると、実行ビットがなくなる。。。
smb.confで、create mask を 644 にしているのでは???と思い、gVim の保存方法に問題があるんじゃねーの?って推定して、ググると、どうやら backupcopy 変数が関係しているらしい、とのこと。

:helpで調べると unix以外のvimでは backupcopyの初期値は auto だそうで、backup=yes を ~/_vimrc に追加すると、僕の期待した通りの挙動に一応なりました。

ってなわけで、Linux側で共有されたファイルをWindowsのgVimで編集するときは、:set backupcopy=yes で、/etc/samba/smb.conf の map **** と nt acl support を no に設定。

gVimでのシェル切り替え

Visual Studio 2019 Community インストール。
まぁ、たまにC#とか、昔のC++で書いたツールをビルドするのにやっぱ必要。
IDEはほとんど立ち上げず、gVimで編集、編集。もうカーソル移動が HJKLバインドじゃないと苦痛を感じるレベルまで悪化。
xkeymacs ならぬ、xkeyvim ってググるおっさんがここにいる。

そんなことはさておき。

gVimでC#とかC,C++(Win32 API)のコードを叩いていると、terminal でビルドしたくなります。だけど、おっさんはgVimのshellオプションをWSL(Bash)に変えてあるので、困った、困った、こまどり姉妹になるわけです。

困るので、gVimの複数のオプションを一括変更するだけのコマンドを書く・・・なんか激しく無駄なことをしている気がしないでもないが・・・CMD と WSLを行ったり来たりするにはこれしかない。

" Set CMD 既定値に戻す
function! Fsetcmd()
  set shell&
  set shellcmdflag&
  set shellslash&
  set shellquote&
  set shellxquote&
  set shellxescape&
  set grepprg&

  echo 'change shell to default windows cmd'
endfunction

" Set WSL
function! Fsetwsl()
  let &shell = 'bash'
  let &shellcmdflag = '-c'
  let &shellslash = 1
  let &shellquote='"'
  let &shellxquote = ''
  let &shellxescape = ''
  let &grepprg = 'grepwsl -n'

  echo 'change shell to WSL bash'
endfunction

command! Setcmd call Fsetcmd() 
command! Setwsl call Fsetwsl() 

vimスクリプトで、オプション変数を初期値に戻すにはどう書けばいいのかなぁ・・・???
追記:
オプション変数を規定値に戻すのは set {option}& にすればいいようで・・・ヘルプに書いてますね・・・反省

普段は シェルを wsl-bash にしているので、 :Setcmd とすれば、:termや:shell や :r !hogehoge でcmd.exeを使うデフォルトに戻れる。
こんなアホなことしてんのおっさんだけだよなぁ。。。

gvim(+kaoriya) + WSL Part3

:prev の続き

※この内容は事前に Part1で記述したように shell,shellcmdflagとかの変数をWSL用に変更する必要があります。

とりあえず、gVim(+kaoriya) と WSLの連携をある程度スムーズにできるようになりましたが、問題が一つ発生。
ローカルドライブだと問題はないのですが、リムーバルディスクとかネットワークドライブだと、WSL内の /etc/fstab に記述して自動マウントしてても、Windows10アプリケーションからWSLに移行した時点でカレントディレクトリが引き継げません。

ネットワークドライブを ドライブレター(Z: とか)にアサインして、cmd.exeで cd /d z: としてカレントディレクトリを変更し、bash.exe(or wsl.exe)しても、ホームディレクトリに飛ばされます。。。
カレントディレクトリを引き継いでくれるのは、c:ドライブとか、ローカルハーディスクだけのようです。ディスクタイプによって判断しているんでしょうか・・・。

これが一番困るのは、:grep コマンド。:grep コマンドの結果をQuickFixで開けるようにしているけど、カレントディレクトリ以下でローカルドライブだとなんとか使えるけど、リムーバルドライブとか、ネットワークドライブだと、パスの関係で全滅。WSLのフルパスで検索対象を渡せば grep は機能するが、QuickFixウィンドウに表示されるのはWSL側のフルパスなので 当然 gVim ではそのパスが理解できないので、結局使えない。

解決方法は、3つ。

(1) :grep をあきらめ、:vim(grep) で代替。
  ⇒ (ネットワークドライブだと特に)遅い。常用できないほど遅い。

(2) Msys の grep.exe で代替
 ( _vimrc にて set grepprg=/mnt/c/Msys64/usr/bin/grep.exe\ -n

  ⇒ WSLのみでなんとかする、という最初の趣旨から外れてしまうが・・・しょうがない。

(3) WSL側に grepの結果からパス名部分を変換するフィルタスクリプトをかます
 ( _vimrc にて set grepprg=grepwsl\ -n

  ⇒ ネットワークドライブ内のファイルを編集している時だけ検索パスをフルパス(WSL)を指定しないといけない。
  ⇒ また、若干遅くなるが、許容範囲内。。。WSLだけで完結できる。

というわけでとりあえず しばらく(3)を常用することにした。すでに Msys入れている場合は(2)の方が簡単かも・・・。ネットワークドライブのときだけ検索パスを絶対パスにする必要があるが、まぁしょうがない。

まずネットワークドライブ(例: Y)をドライブレターにアサインし、 sudo mkdir /mnt/yでディレクトリを作成し、/etc/fstabに登録する。

# /etc/fstab
LABEL=cloudimg-rootfs   /        ext4   defaults        0 0
Y: /mnt/y drvfs defaults,noatime,uid=1000,gid=1000 0 0

WSL側のpathが通っているところに(たとえば /usr/local/bin/ とか)grepwsl を作成しsudo chmod +x /usr/local/bin/grepwsl

#!/bin/sh
##############################################################################
#  wslgrep  .... WSLに入っているwslpathコマンドを使用してパスを変換 
#  2018/08/29 絶対パスに変換するオプション(-a)を追加/正規表現ちょい修正
##############################################################################
grep $@ | perl -pe 's/^([^:]+)/`wslpath -m -a $1 | tr -d "\n"`/e;'

これは単にgrepの結果を perl に渡して変換処理をしてるだけ。sedでもawkでもなんでも。単にperlに慣れているだけ。

んで、~/_vimrc に追記

;;; ~/_vimrc
set grepprg=grepwsl\ -n

ってなけで、とりあえず、なんとかなった!(^^;

gvim(+kaoriya) + WSL Part2

:prev
この前のつづきです。

シェルをWSL(ubuntu bash)にすることで、:terminalの他、外部コマンド実行(“!” から始まるやつ)をWSL内で完結することができました。
が、ただ一点、:terminal {command} にする場合、:terminal bash -c '{command}' としないとエラーになってしまうので、これが不満。

そこで、Wslterm というユーザー定義のコマンドを _gvimrc に追加することで、ラクしよう、と、こういうわけです(^^;
ヘルプを斜め読みしてとりあえず関数とコマンドの定義の仕方を最低限覚えて、以下の記述に辿り着く。
ほんとはもっとスマートなやり方があるんだと思いますが・・・素人の思いつきです。。。

" terminal の WSLラッパー コマンド (かなり修正:2018/7/13 0:02)
" ・・・なんかトンチカンなことをやってる気がしてきた・・・
" append to ~/_gvimrc 
set shellslash
set shell=C:/WINDOWS/system32/bash.exe
set shellcmdflag=-c
set shellquote=\"
set shellxescape=
set shellxquote=

function! Wslterms(c,f,l,...)
  let cmdline = []
  let option = []
  let i = 0
  while i < a:0
    if stridx(a:000[i],'++') == 0
      call add(l:option,a:000[i])
    else
      call add(l:cmdline,a:000[i])
    endif
    let i += 1
  endwhile
  let l:options = len(l:option) ? join(l:option," ")." " : ""
  let l:cmd = "terminal " . l:options

  if len(l:cmdline) > 0
    let l:cmd = printf("%s%s %s %s",l:cmd,&shell,&shellcmdflag,shellescape(join(l:cmdline," ")))
  endif

  if a:c 
    let l:cmd = join([a:f,a:l],",") . l:cmd
  endif

  execute(l:cmd)
endfunction
command! -nargs=+ -complete=file -range=0 Wslterm call Wslterms(<count>,<line1>,<line2>,<f-args>) 

これで、:Wslterm perl %とか、’<,'>Wslterm hogehoge とかやると、bash -c をつけて簡易的に :terminalの真似ができました(^^;; おまけで、gvimのterminalの中で、WSL(ubuntu)内のvimでファイルを編集(左)とかできちゃう!w

ただ、vim自体操作は慣れてきたとはいえ、まだよく分かっていないのでこういうアプローチでいいのか、よく分かんないです。またvimスクリプトの本でも買って勉強しよ。

■補足 2018/09/24
guioptions に “!” を追加すると :shell コマンド時、コンソールウィンドウが立ち上がるのではく、gVimのウィンドウを使用するみたい。知らんかった・・・・。

:set guioptions+=!

_gvimrcに書いとくと、かなりイケてる感が味わえるかも(^^;
ちなみに僕の guioptions は・・・

"snip
guioptions=egmrL!
"snip

となっております。。。

:continue
つづきは part3で

gvim(+kaoriya) + WSL

ちょっと躓いたので、備忘録。

gvim(+Kaoriya)で特に何も設定しないと、シェルは cmd.exe が使われますよね。8.1から:terminalが使えるようになったので、とりあえず %USERPROFILE%\_gvimrc に 以下の記述をつけてシェルをWSLのbashにしました・・・。

set shell=C:\WINDOWS\System32\bash.exe

一瞬だけ自己満に浸ってしまいましたが・・・コマンドラインモードで次を実行すると・・・

:r !date

テンポラリファイルがオープンできない旨のエラーが出て撃沈。

困ったときのグーグル先生で検索するも探し方が悪いのか解決できない。基本に立ち戻り、vimのヘルプを参照(^^; ( :help options )

shell を変更する場合は、shellcmdflag,shellquote,shellxquote等も変更してね・・・ということらしい。
はじめからこちらを見てれば問題なかった・・・。
ヘルプのとおりに以下のように設定すると、問題なくでけた!

set shell=C:\WINDOWS\System32\bash.exe
set shellcmdflag=-c
set shellquote=\"
set shellxescape=
set shellxquote=

※修正:shellxquoteを空にすると perl のファイルを開く時に ftplugin/perl.vim で標準入力待ちになって帰ってこなくなったので、よくわからないけど 引用符をいれたらなんか治った・・・よくわからん。まぁいいや。
※修正の修正:shellxquoteを設定したら今度は !command が動かなくなった・・・ということで戻す・・・。該当ファイル(ftplugin/perl.vim)を見たら perlpath = system('perl -e "join ....");の部分で perlpath を設定しているみたいなので、仕方ないので _gvimrc に let g:perlpath = “ほにゃららら” とかでお茶を濁して切り抜けた。

紆余曲折がありましたが・・・:terminal のおかげで、なかなかスマートに事を為しとげることができ、いとをかし。。。

スクリプトをせっせと書きつつ、コード片を スクリプトに流し込み、動作確認、CTRL-W N で端末ノーマルモードに移行しコピって、スクリプトにペーストとかもうキーボードから手を放さず全部できちゃう!

今までのようにコンソール画面をマウスでチマチマ選択しながらコピペ・・・とかもうおさらばさ!もうWindowsはダサいとか言わせないぜ!・・・・とかいいつつ、使ってるツール、環境は全部Linux由来なんだけど・・・(^^;;; WSLのおかげで、gvimの端末からフツーのWindowsアプリも起動できるし、いうことなし。\(^o^)/。

ただ、一点惜しむらくは、:term command とすると、当然環境変数PATHからcommandを探そうとします。これを WSL の中で行わせるには、:term bash -c command とかやらないと無理っぽい。この “bash -c “っていうのはイチイチ打つのはメンドイ。しかし、初心者のおいらにはこれまた解決方法が分からず、一向に初心者からなかなか抜け出せないな・・・。

:continue
つづきは part2で