wkhtmltopdf / wkhtmltoimageを使って画面キャプチャするバッチスクリプト
を先日書きましたが、こういった類のバッチを書いたのは初めて?だったので色々ハマりました。
今後同じ過ちを繰り返さぬように、ここに記したいと思います。
変数の即時展開, 遅延環境変数 にハマった(バッチスクリプト)
ハマりポイント
for /f "skip=1 tokens=1,2,3* delims=," %%i in (%TARGET%url.csv) do ( set valid=false if %%k == pdf set valid=true if %%k == img set valid=true if %valid% == true ( # ここにこない!! call :OUTPUT_FILE %%i %%j %%k ) else ( echo ----- [error] Output type Please specify the pdf or img ----- ) )
最初こう書いていたんですが、validがtrueにならずにかなり悩みました。
でもこの辺インターネッツはやっぱり凄くて調べてみると以下の記事が見つかり解決できました。
変数の中身は、その行を読み込んだときに値に置き換わる
もひとつ重要なことに、上記の変数はfor文の中などでは定数のように振る舞います。つまり、for文の実行前に%変数%が静的に展開されます。実行中に値を変化させたい場合は、setlocal enabledelayedexpansion を宣言します。遅延環境変数と呼ばれてるようです。そして、変数を参照する時には!変数!のように、感嘆符で囲みます。
バッチファイルは1行(1文)が読み取られたときに変数の展開が終わる(即時展開)から、最初のset valid=false
でsetされたvalidは、if文の評価時で既にfalseになっているというわけです。
と、ざっくり自分なりに噛み砕いてみましたけど、意味不明なのでとりあえずコードで。
# このforはここから for /f "skip=1 tokens=1,2,3* delims=," %%i in (%TARGET%url.csv) do ( set valid=false # forの1文が読み込まれたときにfalseになる(即時展開) if %%k == pdf set valid=true # 上書きされない if %%k == img set valid=true # 上書きされない if false == true ( # つまりこういうことになっている call :OUTPUT_FILE %%i %%j %%k ) else ( echo ----- [error] Output type Please specify the pdf or img ----- ) ) # ここまでで1文
解決方法
setlocal enabledelayedexpansion
を書き、その値を使う時(評価したい時)にその変数を! (エクスクラメーションマーク)
で囲むと展開されるようになる。
setlocal enabledelayedexpansion # これな for /f "skip=1 tokens=1,2,3* delims=," %%i in (%TARGET%url.csv) do ( set valid=false if %%k == pdf set valid=true if %%k == img set valid=true if !valid! == true ( # !で囲むとこの時に展開(評価)される call :OUTPUT_FILE %%i %%j %%k ) else ( echo ----- [error] Output type Please specify the pdf or img ----- ) ) endlocal
CSVの改行コードでハマった(bash)
バッチスクリプトが動いたので、Macとか(bashが使える環境)でも使えるようにシェルも書いたのですが、そこでもハマりました。
Windowsに合わせるためにCSVファイルの改行コードはCRLF
のまま扱っていたのですが、そこがハマりポイントでした。
ハマりポイント
# Shift_JISをUTF-8に変換 | 2行目から読み込み exec < <(iconv -f SHIFT-JIS -t UTF-8 $TARGET/url.csv | tail -n +2) while read LINE do row=(`echo "$LINE"`) url=${row[0]} file_name=${row[1]} type=${row[2]} # 最後の要素 if test ${type} = "pdf" || test ${type} = "img"; then # ここにこない!! else # 全部こっちにくる!! fi done
最初こう書いていて、CSVから読み込んでいる最後の要素を判定していたのですが、すべてfalse!!
なんでやねん。これもあれか、遅延的なやつか、せやろ?
と思って試してみても変わらず(むしろエラー)。。
なんでだよ〜と思いながらも、file_nameで試してみると上手く判定されるではありませんか。
つまり、行末の要素だけ上手くいかないと。つまり、改行コードだと。
シェルスクリプト CRLF
でググってみると、
- 改行コードがCRLFだと行末に
^M
がつく - 改行判断の際に余った
CR
をコマンドの一部として解釈しようとする
みたいな情報がわんさか出てきました。
参考:シェルスクリプトを書くときに気をつける9箇条 – Qiita
CSVファルをcat
$ cat -e url.csv URL,M-^CtM-^C@M-^CCM-^CM-^KM-^V#(M-^JgM-^R#M-^NqM-^V#M-^B#),M-^OoM-^W##-^C^M-^CCM-^Cv(pdf or img)^M$ https://www.google.co.jp/,google,pdf^M$ http://www.yahoo.co.jp/,yahoo,img^M$ https://www.google.co.jp/,google,img^M$ https://ja.wikipedia.org/wiki/M-^S#####{M-^L###^ChM-^CM-^AM-^CCM-^CM-^S,M-^S#####{M-^L###^ChM-^CM-^AM-^CCM-^CM-^S,pdf^M$
ファイルの中を見てみると〜、、うん。確かについてる。ついてるよ〜〜。
改行コードによって判定が上手く言っていなかったと思われます。
解決方法
つまり、改行コードをCRLF
からLF
に変換すればいいじゃない。
# Shift_JISをUTF-8に変換 | 改行コードをLFに変換 | 2行目から読み込み exec < <(iconv -f SHIFT-JIS -t UTF-8 $TARGET/url.csv | tr -d \\r | tail -n +2) while read LINE do row=(`echo "$LINE"`) url=${row[0]} file_name=${row[1]} type=${row[2]} # 最後の要素 if test ${type} = "pdf" || test ${type} = "img"; then # ここにきた〜〜〜!! else fi done
改行コードをLFに変換
tr -d \\\r
これは単純にCRLFからCRを削除するという命令です。
まとめ
- 変数の即時展開, 遅延環境変数に気をつける(.bat)
- 改行コードに気をつける