Windows 10 Anniversary UpdateからサポートしたWindows Subsystem for Linux(WSL)。その結果としてWindows 10上でもBashを始めとするLinuxコマンドが利用可能になった。本連載ではWSLに関する情報や、Bashから実行するシェルスクリプトを紹介する。

コマンド「ifconfig」に対応

2016年11月9日(現地時間)にリリースされたWindows 10 Insider Preview ビルド14965は、大幅な改善が加わっている。リリースノートを読めば一目瞭然だ。Windows 10の環境変数PATHが既定でBUW(Bash on Ubuntu on Windows)のPATHに加わるなど、多くの修正とバグフィックスがなされたものの、注目すべきはNetlinkソケットのサポートだ。

BUW上でコマンド「ifconfig」が動作するようになった

公式ブログ「Windows Command Line Tools For Developers」の記事では、コマンド「ifconfig」の実行例を披露している。ロジックとしては仮想デバイス「/proc/net/dev」経由でNetlinkソケットにアクセスし、ifconfigの実行を実現。もっとも現時点での完成度は低く、MicrosoftはBSoD(BlueScreen of Death)を引き起こす可能性があると警告をうながした。より詳しいロジックについては、公式ブログ「WSL Networking」の記事が参考になる。冗長になるため本連載では割愛するが、ソケットの動きについて解説されているので、興味がある方は参考にしてほしい。

イベントソースを可視化する

さて、前回は「イベントビューアー」に登録されたイベントログをCSV化し、状態を可視化するシェルスクリプトを紹介した。前回は「レベル」を参照したが、今回は「ソース」に注目する。Windows OSはイベントログを記述する際にどのイベントに連動するか明示するため、イベントソースを登録しなければならない。これらの情報は、HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLogキーで管理され、サブキーとして並ぶApplicationキーやSystemキー、Securityキーには、イベントソースと同名のキーが並んでいる。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Systemキーの内容

各イベントソースは展開可能な文字列値「EventMessageFile」で指定した「%SystemRoot%\System32\IoLogMsg.dll」というメッセージDLLファイルを用いてメッセージを出力しているが、"どの分野のイベントが発生しているか分かる元の情報"と理解して構わない。今回は「ソース」の文字列を抜き出し、イベントソースの登場回数をカウントするシェルスクリプトにチャレンジしてみよう。ただし、動作検証を行ったところ、任意の文字列やフィールドを抜き出すコマンド「cut」が意図どおり動作しなかったため、イベントログをタブ区切りのテキストファイルとして用意している。

「eventvwr.exe」を実行するなどして「イベントビューアー」を起動し、対象とするログのカテゴリーを右クリック。<すべてのイベントを名前をつけて保存>をクリックする。任意のファイル名をテキストボックスに入力して、「ファイルの種類」を「テキスト(タブ区切り)」に変更してから<保存>ボタンをクリック

後はいつもと同じくお使いの環境に合わせて変数の値を変更し、シェルスクリプトに実行権限を与えてからお試し頂きたい。

 #!/bin/bash

 CMDNAME=`basename $0`
 BASEFILE=/mnt/c/Users/kaz/Desktop/System.txt

 function usage() {
    echo "Usage: $CMDNAME [-ls]" 1>&2
    exit 0
 }

 while getopts :ls Option
 do
    case $Option in
        l )
            Flag_L="TRUE" ;;
        s )
            Flag_S="TRUE" ;;
        \?* )
            usage ;;
    esac
 done

 shift $((OPTIND - 1))

 if [ "$Flag_L" = "TRUE" ]; then
    Count_Critical=0
    Count_Alert=0
    Count_Details=0
    Count_Error=0
    Count_Info=0

    for Line in `cat $BASEFILE`; do
        x=`echo ${Line} | cut -f 1`
        case $x in
            "重大" )
                Count_Critical=$((Count_Critical+1)) ;;
            "警告" )
                Count_Alert=$((Count_Alert+1)) ;;
            "詳細" )
                Count_Details=$((Count_Details+1)) ;;
            "エラー" )
                Count_Error=$((Count_Error+1)) ;;
            "情報" )
                Count_Info=$((Count_Info+1)) ;;
        esac
    done

    echo 重大レベルは $Count_Critical 件
    echo 警告レベルは $Count_Alert 件
    echo 詳細レベルは $Count_Details 件
    echo エラーレベル $Count_Error 件
    echo 情報レベルは $Count_Info 件
 fi

 if [ "$Flag_S" = "TRUE" ]; then
    TMPFILE=/mnt/c/Users/kaz/Desktop/$$tmp.txt
    Count=0

    for Line in `cat $BASEFILE`; do
        x=`echo ${Line} | cut -f 3 | grep -e ^Microsoft`
        if [ -n "$x" ]; then
            Array=(`echo $x` "${Array[@]}")
            Count=$((Count+1))
        fi
    done

    orig_ifs=$IFS
    IFS=$'\n'
    Array2=($(echo "${Array[*]}" | sort))
    IFS=$orig_ifs
    for v in "${Array2[@]}"; do
        echo $v >> $TMPFILE
    done
    uniq -c $TMPFILE
    rm $TMPFILE
 fi

今回は前回のコードを残すため、オプションによって動作が変更するようにした。オプション「-l」を付けて実行すると前回と同じ動作を行い、オプション「-s」で今回追加したコードが動作する仕組みだ。そのため、11行目~23行目はBashの内部関数であるgetoptsを使用し、オプションによって動作を振り分けている。なお、getoptsで使用可能なオプション文字列として「:ls」と記述しているが、このコロンは未指定のオプションを使った際に、エラーメッセージの出力を抑制するためのものだ。また、オプション文字列の後ろにコロンを付けると、変数「OPTARG」でオプションの引数を取得し、シェルスクリプト内で利用可能になるが、今回はそこまで複雑な動作は必要ないため、使用していない。

オプション「-l」を付けて実行した状態。前回と同じく「レベル」の件数をカウントする

オプション「-s」を付けて実行すると、「ソース」の登場回数をカウントする

新たに追加したコードは55~76行目。56行目で一時ファイルを定義し、59~65行でエクスポートしたイベントログを標準出力し、3フィールド目の文字列から「Microsoft」で始まる文字列だけを取得。61~64行目のif文は文字列が空白でないことを確認してから配列「Array」に抽出した文字列を格納している。67~70行目は配列の内容をソートし、71~73行目で一時ファイルに結果を出力。そして74行目はソートしたファイルから内容が重複する行を削除するコマンド「uniq」を使い、登場回数をカウントしている。コードを書き終えてから配列のソートは不要であることに気付いたが、このような方法で内部処理できる例として残すことにした。

阿久津良和(Cactus)