ファイル名の整頓

意外と面倒だけど大事なこの項目。
最初に設定すると、あとの手数を減らせるので、今のうちに考えておきます。

必要なもの
患者番号 検査時期 シーケンス ぐらいですかね。

ここはいよいよシェルスクリプトの出番でしょうか。
私の環境はmac なので、zshでいきます。

例によってチャッピーと相談だよ!


出力ファイルの命名規則を以下に設定

<ID>_<検査時期>_<シーケンス>.nii.gz

検査時期は被験者によって違うのと、タイミングをグループ別にするので二段階にして、まずは検査時期→日付8桁の作業としました。

というわけでできたスクリプトは

(T1 / T1C / T2、.nii/.nii.gz/.json を揃えてrename)

#!/usr/bin/env zsh
set -euo pipefail
setopt null_glob

DRYRUN=1
if [[ "${1:-}" == "--run" ]]; then
DRYRUN=0
fi

strip_nii_ext() {
local f="$1"
if [[ "$f" == *.nii.gz ]]; then
print -r -- "${f%.nii.gz}"
else
print -r -- "${f%.nii}"
fi
}

get_pid() { [[ "$1" == "_" ]] && print -r -- "${1%%_*}" || print -r -- "$1"; }

get_ymd() {
local base="$1"
if [[ "$base" =~ ([0-9]{14}) ]]; then
local ts="${match[1]}"
print -r -- "${ts[1,8]}"
else
print -u2 "SKIP (no 14-digit timestamp): $base"
return 1
fi
}

infer_mod_3() {
local s="${1:l}"
if [[ "$s" == "+c" || "$s" == "+ce" || "$s" == "sag+c" || "$s" == "ce" || "$s" == "ce_t1" || "$s" == "t1c" ]]; then
print -r -- "T1C"; return 0
fi
if [[ "$s" == "t2w" || "$s" == "t2_" || "$s" == "t2_tse" || "$s" == "t2tse" || "$s" == "tse" ]]; then
print -r -- "T2"; return 0
fi
if [[ "$s" == "t1w" || "$s" == "t1_" || "$s" == "mpr" || "$s" == "ffe" ]]; then
print -r -- "T1"; return 0
fi
print -r -- "UNK"
}

#NIfTI本体のみ対象(._* を除外)

find . -maxdepth 1 -type f ( -name ".nii" -o -name ".nii.gz" ) ! -name "._*" -print0 |
while IFS= read -r -d '' path; do
f="${path#./}"
base="$(strip_nii_ext "$f")"

pid="$(get_pid "$base")"
ymd="$(get_ymd "$base")" || continue
mod="$(infer_mod_3 "$base")"
[[ "$mod" == "UNK" ]] && { print -u2 "SKIP (mod=UNK): $f"; continue; }

newbase="${pid}${ymd}${mod}"

#1) NIfTIをリネーム

if [[ "$f" == *.nii.gz ]]; then
newnii="${newbase}.nii.gz"
else
newnii="${newbase}.nii"
fi

if [[ -e "$newnii" && "$f" != "$newnii" ]]; then
print -u2 "ERROR (target exists): $newnii <-- from $f"
continue
fi

if (( DRYRUN )); then
print "DRYRUN: $f -> $newnii"
else
[[ "$f" != "$newnii" ]] && /bin/mv -v -- "$f" "$newnii"
fi

#2) 同名jsonがあれば追従リネーム(._json は無視)

oldjson="${base}.json"
newjson="${newbase}.json"
if [[ -e "$oldjson" ]]; then
if [[ -e "$newjson" && "$oldjson" != "$newjson" ]]; then
print -u2 "ERROR (target exists): $newjson <-- from $oldjson"
else
if (( DRYRUN )); then
print "DRYRUN: $oldjson -> $newjson"
else
[[ "$oldjson" != "$newjson" ]] && /bin/mv -v -- "$oldjson" "$newjson"
fi
fi
fi
done

て感じ。


使い方

第一段階終了。

第二段階は日付→基準日判定


だそうです。

変換テーブルを作る。

これは流石に手作業が必要か?

抜き出して条件分岐で設定も可能性あるか。

(別案件として保留)

テーブルに従って日付をリネームするスクリプト


前提:data_map.tsvが準備できている。
チャッピーがわでちょっとハマったっぽい。試行錯誤をくりかえし、sed awkでなんとかなった様子です。


#!/usr/bin/env zsh
set -euo pipefail
setopt null_glob

DRYRUN=1
if [[ "${1:-}" == "--run" ]]; then
DRYRUN=0
fi

SCRIPT_DIR="${0:A:h}"
MAPFILE="${SCRIPT_DIR}/date_map.tsv"

if [[ ! -f "$MAPFILE" ]]; then
print -u2 "ERROR: mapping table not found: $MAPFILE"
exit 1
fi

strip_ext() {
local f="$1"
if [[ "$f" == *.nii.gz ]]; then
print -r -- "${f%.nii.gz}"
elif [[ "$f" == *.nii ]]; then
print -r -- "${f%.nii}"
elif [[ "$f" == *.json ]]; then
print -r -- "${f%.json}"
else
print -r -- "$f"
fi
}

add_ext() {
local base="$1" orig="$2"
if [[ "$orig" == *.nii.gz ]]; then
print -r -- "${base}.nii.gz"
elif [[ "$orig" == *.nii ]]; then
print -r -- "${base}.nii"
elif [[ "$orig" == *.json ]]; then
print -r -- "${base}.json"
else
print -r -- "$orig"
fi
}

ymd -> label をTSVから取得(コメント行は無視)

lookup_label() {
local ymd="$1"
/usr/bin/awk -F'\t' -v d="$ymd" '
$0 ~ /^#/ {next}
$1 == d {print $2; exit}
' "$MAPFILE"
}

while IFS= read -r -d '' path; do
f="${path#./}"
base="$(strip_ext "$f")"

期待形式:PID_YYYYMMDD_MOD(後ろに _... があっても保持)

if [[ "$base" =~ '^([A-Za-z0-9]+)([0-9]{8})(T1C|T1|T2)(_.+)?$' ]]; then
pid="$match[1]"
ymd="$match[2]"
mod="$match[3]"
tail="${match[4]:-}"
else
continue
fi

label="$(lookup_label "$ymd")"
if [[ -z "${label:-}" ]]; then
print -u2 "SKIP (date not in map): $f (ymd=$ymd)"
continue
fi

newbase="${pid}${label}${mod}${tail}"
new="$(add_ext "$newbase" "$f")"

[[ "$f" == "$new" ]] && continue
if [[ -e "$new" ]]; then
print -u2 "ERROR (target exists): $new <-- from $f"
continue
fi

if (( DRYRUN )); then
print "DRYRUN: $f -> $new"
else
/bin/mv -v -- "$f" "$new"
fi
done < <(
/usr/bin/find . -maxdepth 1 -type f
( -name ".nii" -o -name ".nii.gz" -o -name ".json" )
! -name "._" ! -name ".DS_Store" -print0
)

いいなと思ったら応援しよう!