異なるサブフォルダ内のファイルの名前を変更する

異なるサブフォルダ内のファイルの名前を変更する

異なるフォルダにある複数のファイルの名前を変更したいと思います。具体的には、ファイル名の中央にゼロを追加して、すべてのファイル名が 3 桁になるようにします (論理的な順序で表示されます)。

具体的には、130 以上のフォルダーがあり、各フォルダーには 400 以上の .nii ファイルがあります。各 .nii ファイルのパターンは次のとおりです。

swu_run1_P_139_Vol_1.nii

  • P_### の範囲は P76 から P277 (参加者番号) です
  • Vol_### の範囲は 1 から 405 (ボリューム番号) です

ボリュームの範囲は1から405なので、リストは1から405ではなく100から始まります(例:100 - 101 - 102 [..] - 109 -10- 110 - 111 など)。これを解決する 1 つの方法は、ファイル名にゼロを追加してすべてを 3 桁にすることです。例:

swu_run1_P_277_Vol_1.nii -> swu_run1_P_277_Vol_001.nii
swu_run1_P_277_Vol_2.nii -> swu_run1_P_277_Vol_002.nii
swu_run1_P_277_Vol_10.nii -> swu_run1_P_277_Vol_010.nii
swu_run1_P_277_Vol_120.nii -> swu_run1_P_277_Vol_120.nii

私は Unix/Linux システムの経験がほとんどありませんが、以前のスレッドを使用して、次のコードを作成しました。このコードは 2 つの部分から構成されています。

1. 複数のファイル名を変更し、ファイル名の中央にゼロを追加します。

rename Vol_ Vol_0 *Vol_[0-9].nii

これをサブフォルダーで実行すると、次のエラー メッセージが表示されます。

Error: rename: swu_run1_P_275_Vol_1.nii: rename to swu_run1_P_275_Vol_01.nii failed: No such file or directory

不思議なことに、Vol_1-9 にゼロが追加されます。ただし、すでに 2 桁または 3 桁の数字にはゼロは追加されません。

swu_run1_P_277_Vol_1.txt -> swu_run1_P_277_Vol_01.nii
swu_run1_P_277_Vol_10.txt -> swu_run1_P_277_Vol_10.nii
swu_run1_P_277_Vol_100.txt -> swu_run1_P_277_Vol_100.nii

式で何らかの奇妙なループが発生しているようです (新しい Vol_01 を変更しようとして、エラー メッセージを表示します)。また、2 桁または 3 桁の数字にゼロが追加されないのはなぜですか。

2..nii関連するサブフォルダー内のすべてのファイルを検索し、一括で名前変更を実行します。

find . -iname "*.nii" -execdir rename Vol_ Vol_0 *Vol_[0-9].nii '{}' \;

このコードについての私の理解は次のとおりです。

  • find . -iname "*.nii"現在のフォルダとサブフォルダの両方にあるすべての.niiファイルを検索します。

  • -execdir現在のフォルダとサブフォルダに次の式を適用するように指示します。つまり、ゼロを追加します。

  • rename Vol_ Vol_0 *Vol_[0-9].niiゼロを加算します(テキストから テキスト ファイルリスト

  • '{}'ファイルのパス名がある

  • \;-execdir式を終了するためにあります

さらに多くのフォルダーでコードを実行しようとすると、次のエラー メッセージが表示されます。

Error: rename: *Vol_[0-9].nii: rename to *Vol_0[0-9].nii failed: No such file or directory

-execdir がサブフォルダーで実行されていないためにエラー メッセージが表示されると思いますが、解決方法がわかりません。

シェルを実行するために各サブフォルダーに手動でアクセスするのは避けたいので、このコードを改善する (そして動作させる) 方法について何か提案はありますか? また、「そのようなファイルまたはディレクトリはありません」というエラー メッセージが表示されるのはなぜですか?

答え1

swu_run1_P_277_Vol_1.nii変数 に名前があった場合name、 は${name##*_}次のようになります1.nii(一致する最長のプレフィックス文字列*_は削除されます)。

これを取って削除すると、.nii3 文字の幅にゼロを埋め込む数値が残ります。

name=swu_run1_P_277_Vol_1.nii

number=${name##*_}
number=${number%.nii}

シェルではbash、数値をゼロで埋める最も簡単な方法は(おそらく)次のとおりですprintf

printf '%.3d' "$number"    # would print 001 (with no newline)

同時に新しい名前を作成することもできます。

printf '%s_%.3d.nii' "${name%_*.nii}" "$number"

このコマンドは、元の名前から一致するサフィックス文字列を削除し、ゼロで埋められた数字を追​​加してからサフィックス文字列printfを追加して印刷します。swu_run1_P_277_Vol_001.nii_*.nii_.nii

最後に、結果の文字列を印刷します。直接を新しい変数に代入しますprintf -v newname ...

これを 1 つの名前にまとめると次のようになります。

name=swu_run1_P_277_Vol_1.nii

number=${name##*_}
number=${number%.nii}

printf -v newname '%s_%.3d.nii' "${name%_*.nii}" "$number"

そうなると、それは単なる問題ですmv "$name" "$newname"

では、関連するすべてのファイルに対してこれをどのように実行すればよいのでしょうか?

関連するすべてのファイルがグロブパターン に一致すると仮定すると*Vol_*.nii、 によりfind

find . -type f -name '*Vol_*.nii' -exec bash -c '
    for pathname do
        dirpath=${pathname%/*}  # or: dirpath=$(dirname "$pathname")
        name=${pathname##*/}    # or: name=$(basename "$pathname")

        number=${name##*_}
        number=${number%.nii}

        printf -v newname "%s_%.3d.nii" "${name%_*.nii}" "$number"

        printf "Would rename %s --> %s\n" "$pathname" "$dirpath/$newname"
        # mv "$pathname" "$dirpath/$newname"
    done' bash {} +

ここでのインラインスクリプトは、(通常のファイルである) および(特定の名前を持つ) の条件を満たす、見つかったパス名のバッチでbash -cによって呼び出されます。スクリプトはこれらのパス名をループし、ファイル名を に、ディレクトリ パス名を に取り出します。find-type f-namenamedirpath

次に、前と同じ操作を実行して新しい名前を決定し、それを に保存してnewname、ファイルの名前を変更します。

安全のため、実際のコマンドはコメント アウトしました。まずこれを一度実行して、出力が正しいことを確認してください。GNU ツールを使用する場合は、名前の衝突があった場合にファイルのバックアップを作成するためにmvも使用することをお勧めします。mv -b


ちなみに、zsh殻の中の塊状の模様は

./**/*Vol_*.nii(n)

これらすべての名前に拡大する番号順(そして int サブディレクトリを再帰的に下ります):

$ print -rC1 ./**/*Vol_*.nii(n)
./swu_run1_P_277_Vol_1.nii
./swu_run1_P_277_Vol_2.nii
./swu_run1_P_277_Vol_10.nii
./swu_run1_P_277_Vol_120.nii

関連情報