Excel for macOSのUTF-8 CSVからBOMを自動で除去する

Mac

ツイでそんな話題があったので、ちょっとAutomatorで書いてみた。

まず、Automatorでremove_bom.workflowというフォルダアクションを作成する。

ext=${1##*.}
if [ "${ext}" == "csv" ]; then
	bom_exists=$(grep -l ^$'\xef\xbb\xbf' $1)
	if [ "$bom_exists" == "$1" ]; then
		cat $1 | (rm $1; sed '1s/^[^[:alnum:]]*//' > $1)
	fi
fi

拡張子がcsvで、BOMが付いていたら、alnum(Alpha Numeric)じゃない先頭の文字列を消す、というロジック。macOSのsedではhexdecimalな検索が出来ないのでこうしてるが、brew install sedすればもっと確実に、

sed -i '1s/^\xef\xbb\xbf//'

すりゃ良い。

で、こいつを保存したら「監視したいフォルダ」を右クリックしてコンテキストメニューから「フォルダアクション設定…」を選ぶ。

「サービスを確認」ダイアログで[サービスを実行]ボタンをクリック。

「フォルダアクション設定」が表示されたら、右側の[スクリプト]の下にある[+]ボタンをクリック。

「関連付けるスクリプトを選択:」で先に保存したアクションを選択する。

さて、Excelで、”CSV UTF-8 (コンマ区切り) (.csv)”で保存してみよう。

うまく行けば、保存したCSVは次の瞬間にBOMが除去されているので、hexdumpコマンドで見るとこの通り”EF BB BF”が先頭から消えているはず。

これはmacOSのfseventsdをベースにしているので、既に存在するファイル名に対してExcelで「別名で保存」して上書きすると動かない弱点があるので注意。

実はこれ、同じ要領でmacOSのスクショの名前を変更するのに使っていたので、似たようなものだったりする。”ScreenShots YYYY-MM-DD hh.mm.ss.png“という名前で保存されてしまって、この日時部分がscreenshotsコマンドはカスタマイズ出来ないが、個人的に”YYYYMMDDhhmmss.png“というファイル名の方が都合がよく、スクショをとったら自動的にファイル名をrenameしてる。

追記(2019.07.23):さらにバグを見つけたので修正。

prefix=$(defaults read com.apple.screencapture name)
dname=$(dirname "$1")
fname=$(basename "$1")
ext=".$(defaults read com.apple.screencapture type)"


if [ "${fname:0:${#prefix}}" == "$prefix" ]; then
	if [ "$ext" == "." ]; then
		ext=$(echo "$fname" | sed 's/.*\(\.[^\.]*\)$/\1/')
	fi

	to=(`echo -n "$fname" | sed "s/${prefix}\ \([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)\ \([0-9]\{1,2\}\)\.\([0-9]\{2\}\)\.\([0-9]\{2\}\)${ext}/\1 \2 \3 \4 \5 \6/"`)
	yyyymmdd="${to[0]}${to[1]}${to[2]}"

	# Why Apple doesn't use zero-filled 2 digits 'hour'?
	hhmmss=$(printf %02d ${to[3]})${to[4]}${to[5]}
	mv "${1}" "${dname}/${yyyymmdd}${hhmmss}${ext}"
fi

追記(2019.07.18):バグを見つけたので修正版。

prefix=$(defaults read com.apple.screencapture name)
dname=$(dirname "$1")
fname=$(basename "$1")
ext=".$(defaults read com.apple.screencapture type)"
if [ "$ext" == "" ]; then
	ext=$(echo "$fname" | sed 's/.*\(\.[^\.]*\)$/\1/')
fi
if [ "${fname:0:${#prefix}}" == "$prefix" ]; then
	to=(`echo -n "$fname" | sed "s/${prefix}\ \([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)\ \([0-9]\{1,2\}\)\.\([0-9]\{2\}\)\.\([0-9]\{2\}\)${ext}/\1 \2 \3 \4 \5 \6/"`)
	yyyymmdd="${to[0]}${to[1]}${to[2]}"
	# Why Apple doesn't use zero-filled 2 digits 'hour'?
	hhmmss=$(printf %02d ${to[3]})${to[4]}${to[5]}
	mv "${1}" "${dname}/${yyyymmdd}${hhmmss}${ext}"
fi

こちらは旧バージョン。

fname=$(basename $1)
if [ "${fname:0:11}" == "ScreenShots" ]; then
	to=$(echo $1 | sed 's/ScreenShots\ \([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)\ \([0-9]\{2\}\)\.\([0-9]\{2\}\)\.\([0-9]\{2\}\)\.png/\1\2\3\4\5\6.png/')
	mv "${1}" ${to}
fi

なお、これらのフォルダアクションは、~/Library/Workflows/Applications/Folder Actions/に保存されている。