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

着々と完成度を高めつつあるWSL

MicrosoftはWSLの開発においてLTPを利用している。LTPはLinuxの信頼性や堅牢(けんろう)性、そして安定性を検証するテスト環境を実現する「Linux Test Project」の略称。BUW(Bash on Ubuntu on Windows)のリリースノートでは、ビルド更新時に合格を示す"Passing Test"とエラーやスキップなどを示す"non-Passing"の数値を公開している。

今回調べてみると、Windows 10 Creators Updateを目指す最初のOSビルド14901はLTPの結果を公開しておらず、その翌月にリリースしたOSビルド14926から。各数値をまとめたのが下図に示したグラフだが、OSビルド15002のタイミングでパスした項目(青色)が増え、パスしなかった項目(オレンジ色)が減じている。多くの噂(うわさ)をまとめると、Windows 10 Creators Updateのリリースは今春頃となりそうだが、そこまで非パス項目がどれだけ軽減し、一般的なLinuxディストリビューションとの互換性が高まるのか注目したい。

LTPの結果をグラフ化したもの

米国の祝日をコマンドラインから確認する

さて、筆者は海外IT企業のブログを巡回しているが、日によって投稿数が極端に少ない。もちろん各企業が意味もなく記事を投稿するはずもなく、まちまちになってしまうのは至極当然だが、後からフッと気付くのである。「米国は祝日か」と。改めて述べるまでもなく祝日は各国の事情によって異なり、建国や独立を記念すために祝日と定めるケースが少なくない。そのため、日本時間で考えると失敗してしまうのだ。

普段から使っているカレンダーに米国の祝日を登録すれば済む話だが、できることなら予定項目を増やすのは、日々の取材などが見にくくなってしまうため避けたいのが正直なところ。そこで思い浮かんだのが今回のシェルスクリプトである。Googleカレンダーが公開している米国祝日のICS(iCalendar)ファイルをダウンロードし、必要な情報だけ抽出するというものだ。

Googleカレンダーでは各国の祝日をICSファイルとして利用できる

ICSファイルの内容。このようなコアオブジェクトが並ぶ

いつもどおり任意のテキストエディターで、下図に示したコードをコピー&ペーストした後に、chmodコマンドで実行モードを付与してから、シェルスクリプトを実行してほしい。

 #!/bin/bash

 Url='https://calendar.google.com/calendar/ical/ja.usa%23holiday%40group.v.calendar.google.com/public/basic.ics'
 TmpFile=$(mktemp)
 IFS_Backup=$IFS
 IFS=$'\n'

 Array=($(curl -s $Url | sed -n '/^BEGIN:VEVENT/,/^END:VEVENT/p'))
 for Obj in ${Array[@]}; do
    case ${Obj} in
        DTSTART* )
            StrDate=$(echo ${Obj##*:} | tr -d '\r' | tr -d '\n')
            ;;
        SUMMARY* )
            StrSummary=$(echo ${Obj#*:} | tr -d '\r' | tr -d '\n')
            ;;
    esac
    if [ -n "$StrDate" -a -n "$StrSummary" ]; then
        echo $StrDate ' ' $StrSummary >>$TmpFile
        unset StrDate
        unset StrSummary
    fi
 done
 sort $TmpFile
 rm $TmpFile
 IFS=$IFS_Backup

まずはシェルスクリプトの解説から始めよう。9行目は変数「Url」で定義したURLをcurlコマンドで取得し、その内容をsedコマンドに渡している。ここではオプション「-n」を追加してマッチした行のみ出力する処理を加えた。今回の記述ではiCalendarのルールである「『BEGIN:VCALENDAR』で始まる行から『END:VEVENT』で終わる行」を出力している。

その内容を10~24行目のforコマンドでループさせ、行内の文字列に「DTSTART」がある場合は13行目、「SUMMARY」がある場合は16行目の処理を実行させた。先に示した図で分かるように、前者は予定の開始日、後者は予定内容を含んでいるため、変数展開を用いて必要な文字列を抽出している。

前者で用いた変数展開は前方一致でマッチした部分を削除する「最長前置パターンの削除」を利用し、後者は「最短前置パターンの削除」を用いた。変数展開は代替値の使用や文字列長の取得なども可能だが、簡単な文字列抽出であればsedコマンドなどを使わない方が分かりやすい。

 ${変数名%文字列} 最短後置パターンの削除
 ${変数名%%文字列}    最長後置パターンの削除
 ${変数名#文字列} 最短前置パターンの削除
 ${変数名##文字列}    最長前置パターンの削除

なお、パイプで渡しているtrコマンドは変数に代入する前に改行コードを除外するために加えた。19行目~23行目のif文はcase文で取り出した文字列を値として持つ変数「StrDate」「StrSummary」が空か否かを判断し、空でない場合は20~22行目の処理を実行している。また、if文が正しく動作するため、21~22行目ではunsetコマンドで変数の値をクリアさせた。後は結果をmktmpコマンドで作成した一時ファイルに出力し、その内容を予定の開始日で並べ替えている。

こちらが実行結果。取得したICSファイルを元に開始日と祝日の内容を標準出力する

シェルスクリプトを実行すると、米国の祝日が標準出力に書き出されるため、筆者はコマンドを実行して、冒頭で述べたミスを避けられるようになった。筆者は動作を確認していないが、変数「Url」の内容を「https://calendar.google.com/calendar/ical/ja.usa%23holiday%40group.v.calendar.google.com/public/basic.ics」に変更すれば日本の祝日も同じように取得できるはずである。その際は文字列操作時に工夫が必要と思われるが、是非チャレンジしてほしい。

阿久津良和(Cactus)