バッチスクリプトを書いてハマったところ

投稿日:

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)
  • 改行コードに気をつける
投稿日:
カテゴリー: シェル

作成者: shimabox

Web系のプログラマをやっています。 なるべく楽しく生きていきたい。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください