有馬総一郎のブログ

(彼氏の事情)

2017年08月04日 23:23:29 JST - 2 minute read - MS-DOS

batファイルで列を行に変換

時代は、もはやPowerShellとはいえ、お手軽に書けるのはMS-DOS。ちょちょいのちょいで書けると思ったら、意外なトラップがあったので、メモる。

select_table.log

HEADER1	HEADER2	HEADER3
-------	-------	-------
value=a	value=b	value=c
(1件ヒットしました。)

のようなSQLの結果を出力したファイルがあったとする。

区切り文字タブとして、横に列として展開されているので、縦にしたい。まあ、エクセルに貼り付けて、形式を指定して貼り付けなおすというのが一番パッと思いつく方法であるけれども、それをbatファイルでやろうとする。

@echo off
type nul>TATE.txt
for /f "skip=2 eol=( delims=" %%I in (select_table.log) do call :YOKO_TO_TATE %%I
exit
:YOKO_TO_TATE
:BEGIN
if "%1" == "" goto END
echo %1=%2>>TATE.txt
shift
shift
goto BEGIN
:END
exit /b

結果から書けばこうなった。

value=a
value=b
value=c

と出力された TATE.txtが作成される。

初めは :YOKO_TO_TATEをもっとシンプルに書けると思い、

:YOKO_TO_TATE
for %%I in (%*) do echo %%I>>TATE.txt
exit /b

としたのだけど、何故か

value
a
value
b
value
c

と出力されてしまう。というのも、MS-DOSでは、スペースやタブだけでなく、ご丁寧に=,も区切り文字として認識されてしまう。

YOKO_TO_TATEの引数としては、value=1 value=2 value=3と正しく渡ってきてはいるが、それを配列として(%*)と展開しようとすると、タブだけでなく=も区切り文字として認識されて、value 1 value 2 value 3の配列と認識されてしまった。

これを解消するには"value=1" "value=2" "value=3"とダブルコーテーションで括るしかない。上記の例のように、列三つと少なく、固定であれば

@echo off
type nul>TATE.txt
for /f "skip=2 eol=( tokens=1-3" %%I in (select_table.log) do call :YOKO_TO_TATE "%%I" "%%J" "%%K"
exit /b
rem 横を縦に変換
:YOKO_TO_TATE
for %%I in (%*) do echo %%~I>>TATE.txt
exit /b

でも可能ではある。でも、それだと列が多くなると大変だし、列の数が固定でなければならない。

結果、一番上の text1.batのようにした。shiftで引数を繰り上げながら、echo %1 %2で二つの引数を、再度=で繋げて出力する。二度手間で、かつ、パッと見、意味が分かりにくいものとなってしまった。

とはいえ、=を含む引数配列というパターンがレアであって、for %%I in (%*) doで可能なケースが殆どだろう。

まあ、MS-DOSのそういった癖を知った上で付き合っていくのが、まあ楽しかったりするんだけどね、制限あるなかどうやるか?みたいな。PowerShellは、自由度が高すぎて、どう付き合っていったらいいのか、分かりにくかったりするのよね…