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

PowerShellコマンドレットをシェルスクリプトから利用する

最新のWSLではWindowsの環境変数PATHをBash on Ubuntu on Windows (BUW)で引き継ぎ、bash上でコマンドプロンプトやWin32アプリケーションの実行が可能になった。つまり、BUW上でMS-DOSのバッチファイルやPowerShellのシェルスクリプトが実行できるという意味である。

BUW上でPowerShellのコマンドレットを実行した状態。Get-EventLogコマンドレットでイベントログを確認できる

だが、PowerShell上の環境がそのまま使える訳ではない。例えばPowerShell環境は強力なコマンドレットやパラメーターの補完機能を備えているが、それらはBUW上で使用不可能。また、現在はパイプ処理がサポートされていないため、出力結果を加工するFormat-ListやCSVファイルとして出力するExport-CSVといったコマンドレットは使えない。

パイプ処理の動作は確認できなかった

Bashのシェルスクリプト上でPowerShellのコマンドレットが使えると、Windows 10環境の情報取得や設定変更など、管理面が大幅に強化されるため、今後の機能強化を期待したいところだ。

さて、既存のシェルスクリプト上からPowerShellを単独で呼び出すことは可能である。そこでPowerShellのスクリプトを作成し、そのスクリプトをBashのシェルスクリプトから呼び出すことで、イベントログの出力手順を自動化することにした。なお、PowerShellはセキュリティを強化するため、既定ではスクリプトを実行できない。PowerShell上で「Set-ExecutionPolicy RemoteSigned」を実行して、スクリプトの実行ポリシーを変更するか、「設定」の<更新とセキュリティ/開発者向け>に並ぶ、<署名せずに実行するローカルPowerShellスクリプトを許可するように、実行ポリシーを変更します。リモートスクリプトには署名が必要です>にチェックを入れてほしい。

「設定」から<更新とセキュリティ>→<開発者向け>と開き、<署名せずに実行するローカルPowerShellスクリプトを許可する~>にチェックを入れる

では、今回のPowerShellスクリプトおよびBashシェルスクリプトを紹介する。PowerShellスクリプトはWindows 10上のテキストエディターでもBUW上のvimなどでもかまないので、以下の内容をコピー&ペーストし、「Begin1.ps1」として保存してほしい。

 Get-EventLog System | Select-Object $Args[0]

Bashのシェルスクリプトだが、いつもと同じく実行権限を与えてからお試し頂きたい。後はPowerShellスクリプトと同じ場所にあるBashシェルスクリプトを実行する。

 #!/bin/bash

 CMDNAME=`basename $0`

 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

    orig_ifs=$IFS
    IFS=$'\n'
    for Line in `powershell.exe ./Begin.ps1 EntryType`; do
        x=`echo ${Line} | grep -v -e '^\s*$' -e '^EntryType' -e '^------'`
        case $x in
            *Critical* )
                Count_Critical=$((Count_Critical+1)) ;;
            *Warning* )
                Count_Alert=$((Count_Alert+1)) ;;
            *Verbose* )
                Count_Details=$((Count_Details+1)) ;;
            *Error* )
                Count_Error=$((Count_Error+1)) ;;
            *Information* )
                Count_Info=$((Count_Info+1)) ;;
        esac
    done
    IFS=$orig_ifs

    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 `powershell.exe ./Begin.ps1 Source`; do
        x=`echo ${Line} | grep -v -e '^\s*$' -e '^Source' -e '^------'`
        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

コードをご覧になるとお分かりのとおり、今回の変更点は必要最小限にとどめているが、31行~32行目および48行目で、変数IFSの内容を変更していることに気付かれることだろう。Bashは既定で空白やタブ、改行を区切り文字としているが、ここでは既存の設定を待避させ、区切り文字を改行に変更。そして、元に戻している。この処理はPowerShellスクリプトの内容をBUW上で正しく処理するために必要だ。

続いて33行目のfor文では、そのPowerShellスクリプトに引数を付けて呼び出している。今回はSelect-Objectコマンドレットを併用し、引数として列挙するオブジェクトを指定する方法をも用いた。次の34行目で行う変数xへの代入も、あらかじめgrepで空行や不要な文字列を除外している。なお、35~45行目のcase文もロジックは一緒だが、出力された文字列に合わせるためパターンの内容を変更した。57~78行目で始まるオプション-sの動作も若干の変更を加えているが、ロジックは前回、変更箇所もオプション-lと同じため割愛する。

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

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

このようにBUW環境にとどまらず、Windows 10のコマンドなどを組み合わせることで、これまでは冗長になりがちなコードも比較的簡単に記述可能になった。これこそWSLで実現できる新たな開発環境 と言えるだろう。

阿久津良和(Cactus)