初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(1)]

名前は「burdock」

シェルスクリプトでファイルのリネームツールを作って……作れたのかなぁ。リネームできてるっちゃあできてますが、画像の形式変換だとかもできちゃうんすけど……。

環境とか注意とか

Ubuntu18.04 VirtualBox6.1.8_GNU bash, バージョン 4.4.20(1)-release (x86_64-pc-linux-gnu)

  • 連想配列が使えるbash専用です。
  • 仕様上、rmだろうとrebootだろうと使えます。事故に注意。
  • 使うのは誰でも自由ですが、バグはもちろん事故も十分にありえるので、そのへんは了承してください。私は責任は持てません。

使用例

1.写真ファイルからexif情報を取り出してリネーム_端末用インターフェースから

(途中でテキストファイルを編集する必要あり)

  1. (--once)1ファイル1実行モードに変更し、
  2. (--data-load)テキストファイルをこのスクリプトが読み込んでシェル変数に格納、
  3. (_suffix,中身はsed)名前と拡張子の間に変数で指定したテキストデータを書き込んで、
  4. (--ext)拡張子編集モードに変更し、
  5. (_small,中身はtr)拡張子を小文字に変換する。


2.ファイルに乱数のサフックスをつけてリネーム_コマンドラインから
  1. シェル変数モードに変更し、
  2. コマンド実行モードを1ファイル1実行モードに切り替え、
  3. サフックスにBash組み込み変数の${RANDOM}を、1ファイルにつき1回展開しながら付加する。
$ ls | burdock --shell --once '_suffix "_${RANDOM}"'
cd -- "/media/sf_003" ;  mv -n -v -- 'diary.txt' 'diary_30078.txt' ; 
cd -- "/media/sf_003" ;  mv -n -v -- 'image.jpg' 'image_3938.jpg' ; 
cd -- "/media/sf_003" ;  mv -n -v -- 'memo.txt' 'memo_13931.txt' ; 
cd -- "/media/sf_003" ;  mv -n -v -- 'picture.jpg' 'picture_9382.jpg' ; 

コマンドライン版の中核スクリプトのみ

と、けっこう量があるんで、どうやるべかと。
プログラマーの世界だと常識な方法とかあるんでしょうけど、私にゃ分からんので、あとでzipで出します。フォントと同じところにでも。
スクリプト1枚で動く中核部分だけ置いときます。もう基本仕様は変わらんと思うから。
ユーザインターフェースのほうは、まだ書き換えが激しくて。公開してもすぐ消しちゃいそうなんす。

#!/bin/bash
#set -x
# ==========================================================================================
# ■ ファイルリネーム 「burdock」Core ver8
# ==========================================================================================

# ---- 許可リスト_ユーザ設定 (うっかりミス防止) -------------------------------------
# 任意コマンド実行時、「${ARROW_LIST_CMD[]}」に
# 明記されたコマンドが先頭に有るときのみ実行を許可する。
# 「_」は組み込みシェル変数とスクリプト用
# 全てのコマンドを許可するなら、「.」を追加する
declare -a -g ARROW_LIST_CMD
ARROW_LIST_CMD=( _ sed tr cut xargs echo printf rev awk date nl iconv nkf )


# ==== 前処理開始 =========================================================================
# 【debug】動作速度測定
SECONDS=0
: '【PATH追加】' && {
SCRIPT_DIR=`dirname $( readlink --canonicalize-existing "${0}" )`
PATH="${SCRIPT_DIR}:${SCRIPT_DIR}/burdockCoreLib:${PATH}"
if ! . coreLib.func ; then
	echo "${0}:組み込みコマンドは無効です。" 1>&2
fi
	}
: '【フラグ初期化】' && { # 一度trueにすると、falseには戻らない
stdinFlag='false'
demoFlag='false'
actionFlag='false'
pipeFullFlag='false'
pipeNameFlag='false'
pipeExtFlag='false'
pipeCsvFlag='false'
arrowlistFlag='false'
cmdAnalysFlag='false'
cmdAnalysFullFlag='false'
databaseFlag='false'
	}
: '【スイッチ初期値】' && { # オプションで切り替わる。命令が無い限り状態を保持する。
# CMD_TARGET を設定
cmdTargetSW='name'
#cmdTargetSW='ext'

# CMD_ACTION を設定
#cmdActionSW='once'
cmdActionSW='loop'

# CMD_VAR_MODE を設定
databaseSW='escape'
#databaseSW='database'
#databaseSW='shell'
	}
: '【stdinは入力されたか?】' && {
if test -p /dev/stdin ; then
	stdinFlag='true'
else
	true
fi
	}

: '【位置パラメータ取得用_シェル関数】' && {
fileLoadFunc(){ # ユーザ指定ファイルを読み込む
# ${1} ファイルパス 指定変数名はファイル名をそのまま使う
local loadFilePath loadFileName NN XX

# 【ザル】 # プロセス置換、通常ファイル、オプション、で分岐
if test -p "${1}" ; then
	echo "${0}:${1}:パイプファイルを検出しましたが、使用できません。通常のファイルからの読み込みにのみ対応します。" 1>&2
	exit 0
elif test -f "${1}" ; then
	true
else
	return 1
fi

loadFilePath=`readlink --canonicalize-existing "${1}"`
loadFileName="${loadFilePath##*/}"

NN='-1'
while read -r XX ; do
	NN=$(( ${NN} + 1 ))
	DATA_BASE[${loadFileName}_${NN}]="${XX}"
done 0< "${loadFilePath}"

DATA_BASE[${loadFileName}]="${DATA_BASE[${loadFileName}_0]}"

return 0
}
	}

: '【位置パラメータ・同等ファイル_取得用_配列宣言】' && {
# 入力された生データをリニアに並べて整理する
declare -a -g POSITION_PARM

# ---- リネームコマンドとそのプロパティ1次配列 ----------------------
# ファイル名・拡張子変換リネームコマンド 統合配列
declare -a -g CMD_1D
# 「loop once」「name ext」適用対象は名前か拡張子か
declare -a -g CMD_ACTION CMD_TARGET
# 「escape database shell」シェル変数の扱い_$=>\\$,DATA_BASE,無変換
declare -a -g CMD_VAR_MODE

# ---- その他_連想配列 ------------------------------------------------
# # ファイル名・拡張子変換 統合配列 x ファイル数
declare -A -g CMD_2D
# データベース_内部作成,外部ファイルLoad
declare -A -g DATA_BASE
	}

: '【位置パラメータとリネームコマンドファイルを線形取得】' && {
# 位置パラメータを配列に読み込む。
# 位置パラメータで指定された、リネームコマンドファイルも読み込む。
# これらを順に配列に格納し、連続したパラメータに整理する。
while test ${#} -ne 0 ; do # ----------------------------------------↓

case "${1}" in
( --loadcmd | --loadcommand ) # ファイルから読み込み

if test -e "${2}" ; then
	shift 1
else
	echo "ERR:${2}:存在しません"
	exit 2
fi

# txt行を配列に格納(空行を詰めながら)
while read -r LL ; do
if test -n "${LL}" ; then
	POSITION_PARM+=("${LL}")
fi
done 0< "${1}"

;;
( * ) # 位置パラメータのファイルload以外すべてを配列に格納
	POSITION_PARM+=("${1}")
;;
esac

shift 1
done # ---------------------------------------------------------------↑

: << '#------------------------------debug'
( IFS='
' ; echo "${POSITION_PARM[*]}" 1>&2 )
#------------------------------debug
	}

: '【位置パラメータ取得】' && {
NN='0'
while test ${NN} -lt "${#POSITION_PARM[@]}" ; do # ------------------------------↓
case "${POSITION_PARM[${NN}]}" in

# ----  コマンド適用対象を切り替え ----------------------------------------
( --name ) # 名前にコマンドを適用する
	cmdTargetSW='name'
;;
( --ext ) # 拡張子にコマンドを適用する
	cmdTargetSW='ext'
;;
# ---  コマンド実行タイプを切り替え ---------------------------------------
( --loop ) # まとめて1度の実行
	cmdActionSW='loop'
;;
( --once ) # 1回の処理で1度実行
	cmdActionSW='once'
;;
# ---- データベース ----------------------------------------------------
( --escape )
	databaseSW='escape'
;;
( --database ) # シェル変数モードON(true)/OFF(false)
	databaseSW='database'
	databaseFlag='true'
;;
( --shell )
	databaseSW='shell'
;;
( --data-load | --database-load ) # ファイルを読み込んで${DATA_BASE[]}を作成
	while fileLoadFunc "${POSITION_PARM[${NN}+1]}" ; do
		NN=$(( ${NN} + 1 ))
		databaseFlag='true'
	done
;;
# ---- 実行モード指定 --------------------------------------------------
( --action )        # リネーム実行
	actionFlag='true'
;;
( --demo ) # リネーム実行予想_mvコマンドを出力
	demoFlag='true'
;;
( --pipe-full ) # 変更予定ファイル名のみをフルパス表示
	pipeFullFlag='true'
;;
( --pipe-name ) # パスを除く名前のみを出力
	pipeNameFlag='true'
;;
( --pipe-ext ) # パスを除く拡張子のみを出力
	pipeExtFlag='true'
;;
( --pipe-csv )
	pipeCsvFlag='true'
;;
# ---- 許可リストを出力 --------------------------------------------
( --arrowlist )
	arrowlistFlag='true'
;;
# ---- コマンドのみの解析結果(入力ファイルと無関係な解析)を出力 --------
( --cmd-analysis )
	cmdAnalysFlag='true'
;;
# ---- 入力ファイル数に従って作成したコマンドを出力する ----------------
( --cmd-analysis-full )
	cmdAnalysFullFlag='true'
;;
# ---- etc --------------------------------------------------------------
( -- )              # 以降の引数を無視する
	break
;;
# ---- リネームコマンドを格納 ------------------------------------------
( * ) # 上記オプション以外は、すべてリネームコマンド1単位と見なす
	CMD_1D+=("${POSITION_PARM[${NN}]}")
	CMD_TARGET+=(${cmdTargetSW})
	CMD_VAR_MODE+=(${databaseSW})
	CMD_ACTION+=(${cmdActionSW})
;;
esac
NN=$(( ${NN} + 1 ))
done # ----------------------------------------------------------------------------↑

: '【コマンドリスト簡易チェック】' && {
if test "${#CMD_1D[@]}" -eq "${#CMD_ACTION[@]}" &&
   test "${#CMD_1D[@]}" -eq "${#CMD_TARGET[@]}" &&
   test "${#CMD_1D[@]}" -eq "${#CMD_VAR_MODE[@]}" ; then
	true
else
	echo 'コマンドリスト受け取りでエラー' 1>&2 ; exit 2
fi

		}
	}


: '【動作モード判定1】' && {
# ---- Mode判定1 --------------------------------------------------------------
if test ${arrowlistFlag} = 'true' ; then   # (1) 許可リスト出力
	Mode='arrowlist'
elif test ${cmdAnalysFlag} = 'true' ; then # (2) コマンドのみ解析
	Mode='cmdAnalys'
elif test ${stdinFlag} = 'false' ; then    # (3) 簡易説明書
	Mode='usage'
elif test ${stdinFlag} = 'true' ; then     # (4) 各種メインプロセス実行へ
	Mode='mainproc'
else
	echo '想定外のエラー' 1>&2
	exit 2
fi

# (1) 許可リスト出力 -> exit
# (3) usage               -> exit

case "${Mode}" in
( arrowlist )
	echo ${ARROW_LIST_CMD[@]}
	exit 0
;;
( usage )
	echo 'ls | '"${0}"' command ....'
	exit 0
;;
esac
	}

: '【疑わしいコマンドを検査】' && {
# 検査用grepコマンドの引数を作成
XX=`echo "${ARROW_LIST_CMD[@]}" | sed -e 's/[^ ]*/-e ^&/g'`
# echo "${XX}" 1>&2 # 【debug】

# (2) コマンド解析 -> exit
# (4) 各種メインプロセス実行前_コマンド解析
case "${Mode}" in
# ----------------------------------------------------------------
( cmdAnalys ) # コマンド単独の詳細検査 --> exit
# ----------------------------------------------------------------
exitCode='0'
NN='0'
# コマンド単独の詳細検査_表示
for NN in "${!CMD_1D[@]}" ; do # CMD_1Dが空のときはNNをリセットできない
  YY=`echo "${CMD_1D[${NN}]}" | grep --invert-match ${XX}`
  if test -z "${YY}" ; then # 許可リスト通過分
    echo ${NN}'	'"${CMD_TARGET[NN]}"'	'"${CMD_ACTION[NN]}"'	'"${CMD_VAR_MODE[NN]}"'	'"pass"'	'"${CMD_1D[NN]}"
  else
    echo ${NN}'	'"${CMD_TARGET[NN]}"'	'"${CMD_ACTION[NN]}"'	'"${CMD_VAR_MODE[NN]}"'	'"block"'	'"${CMD_1D[NN]}"
    exitCode='1'
  fi
done
echo "${#CMD_1D[@]}"'	'${cmdTargetSW}'	'${cmdActionSW}'	'${databaseSW}'	'"--"'	'"NEXT"

# 判定用_終了コード
exit ${exitCode}
;;
# ----------------------------------------------------------------
( mainproc | * ) # 簡易検査 --> 合格した場合はメイン処理へ
# ----------------------------------------------------------------
YY=`for NN in ${!CMD_1D[@]} ; do
	echo "${CMD_1D[${NN}]}"
done | grep --invert-match ${XX}`
# echo "${YY}" 1>&2 # 【debug】
if test -z "${YY}" ; then # **** 合格 メイン処理を実行する ************************
	true
else
	echo "${YY}" | sed -e 's/^/ERR_入力禁止:/' | xargs -I '{}' printf '\033[1;31;40m%s\033[0m\n' '{}' 1>&2
	exit 1
fi
;;
esac
	}

# :::: 各種メイン処理実行が決定 :::::::::::::::::::::::::::::::::::::::::::::::::::

: '【動作モード判定_メイン処理を分岐】' && {
# リネームコマンドが完成次第stderrへ出力して終了する
if test ${cmdAnalysFullFlag} = 'true' ; then
	Mode='renameCommandFull'
# demoモード明示 mvコマンドを出力する
elif test ${demoFlag} = 'true' ; then
	Mode='mvCommand'
# pipeモード 出力ファイル名のみを絶対パス出力する
elif test ${pipeFullFlag} = 'true' ; then
	Mode='pipeFull'
elif test ${pipeNameFlag} = 'true' && test ${pipeExtFlag} = 'true' ; then # パス無しの名前と拡張子を出力
	Mode='pipeNameExt'
elif test ${pipeNameFlag} = 'true' && test ${pipeExtFlag} = 'false' ; then # パス無しの名前を出力
	Mode='pipeName'
elif test ${pipeNameFlag} = 'false' && test ${pipeExtFlag} = 'true' ; then # パス無しの拡張子を出力
	Mode='pipeExt'
# actionを明示されたときのみmvコマンド実行
elif test ${actionFlag} = 'true' ; then
	Mode='Action'
elif test ${pipeCsvFlag} = 'true' ; then
	Mode='Csv'
# stdinが有っただけならばmvコマンドの文字列を出力し、mvコマンド実行はしない。
else
	Mode='mvCommand'
fi
	}

# ===================================================================================
# メイン処理を開始 Demo,Action,Command 共通の前処理
# ===================================================================================

# グローバル変数_配列を宣言
declare -a -g stdinData IN_DIR_FULL
declare -a -g IN_NAME OUT_NAME
declare -a -g IN_EXT_DOT OUT_EXT_DOT IN_EXT

# IFS_BAK="${IFS}"

: '【stdinScan】' && { # stdinの文字列を、配列に格納する
while IFS='' read -r FF ; do #-------------------------------------------------↓

# 【ザル】ファイル、ディレクトリのみ通して、パスを正規化
# シンボリックリンクは除外
if test -h "${FF}" ; then
	continue
elif test -f "${FF}" || test -d "${FF}" ; then

: && 	{ # 絶対パス取得_変数展開版
case "${FF}" in
( /* )
	true
;;
( * )
	XX="./${FF#./}" # 頭に「./」があれば削除して、またつける
	FF="`cd "${XX%/*}" ; pwd`/${FF##*/}"
;;
esac
		}

# 絶対パス取得readlink版_多少遅い
#	FF=`readlink --canonicalize-existing "${FF}"`

else
	echo "${FF}:処理対象のファイル、またはディレクトリがありません。" 1>&2
	exit 1
fi

# ファイル、ディレクトリ情報取得
if test -f "${FF}" ; then # ==== ファイル =========================
	stdinData+=("${FF}")        # 正規化した入力行 全て
	IN_DIR_FULL+=("${FF%/*}")   # ディレクトリパス
XX="${FF##*/}"                  # temp ファイル名+拡張子
	IN_NAME+=("${XX%%.*}")      # すべての「.〜」を拡張子と見なす
	OUT_NAME+=("${XX%%.*}")     # リネーム予定ファイル名_初期値

# sedで拡張子を得るとき
#YY=`echo "${XX}" | sed -e 's/^[^.]*//'` 
#	IN_EXT_DOT+=("${YY}")       # 「.」つき(に、sedの都合でせざるを得ない)
#	IN_EXT+=("${YY#.}")         # 「.」を削る(変数用のみ、処理には使わない)
#	OUT_EXT_DOT+=("${YY}")      # リネーム予定拡張子_初期値

: '変数展開で拡張子を得るとき' && {
if test "${XX}" = "${XX#*.}" ; then # 「.」無し拡張子を得る削除展開
	# 拡張子が無いとき、削除展開しても何も削れない
	IN_EXT_DOT+=('')
#	IN_EXT+=('')
	OUT_EXT_DOT+=('')
else
	# ドットファイル...OK,通常拡張子...OK,二重拡張子...OK
	IN_EXT_DOT+=(".${XX#*.}")   # 「.」追加_処理用
#	IN_EXT+=("${XX#*.}")        # 変数用のみ
	OUT_EXT_DOT+=(".${XX#*.}")  # リネーム予定拡張子_初期値
fi
}

elif test -d "${FF}" ; then # ==== ディレクトリ ==================
	stdinData+=("${FF}")        # 正規化した入力行 全て
	IN_DIR_FULL+=("${FF%/*}")        # ディレクトリの親ディレクトリ
	IN_NAME+=("${FF##*/}")      # ディレクトリは全てファイル名と見なす
	OUT_NAME+=("${FF##*/}")     # リネーム予定ディレクトリ名_初期値
	IN_EXT_DOT+=('')            # ディレクトリは拡張子が無いと見なす
#	IN_EXT+=('')                # 変数用のみ、処理には使わない
	OUT_EXT_DOT+=('')           # 拡張子は空に
fi

done  #0< <( cat - )
# -----------------------------------------------------------------------↑
	}

: '【データベース情報作成(Core自動作成分)】' && {
case "${databaseFlag}" in
( true )
for MM in "${!stdinData[@]}" ; do

# ---- コピー作成分 -----------------------------------
# 『path』ファイルの絶対パス 「stdinData[]」のコピー
DATA_BASE[path_${MM}]="${stdinData[${MM}]}"
DATA_BASE[path]="${DATA_BASE[path_0]}"
# 『dirfull』ファイルのディレクトリ_フル「IN_DIR_FULL[]」のコピー
DATA_BASE[dirfull_${MM}]="${IN_DIR_FULL[${MM}]}"
DATA_BASE[dirfull]="${DATA_BASE[dirfull_0]}"
# 『name』ファイルの名前 「[IN_NAME]」のコピー
DATA_BASE[name_${MM}]="${IN_NAME[${MM}]}"
DATA_BASE[name]="${DATA_BASE[name_0]}"
# 『extdot』ファイルの拡張子「.」つき 「IN_EXT_DOT[]」のコピー
DATA_BASE[extdot_${MM}]="${IN_EXT_DOT[${MM}]}"
DATA_BASE[extdot]="${DATA_BASE[extdot_0]}"

# ---- 新規作成分 ファイルパス ----------------------
# 『ext』ファイルの拡張子「頭の.」なし 「IN_EXT[]」の「先頭.」を削る
DATA_BASE[ext_${MM}]="${IN_EXT_DOT[${MM}]#.}"
DATA_BASE[ext]="${IN_EXT_DOT[0]#.}"
# 『dir』ファイルのディレクトリ_親一つのみ「IN_DIR_FULL[]」から作成
DATA_BASE[dir_${MM}]="${IN_DIR_FULL[${MM}]##*/}"
DATA_BASE[dir]="${IN_DIR_FULL[0]##*/}"

done
;;
( false | * )
	true
;;
esac
}

: '【${CMD_1D[]}の「$」を一次加工】' && {
# CMD_VAR_MODE=escape    「$」->「\\$」
# CMD_VAR_MODE=database  「$」->「$VAR -> ${VAR} ->${DATA_BASE[VAR]}」
# CMD_VAR_MODE=shell     「$」->「$」そのまま

for NN in ${!CMD_1D[@]} ; do
case "${CMD_VAR_MODE[${NN}]}" in
( escape )
#	CMD_1D[${NN}]=`echo "${CMD_1D[${NN}]}" |
# sed -e 's/\$\([a-zA-Z0-9_][a-zA-Z0-9_]*\)/${\1}/g;s/\${[a-zA-Z0-9_][a-zA-Z0-9_]*}/\\\\&/g'`
	CMD_1D[${NN}]=`echo "${CMD_1D[${NN}]}" | sed -e 's/\\$/\\\\$/g'`
;;
( database )
	CMD_1D[${NN}]=`echo "${CMD_1D[${NN}]}" |
 sed -e 's/\$\([a-zA-Z0-9_][a-zA-Z0-9_]*\)/${\1}/g;s/\${\([a-zA-Z0-9_][a-zA-Z0-9_]*\)}/${DATA_BASE[\1]}/g'`
;;
( shell )
	true
;;
( * )
	echo 'ERR:CMD_VAR_MODE:不正な値' 1 >&2
	exit 2
;;
esac
done

#echo "${databaseSW}" 1>&2 # debug
#echo "${CMD_1D[*]}" 1>&2 # debug
}

#echo ${SECONDS}:'連想配列CMD_2D作成開始' 1>&2 # 【debug】seconds

: '【${CMD_1D[]}から連想配列${CMD_2D[]}を作成_sed_遅い】' || {
# ▼ stdin取得後に実行する
# 一次配列「CMD_1D」中の「${変数名}」を、
# ${DATA_BASE[変数名_数字]}に書き換え、二次配列CMD_2D作成

for NN  in "${!CMD_1D[@]}" ; do        # コマンドの横列、連想配列nameCmd2d左の数字
case "${CMD_VAR_MODE[${NN}]}" in
( escape | shell )
	for MM in "${!stdinData[@]}" ; do  # ファイルごとのコマンドを作成、CMD_2D右の数字
		# 変換しないでコピーのみ
		CMD_2D[${NN}_${MM}]="${CMD_1D[${NN}]}"
	done
;;
( database )
	for MM in "${!stdinData[@]}" ; do  # ファイルごとに異なるコマンドを作成、CMD_2D右の数字
		# ${DATA_BASE[VAR]} -> ${DATA_BASE[VAR_${MM}]}
		CMD_2D[${NN}_${MM}]=`printf "%s\n" "${CMD_1D[${NN}]}" |
		sed -e 's/\(\${DATA_BASE\[[^]]*\)\]}/\1_'${MM}']}/g'`
	done
;;
esac
done
	}

: '【${CMD_1D[]}から連想配列${CMD_2D[]}を作成_付け焼き刃awk】' && {
# ▼ stdin取得後に実行する
# 一次配列「CMD_1D」中の「${変数名}」を、
# ${DATA_BASE[変数名_数字]}に書き換え、二次配列CMD_2D作成

for NN  in "${!CMD_1D[@]}" ; do        # コマンドの横列、連想配列nameCmd2d左の数字
case "${CMD_VAR_MODE[${NN}]}" in
( escape | shell )
# 変換しないでコピーのみ
	for MM in "${!stdinData[@]}" ; do
	CMD_2D[${NN}_${MM}]="${CMD_1D[${NN}]}"
	done
;;
( database )
# ファイルごとに異なるコマンドを作成、CMD_2D右の数字
# ${DATA_BASE[VAR]} -> ${DATA_BASE[VAR_${MM}]}
XX="-1"
while read -r LL ; do
	XX=$(( ${XX} + 1 ))
	CMD_2D[${NN}_${XX}]="${LL}"
done 0< <( for MM in "${!stdinData[@]}" ; do
            printf "%s\n" "${CMD_1D[${NN}]}"
            done | awk '{ print gensub( "\\${DATA_BASE\\[([^]]*)\\]}","\\${DATA_BASE[\\1_" NR-1 "]}","g",$0 ) }' )
;;
esac
done
	}

: '【リネームコマンド全表示確認】' && {
case "${Mode}" in
( renameCommandFull )
for NN in "${!CMD_1D[@]}" ; do
echo '=== '"[${NN}]: ${CMD_1D[${NN}]} ==========>[${cmdTargetSW},${cmdActionSW}]"

	for MM in "${!stdinData[@]}" ; do
	echo "  [${NN}_${MM}]: ${CMD_2D[${NN}_${MM}]}"
	done
done 1>&2
#echo ${SECONDS}:'連想配列CMD_2D作成終了' 1>&2 # 【debug】seconds
exit 0
;;
( * ) true ;;
esac
}



: '【名前・拡張子_加工コマンド_実行ループ】' && {
for NN in "${!CMD_TARGET[@]}" ; do # -------------------------------------------------↓

#echo "${NN}":${CMD_ACTION[${NN}]}:"${CMD_1D[${NN}]}" 1>&2 # debug

case "${CMD_TARGET[${NN}]}" in
# ==============================================================================
( name )
# ==============================================================================
case "${CMD_ACTION[${NN}]}" in
# -----------------------------------------------------------------
	( once )
# -----------------------------------------------------------------

for MM in ${!stdinData[@]} ; do

#echo N:${NN} M:${MM} "${CMD_2D[${NN}_${MM}]}" #debug

# 1行ごとに異なるコマンド「CMD_2D[]」を実行
set -f
eval 'OUT_NAME[${MM}]=`echo "${OUT_NAME[${MM}]}" | '"${CMD_2D[${NN}_${MM}]}"'`'
set +f
done
;;
# -----------------------------------------------------------------
	( loop )
# -----------------------------------------------------------------
# すべての行を同一コマンド「CMD_1D」で処理
set -f
eval 'while read -r LL ; do
          arrTemp+=("${LL}")
      done 0< <( for MM in ${!stdinData[@]} ; do
                     echo "${OUT_NAME[${MM}]}"
                 done | '"${CMD_1D[${NN}]}"' )'
set +f
OUT_NAME=("${arrTemp[@]}")

unset  arrTemp
;;
( * )
	true
;;
esac
;;
# ==============================================================================
( ext )
# ==============================================================================
case "${CMD_ACTION[${NN}]}" in
# ---------------------------------------------------------------------------
	( once )
# ---------------------------------------------------------------------------
for MM in ${!stdinData[@]} ; do
set -f
	eval 'OUT_EXT_DOT[${MM}]=`echo "${OUT_EXT_DOT[${MM}]}" | '"${CMD_2D[${NN}_${MM}]}"'`'
set +f
done

;;
# ---------------------------------------------------------------------------
	( loop )
# ---------------------------------------------------------------------------
set -f
eval  'while read -r LL ; do
           arrTemp+=("${LL}")
       done 0< <( for MM in ${!stdinData[@]} ; do
               echo "${OUT_EXT_DOT[${MM}]}"
           done | '"${CMD_1D[${NN}]}"' )'
set +f
OUT_EXT_DOT=("${arrTemp[@]}")

unset  arrTemp

;;
( * )
	true
;;
esac

;;
esac
done # --------------------------------------------------------------------------------↑
}

#echo ${SECONDS}:'結果を出力' 1>&2 # 【debug】seconds


# ==== 結果を出力 ======================================================================
case "${Mode}" in
( Action ) # ---- リネーム実行 ----------------------------------------
for NN in ${!stdinData[@]} ; do
	cd -- "${IN_DIR_FULL[${NN}]}"
	for MM in 0.2 1 ; do # MMはリネーム試行回数とsleep時間
	if test "${IN_NAME[NN]}${IN_EXT_DOT[NN]}" = "${OUT_NAME[NN]}${OUT_EXT_DOT[NN]}" ; then
		echo "${IN_NAME[NN]}${IN_EXT_DOT[NN]}"':ファイル名に変更はありません' 1>&2
		break 1
	elif mv --verbose --no-clobber \
 -- "${IN_NAME[NN]}${IN_EXT_DOT[NN]}" "${OUT_NAME[NN]}${OUT_EXT_DOT[NN]}"
	then
		break 1
	else
		sleep ${MM}
		continue 1
	fi
	exit 2
	done

done
;;
( pipeFull ) # ---- リネーム後ファイル名のみ、フルパスで表示 ------------
for NN in ${!stdinData[@]} ; do
	echo "${IN_DIR_FULL[NN]}/${OUT_NAME[NN]}${OUT_EXT_DOT[NN]}"
done

;;
( pipeNameExt )
for NN in ${!stdinData[@]} ; do
	echo "${OUT_NAME[NN]}${OUT_EXT_DOT[NN]}"
done

;;
( pipeName )
for NN in ${!stdinData[@]} ; do
	echo "${OUT_NAME[NN]}"
done

;;
( pipeExt )
for NN in ${!stdinData[@]} ; do
	echo "${OUT_EXT_DOT[NN]}"
done

;;
( Csv ) # リネーム前後のフルパスファイル名のみをタブ区切りで表示
for NN in ${!stdinData[@]} ; do
	echo "${IN_DIR_FULL[NN]}/${IN_NAME[NN]}${IN_EXT_DOT[NN]}"'	'\
"${IN_DIR_FULL[NN]}/${OUT_NAME[NN]}${OUT_EXT_DOT[NN]}"
done

;;
( mvCommand | * ) # ---- mvコマンドを表示 ------------------------
for NN in ${!stdinData[@]} ; do
echo 'cd -- '\'"${IN_DIR_FULL[${NN}]}"\'' ; '\
     'mv -n -v -- '\'"${IN_NAME[NN]}${IN_EXT_DOT[NN]}"\'' '\'"${OUT_NAME[NN]}${OUT_EXT_DOT[NN]}"\'' ; '
done

;;
esac



exit 0