FileMakerからMySQLへのMigration、および、FileMakerのオブジェクトをS3にアップロードするの続きになる。
FileMakerのテキストフィールド内の改行は0x0B (VT:垂直タブ)で繰り返しフィールドの区切り文字は0x1D。このままでは困るので他システムに移行しても使えるよう0x0D, 0x0A (CR/LF)に変換する。また、クラウド用にタイムスタンプをUTCに変換する。
処理はMySQLにインポートする前にアップロードしたCSVファイルをコピーしているシェルスクリプトがあるのでその中で行う。
FileMakerの出力と同じダブルクォート付きCSVでタイムスタンプで0秒が省略されるところに対応している。大きなファイルでサーバーがハングアップするので改良しているがこれでも1ヶ月程度のデータが限界っぽい(Claude 3.5に聞いた結果を貼り付けてるだけですが検証済みです)。
#!/bin/bash
DATE=$(<date.txt)
sh find_new_file.sh -v "$DATE" temp/csv/ > result.txt
RESULT=$(<result.txt)
if [ -z "$RESULT" ]; then
exit 0
fi
cd temp/csv
# 変換とコピーを行う関数
convert_and_copy() {
local input_file="$1"
local output_file="/var/lib/mysql-files/$(basename "$input_file")"
LC_ALL=en_US.UTF-8 awk '
BEGIN {
FS=OFS=","
FPAT="([^,]+)|(\"[^\"]+\")"
}
function is_timestamp(str) {
gsub(/^"|"$/, "", str)
return str ~ /^[0-9]{4}(\/|-)[0-9]{2}(\/|-)[0-9]{2} [0-9]{1,2}:[0-9]{2}(:[0-9]{2})?$/
}
function convert_timestamp(str) {
orig_str = str
gsub(/^"|"$/, "", str)
gsub(/-/, "/", str)
if (str !~ /:[0-9]{2}$/) {
str = str ":00"
}
cmd = "date -d\"" str " JST\" -u \"+%Y-%m-%d %H:%M:%S\""
cmd | getline utc_time
close(cmd)
return (orig_str ~ /^".*"$/) ? "\"" utc_time "\"" : utc_time
}
{
for (i = 1; i <= NF; i++) {
if (is_timestamp($i)) {
$i = convert_timestamp($i)
}
}
# 0x1Dが1つ以上連続する部分を1つの改行に置換
gsub(/\x1D+/, "\r\n")
# VTを改行に変換
gsub(/\v/, "\r\n")
print
}
' "$input_file" > "$output_file"
echo "Converted and copied: $input_file"
}
# 並列処理の管理
MAX_JOBS=4 # 同時に実行するジョブの最大数
CURRENT_JOBS=0
# 全ての.csvファイルに対して変換とコピーを実行
for file in *.csv; do
if [ -f "$file" ]; then
# 同時実行ジョブ数が最大数に達しているか確認
while [ $CURRENT_JOBS -ge $MAX_JOBS ]; do
sleep 1
CURRENT_JOBS=$(jobs -r | wc -l)
done
# 新しいサブシェルでファイル処理を実行
(
convert_and_copy "$file"
) &
CURRENT_JOBS=$((CURRENT_JOBS + 1))
fi
done
# 全てのバックグラウンドジョブの完了を待つ
wait
echo "All files have been processed."
# MySQLにインポート
mysql --defaults-file=~/.my.cnf < csvimp.sql
# 現在のディレクトリをホームディレクトリに変更
cd ~/
# 日付を更新
date +%Y%m%d%H%M > date.txt