# [メタ情報] # 識別子: DAS管理用sh作成_exe # システム名: DAS管理用sh作成 # 技術種別: Misc # 機能名: Misc # 使用言語: ShellScript # 状態: 実行用 # [/メタ情報] 要約:このシェルスクリプトは、DAS管理用の実行ランナーで、Googleスプレッドシート「tanada」を基準に処理を制御する。環境変数でWebhookやトークン、シートIDを指定し、排他ロックとログ管理を行いながら実行される。シートをCSVとして取得し、各行の処理ID・転出パス・容量上限(TB)を読み取る。対象パスの使用容量を算出し、上限に達しているかを判定して、その結果をWebhookへ通知する。実際の移動処理は行わず、状態確認と通知に特化した構成になっている。 /Users/xxxxxxxxm1/das/das_runner.sh #!/bin/bash set -euo pipefail : "${DAS_WEBHOOK_URL:?Set DAS_WEBHOOK_URL}" : "${DAS_TOKEN:?Set DAS_TOKEN}" : "${DAS_SHEET_ID:?Set DAS_SHEET_ID}" # 例: 1i6fBlPSORhyCbwv6a7K1x4Jk3rsoKrLkJlGZDGWjj5c : "${DAS_TANADA_GID:?Set DAS_TANADA_GID}" # tanadaシートのgid(数値) BASE_DIR="$HOME/das" LOG_DIR="$BASE_DIR/logs" LOG_FILE="$LOG_DIR/das_runner.log" LOCK_DIR="$BASE_DIR/.das_lock" mkdir -p "$LOG_DIR" DRY_RUN=0 [[ "${1:-}" == "--dry-run" ]] && DRY_RUN=1 ts() { date "+%Y-%m-%d %H:%M:%S"; } log() { echo "[$(ts)] $*" | tee -a "$LOG_FILE" >/dev/null; } json_escape() { python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' "$1" } post_status() { local state="$1" job_id="$2" message="$3" local needs_dedupe="${4:-false}" dedupe_count="${5:-0}" dup_report="${6:-}" local payload payload=$(cat </dev/null; do sleep 5 done echo $$ > "$LOCK_DIR/pid" 2>/dev/null || true log "LOCK acquired pid=$$" } release_lock() { rm -rf "$LOCK_DIR" 2>/dev/null || true log "LOCK released" } # df -k の used(KB) から TB(10^12)換算 used_tb_of_path() { local p="$1" df -k "$p" 2>/dev/null | awk 'NR==2{printf "%.2f", $3*1024/1e12}' } # -------- tanadaシートをCSVで取得(Google Sheets export)-------- # D列=転出パス / I列=限度_TB / A列=処理id の想定 fetch_tanada_csv() { local url="https://docs.google.com/spreadsheets/d/${DAS_SHEET_ID}/export?format=csv&gid=${DAS_TANADA_GID}" curl -sS -L "$url" } # CSVを読み、(処理id, 転出パス, 限度_TB) を出力(ヘッダ除外) # NOTE: CSVはカンマ区切り・ダブルクォートあり得るので python で安全にパース iter_tanada_rows() { python3 - <<'PY' import csv,sys,io data=sys.stdin.read() r=csv.reader(io.StringIO(data)) rows=list(r) if not rows: sys.exit(0) # 1行目はヘッダ想定 for i,row in enumerate(rows[1:], start=2): # 必要列が足りない行はスキップ if len(row) < 9: continue job_id = (row[0] or "").strip() # A: 処理id src = (row[3] or "").strip() # D: 転出パス limit = (row[8] or "").strip() # I: 限度_TB if not job_id or not src: continue # limit が空なら 0 扱い(=常に未達として扱う) if not limit: limit="0" print(job_id, src, limit, sep="\t") PY } main() { with_lock trap 'release_lock' EXIT log "START dry_run=$DRY_RUN (table-driven: tanada sheet)" post_status "TANADA" "START" "tanada start (table-driven)" local csv csv="$(fetch_tanada_csv)" # 1件も取れないときは止める if [[ -z "$csv" ]]; then post_status "IDLE" "ERROR" "tanada CSV empty (check sheet id/gid)" log "ERROR: tanada CSV empty" return 1 fi while IFS=$'\t' read -r job_id src limit_tb; do # srcが存在しない(未マウント等)ならSKIP if [[ ! -d "$src" ]]; then log "$job_id SKIP: src not found: $src" post_status "TANADA" "$job_id" "SKIP: src not found: $src" continue fi used_tb="$(used_tb_of_path "$src")" log "$job_id used=${used_tb}TB limit=${limit_tb}TB src=$src" # used >= limit ? should=$(python3 - <=t else "0") PY ) if [[ "$should" == "1" ]]; then post_status "TANADA" "$job_id" "LIMIT reached: used ${used_tb}TB >= ${limit_tb}TB (NO MOVE in this version)" else post_status "TANADA" "$job_id" "under limit: used ${used_tb}TB < ${limit_tb}TB" fi done < <(printf "%s" "$csv" | iter_tanada_rows) post_status "IDLE" "END" "done" log "END" } main