wsl内からWindowsのIPを取得
WSL2内からWindows側のIPを取得するのは意外と面倒です。
WindowsのIPアドレスとwsl2のネットワークのアドレスがあり、Windows側のIPアドレスをwslから知りたい、という局面はしばしば発生しますが、決定打、といえる方法がないようです(あったら教えてください)。
Windows上で自分のIPアドレスをコマンドで取得、といえば、ipconfig.exeですが、これがなかなか自動処理には向かない出力をしています。が、ある日、ipconfig.exeの出力を見ていて「あれ、これはちょっといじればyamlになるのでは」と気が付きました。
ipconfig.exeの出力をyamlにして出力するipconfigと、これを利用してWindows側のIPアドレスを取得するgetHostIpを作成しました。
- powershell経由で
Get-NetIpConfigurationを使うものも書いてみましたが、上記のyq + jqのほうがやりやすかったので作ってみました
文字コードの変換
出力形式を加工する以前に、ipconfig.exeはcode page(LOCALEに相当)によって出力する項目名が変わるのでコードページを437(US-ASCII)に変換して実行する必要があります。
これで項目名を英語に統一することはできるのですが、ネットワークインタフェース名などに日本語がついている場合(デフォルトでこうなる)のインタフェース名が項目の「値」として、現在のコードページに関係なく設定された元の文字コードのまま出力されてしまいます。
ipconfig.exeの結果にnkfを挟むことも考えましたが、一応日本語以外でも利用できるようiconv(ubuntu系でもRHEL系のどちらでも普通にインストールできるはず)を利用し、-f(変換元文字コード)にCP+(windows側のコードページ番号)を指定することで対応しました(ソースは下に添付)。少なくとも日本語ではこれでうまくいく、というレベルの処理です。
国際化を考えると単純な処理しかしていないので対応できないコードページなどもあると思うので、必要に応じてelifを追加する必要があると思います(と日本語で書いているあたり、あまりやる気が感じられない)。
ちなみにコードページを65001(UTF-8)にしても「値」がMS932(ShiftJIS)なのは変わらないので、この対応は必要です。なので枯れている437を選んでいます。
yamlへの変換
ここまでくれば、ipconfig.exeの出力結果はほぼyamlなので力業で変換します。
#! /bin/sh
CP=$(cd /mnt/c; cmd.exe /c 'chcp' | cut -d: -f2 | cut -c2- | tr -d \\r)
if [ "${CP}" = "437" -o "${CP}" = "65001" ] ; then
# us-ascii or unicode
CODE_CONV=cat
elif type iconv > /dev/null 2>&1 ; then
CODE_CONV="iconv -f cp${CP} -t utf8"
else
CODE_CONV=cat
fi
(cd /mnt/c; cmd.exe /c 'chcp 437 && ipconfig /all') \
| ${CODE_CONV} \
| tr -d \\r \
| grep -v '^Active code page: 437' 2>/dev/null \
| sed \
-e 's/\s*$//' \
-e 's/[ .]*:/:/' \
-e 's/^\(Windows IP Configuration\)/\1:/' \
-e 's/\(DNS Suffix Search List\|Default Gateway\): \([^ ]*\)$/\1: \n - \2/' \
-e 's/^\( *\)\([^ :][^:]*\)$/\1- \2/' \
| grep -v '^$'
見るとわかりますがリスト化する対象が力業なので、NICなどによっては追加対応が必要かもしれません。
私の環境では問題なく動いているので、うまくいかない場合にはipconfig.exeの出力をコメントで教えていただくと対応できるかもしれません。
jsonへの変換
-
上記のスクリプトはyaml出力のみでjsonでの出力オプションはつけていません。j
jsonが必要な場合、
ipconfig | yq -o jsonのように間にyqを挟むことで対応します。jqでの加工などjsonへ変換する場合のほうが多そうなので、デフォルトでjson出力オプションをつけてもよいかもしれません。
属性名について
jqで扱う際に、属性名にブランクがあるとquoteする必要があるので、ブランクを消したほうが便利な気もします。
が、出力された値をあまりいじりたくないのと、sedなどで単純に置き換える方法が思いつかなかったので、あきらめました。
自分のIPアドレスの取得
このipconfigを利用すると、自分のIPアドレスの取得は以下のように記述できます。
デフォルトゲートウェイのあるIPアドレスをWindows側のIPとして取得しています。
#! /bin/sh
ipconfig | yq -o json | jq -r \
' to_entries[].value '\
'| select(."Default Gateway" != null)."IPv4 Address" '\
'| sub("\\(Preferred\\)"; "")'
Windowsだと複数のデフォルトゲートウェイを設定できるので、その場合にはうまくいきません。