Bashを任意の場所で開く
BUW(Bash on Ubuntu on Windows)を使用する場合、通常はLinuxのホームディレクトリとなる「%LOCALAPPDATA%\lxss\home{ユーザー名}」フォルダーが開く。だが、任意のフォルダーで使用したいという場面は少なくない。findstrコマンドよりもgrepコマンドで文字列にマッチする行を抽出したい場合、現在のフォルダーからBashを起動できれば便利だろう。この問題はレジストリを操作すれば、簡単に解決できる。少々煩雑になるが、以下にその手順を紹介しよう。
以上で操作は終了だ。Bashを起動したいフォルダーやデスクトップの何もないところを[Shift]キーを押しながら右クリックすると、コンテキストメニューに<Open Bash>が加わったことを確認できるだろう。こちらをクリックすれば、Bashが起動する。具体的にはcmd.exe=コマンドプロンプトを起動し、pushdコマンドでカレントフォルダーを移動。その結果が正常に終了した場合に、bash.exeを起動する仕組みだ。今回は文字列値「Extended」を追加することで、[Shift]キーを押した時でないと項目が現れない設定を施しているが、常に表示させる場合は文字列値「Extended」は追加しない。
なお、追加したレジストリエントリーを削除すれば、コンテキストメニューの項目は消えるが、面倒な場合は下記囲みの内容を、テキストエディターで下記囲みの内容を入力し、ファイル名の拡張子に「.reg」を付けてからUTF-16(BOM付き)で保存。作成したレジストリファイルをダブルクリックすれば、各エントリーを数ステップで削除できる。
Windows Registry Editor Version 5.00
[-HKEY_CLASSES_ROOT\Directory\Background\shell\Bash]
[-HKEY_CLASSES_ROOT\Directory\shell\Bash]
[-HKEY_CLASSES_ROOT\Drive\shell\Bash]
[-HKEY_CLASSES_ROOT\LibraryFolder\Background\shell\Bash]
BashからExcelブックにアクセスする
さて、今回は新しいシェルスクリプトにチャレンジしよう。日常業務でExcelを使う機会は多いと思うが、Excelブック(ワークシート)の実体はZIP形式ファイルであることをご存じだろうか。以前はバイナリ形式だったものの、Excel 2007からXMLベースに切り換え、関連ファイルをZIPでパッケージングしたファイル形式に切り替わっている。unzipコマンドをインストールし、展開するといくつかのフォルダーとファイルが展開されるはずだ。フォルダーをたどっていくと「xl\worksheets」フォルダーに「sheet1.xml」「shhet2.xml」……と、Excelブックのワークシートと同じファイル名が確認できる。だが、こちらはセルなどの設定内容であり、セルに入力した数値や文字列は含まれていない。
すべてのバージョンで確認した訳ではないが、Excel 2016で作成したExcelデータの場合、数値や文字列は「xl\sharedStrings.xml」に含まれている。基本的には改行を含まないXML形式ファイルだが、セル内の文字列に「&」含まれると、エンティティ参照(XML上でダブルクォーテーションなどを扱うエスケープ文字)が加わり、「&」が置き換わる部分のみ改行が加わる仕組みだ。
それではここから文字列を抜き出してみよう。今回はsedではなく、改行について柔軟に対応できるperlを利用したシェルスクリプトを作成した。いつもどおりvimなどで下記の内容を作成し、chmodコマンドで実行権限を付加してほしい。
#!/bin/bash
BaseDir=/mnt/c/Users/kaz/Desktop
BaseFile=$BaseDir/Book1.xlsx
TmpDir=/tmp/_TMP
OutputFile=$BaseDir/Output.txt
if [ ! -d $TmpDir ]; then
mkdir $TmpDir
fi
unzip -qq $BaseFile -d $TmpDir
cat $TmpDir/xl/sharedStrings.xml | perl -pe 's/\&/&/g; s/></>\n</g; s/[\n|\r]//g; s/></>\n</g; s/<[^>]*>//g' | perl -pe 's/^\n//g' > $OutputFile
rm -rf $TmpDir
perlのオプションとしてスクリプトを実行する「e」と結果を標準出力する「p」を組み合わせ、正規表現を用いてワンライナーで行っている。ただし、改行が正しく削除されなかったので、パイプを経由して再びperlで削除した。このスクリプトを実行すると、変数「BaseFile」で指定したExcelブックから文字列だけを変数「OutputFile」で定義しているテキストファイルに出力する。
もちろんExcel本体を使えばタブやカンマで区切ったCSVファイルを出力できるため、このようなシェルスクリプトを用いる場面は少ないだろう。だが、実行するだけでファイル展開から文字列の抜き出し、後処理(展開ファイルの削除)までのワンストップで実行できる点は、シェルスクリプトならではだ。
阿久津良和(Cactus)