Open3

シェルスクリプトでディレクトリ名とファイル名を再帰的に変換する

shiratorishiratori

変換テスト用のディレクトリを用意する

test-dir
 ┣ bar1
 ┃ ┣ bar2
 ┃ ┃ ┣ bar3
 ┃ ┃ ┃ ┗ bar.txt
 ┃ ┃ ┗ bar.txt
 ┃ ┗ bar.txt
 ┗ foo1
 ┃ ┣ foo2
 ┃ ┃ ┣ foo3
 ┃ ┃ ┃ ┗ foo.txt
 ┃ ┃ ┗ foo.txt
 ┃ ┗ foo.txt

シェルスクリプト

rename-r
#!/bin/zsh

# ----------------------------------------------------
# ディレクトリ名とファイル名を再帰的に置換する
# ----------------------------------------------------

# 変換したいルートディレクトリパス
startDir=$1
beforeStrings=$2
afterStrings=$3

if [ -z $startDir ]; then
  echo "ディレクトリパスを指定してください"
  exit 1
fi

if [ -z $beforeStrings ]; then
  echo "置換前の文字列を指定してください"
  exit 1
fi

if [ -z $afterStrings ]; then
  echo "置換後の文字列を指定してください"
  exit 1
fi

# ディレクトリに移動
cd ${startDir}
echo "cd ${startDir}"

# ディレクトリの深さを取得
dirDepth=`find . -type d | awk -F/ '{ print NF }' | sort | tail -1`
echo "dirDepth: $dirDepth"

# ディレクトリの階層を上から順に潜ってリネーム処理をしていく
for dirLevel in $(seq $dirDepth)
do  
    echo "---------------------------------------------------"
    echo "dirLevel: $dirLevel"

    # ディレクトリの深さに応じてファイル名を取得
    targetNames=`find . -maxdepth $dirLevel -name "*$beforeStrings*"`

    if [ -z "$targetNames" ]; then
      echo "対象のファイルが見つかりませんでした"
      continue
    fi
    
    while read targetName; do
      # 新しい名前を作成
      newName=${targetName//$beforeStrings/$afterStrings}
      # 名前を変更
      mv $targetName $newName
      # 結果を出力
      echo "Renamed: $targetName -> $newName"
    done <<< "${targetNames}"
done

ディレクトリ構成

my-command
 ┣ test-dir
 ┗ rename-r

実行結果

my-command $ rename-r test-dir bar pon
cd test-dir
dirDepth: 4
---------------------------------------------------
dirLevel: 1
Renamed: ./bar1 -> ./pon1
---------------------------------------------------
dirLevel: 2
Renamed: ./pon1/bar2 -> ./pon1/pon2
Renamed: ./pon1/bar.txt -> ./pon1/pon.txt
---------------------------------------------------
dirLevel: 3
Renamed: ./pon1/pon2/bar3 -> ./pon1/pon2/pon3
Renamed: ./pon1/pon2/bar.txt -> ./pon1/pon2/pon.txt
---------------------------------------------------
dirLevel: 4
Renamed: ./pon1/pon2/pon3/bar.txt -> ./pon1/pon2/pon3/pon.txt

barpon に変更されている。

test-dir
 ┣ foo1
 ┃ ┣ foo2
 ┃ ┃ ┣ foo3
 ┃ ┃ ┃ ┗ foo.txt
 ┃ ┃ ┗ foo.txt
 ┃ ┗ foo.txt
 ┗ pon1
 ┃ ┣ pon2
 ┃ ┃ ┣ pon3
 ┃ ┃ ┃ ┗ pon.txt
 ┃ ┃ ┗ pon.txt
 ┃ ┗ pon.txt
shiratorishiratori

ディレクトリを対象外にしてファイル名のみ変更したい場合

#!/bin/zsh

# ----------------------------------------------------
# ファイル名のみを再帰的に置換する
# ----------------------------------------------------

# 変換したいルートディレクトリパス
startDir=$1
beforeStrings=$2
afterStrings=$3

if [ -z $startDir ]; then
  echo "ディレクトリパスを指定してください"
  exit 1
fi

if [ -z $beforeStrings ]; then
  echo "置換前の文字列を指定してください"
  exit 1
fi

if [ -z $afterStrings ]; then
  echo "置換後の文字列を指定してください"
  exit 1
fi

# ディレクトリに移動
cd ${startDir}
echo "cd ${startDir}"

# ファイル名一覧を取得
targetNames=`find . -type f -name "*$beforeStrings*"`

if [ -z "$targetNames" ]; then
  echo "対象のファイルが見つかりませんでした"
  return 1
fi

echo "targetNames: ${targetNames}"

while read targetName; do
    echo "---------------------------------------------------"
    echo "targetName: ${targetName}"

    # ディレクトリパスを除いたファイル名を取得
    fileName=`echo ${targetName} | awk -F/ '{print $NF}'`
    echo "fileName: ${fileName}"

    # ディレクトリパスのみを取得
    dirName=${targetName%/*}
    echo "dirName: ${dirName}"

    # 新しいファイル名前を作成
    newName=${fileName//$beforeStrings/$afterStrings}

    # 新しいフルパス
    newFullName=${dirName}/${newName}
    echo "newFullName: ${newFullName}"

    # 名前を変更
    mv $targetName $newFullName

    # 結果を出力
    echo "Renamed: $targetName -> $newFullName"
done <<< "${targetNames}"

実行結果

my-command $ rename-rf test-dir bar pon 
cd test-dir
targetNames: ./bar1/bar2/bar3/bar.txt
./bar1/bar2/bar.txt
./bar1/bar.txt
---------------------------------------------------
targetName: ./bar1/bar2/bar3/bar.txt
fileName: bar.txt
dirName: ./bar1/bar2/bar3
newFullName: ./bar1/bar2/bar3/pon.txt
Renamed: ./bar1/bar2/bar3/bar.txt -> ./bar1/bar2/bar3/pon.txt
---------------------------------------------------
targetName: ./bar1/bar2/bar.txt
fileName: bar.txt
dirName: ./bar1/bar2
newFullName: ./bar1/bar2/pon.txt
Renamed: ./bar1/bar2/bar.txt -> ./bar1/bar2/pon.txt
---------------------------------------------------
targetName: ./bar1/bar.txt
fileName: bar.txt
dirName: ./bar1
newFullName: ./bar1/pon.txt
Renamed: ./bar1/bar.txt -> ./bar1/pon.txt

ファイルのみponに変更されている。

test-dir
 ┣ bar1
 ┃ ┣ bar2
 ┃ ┃ ┣ bar3
 ┃ ┃ ┃ ┗ pon.txt
 ┃ ┃ ┗ pon.txt
 ┃ ┗ pon.txt
 ┗ foo1
 ┃ ┣ foo2
 ┃ ┃ ┣ foo3
 ┃ ┃ ┃ ┗ foo.txt
 ┃ ┃ ┗ foo.txt
 ┃ ┗ foo.txt