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で

NET::ERR_CERT_COMMON_NAME_INVALID なんだこれ?

備忘録。あしらからず。

WSLのおかげでもうほとんどLinuxな仮想マシンは要らなくなったのでホントにラクチン。まぁ、業務上必要なのでまだまだ HyperVにはお世話にならんといけませんが・・・。

で、WSL-UbuntuにSSL通信(HTTPS)させようと、ひさしぶりにopensslコマンドとか使う。なにげにopensslは万能コマンドすね。乱数からダイジェストから暗号化まで全部できちゃう!(^^; で、まぁ、お約束どおりCA.pl -newcaとかで認証局を作って、認証局の証明書をWindowsの証明書マネージャで「信頼されたルート証明機関」にインポート後、openssl.cnfいじって、あとはお決まりのCSR作って、さっき作った認証局で署名して、そのサーバー証明書(CN=localhost)をapache2に食わせて、https://localhost/ にアクセスしたんすよ。

結果、撃沈。昔はこれでいけたんです! IE11だと問題ないし。

最新のChrome/FireFox/Edge ぜんぶダメ。chromeだとNET::ERR_CERT_COMMON_NAME_INVALIDとかいう謎のエラーが出る。。。いやいや、証明書のプロパティを見るとちゃんと CN=localhostってなってるし・・・。

で、このエラーをそのままググると、最近のブラウザはCNは無視するみたいで・・・って、よくわからんが、最近は、CNの一致ではなく、SANを見てるんだとか・・・。理屈はともかく、理由が分かればあとは証明書の作り方を変えればオケ。

とりあえず、CSR(署名要求)まではお約束通り。
※openssl.cnfの設定はググればよろし。ただ、CA向け用とサーバー向け用とファイルを分けて使い分けるのが間違いがなくてよろしかと。
デフォルトのopenssl.cnfをCA用として修正し、サーバー証明書作成時に使う設定は openssl-server.cnfと別ファイルにして使ってます。

#1.認証局作成
$ cd /usr/lib/ssl/misc
$ ./CA.pl -newca

# Windowsの証明書マネージャで「信頼されたルート証明機関」にインポートとするため der に変換。
# でも cacert.pem を直接インポートしたら普通にできたので、↓要らないかも。
$ cd demoCA
$ openssl x509 -in cacert.pem -inform pem -out cacert.der -outform der

#2.サーバーキー作成
$ cd /etc/ssl
$ mkdir server; cd server
$ openssl genrsa -out localhost.pem -aes256 2048
$ openssl rsa -in localhost.pem -out localhost.key

#3.署名要求作成
$ openssl req -new -config ../openssl-server.cnf -key localhost.pem -out localhost.csr

そして・・・CSR(署名要求)を認証局に署名してもらうときに、下記の内容のテキストファイルを用意して、-extfile オプションをつけて食わせるといいようです。理屈はどうでもいい。まずは手順さえ覚えて、あとから調べればいいんです。逃げ。

subjectAltName=DNS:localhost

複数の場合はカンマで区切れば行けそう?

subjectAltName=DNS:localhost,IP:127.0.0.1

このファイルを署名するときに食わせる。

$ echo "subjectAltName=DNS:localhost" > localhost.san
$ openssl ca  -config ../openssl-server.cnf -in localhost.csr -out localhost.crt -extfile localhost.san

そして、できた localhost.keyとlocalhost.crt の二つを /etc/apache2/sites-available/default-ssl.conf の SSLCertificateFile とSSLCertificateKeyFile に記述。そして起動する。

$ a2enmod ssl
$ a2ensite default-ssl
$ service apache2 start

そして、1.で作ったcacert.der をデスクトップにコピーして 右クリック⇒証明書のインストールで「信頼されたルート証明機関」へストア。

ってなわけで?めでたく、最新のブラウザでも https://localhost/ で緑色の鍵マーク付きで表示された。