bash ターミナルで式をキャプチャするにはどうすればよいですか?

bash ターミナルで式をキャプチャするにはどうすればよいですか?

log_<date>.txtネストされたディレクトリに同じ命名法を共有するファイルが散在していますroot<date>ファイルが作成された日付はYYYYmmddHHMMの形式でどこにありますか。コード互換性の問題のため、すべてのファイルの名前を次のように変更したいと思います。<date>_log.txt

式をキャプチャする必要があることはわかっています<date>が、bash でそれをどのように行うのでしょうか? また、ネストされたディレクトリでそれをどのように行うのでしょうか?

答え1

シェル変数 にこれらのファイルの 1 つの名前があるとしますnamelog_変数の値から ビットを削除するには を使用し${name#log_}.txtビットを削除するには を使用します。残っているのは日付であり、その形式は基本的に重要ではありません。削除された値を取得してその末尾に追加して新しい名前を作成するのは${name%.txt}簡単です。_log_.txt

単一のディレクトリ内のファイルに対してこれを行うには、ループを使用します。変数の値の先頭のディレクトリ名も削除する必要があることに注意してください。

shopt -s nullglob

for name in root/log_*.txt; do
    newname=root/${name#root/log_}    # remove prefix, add root/ back
    newname=${newname%.txt}_log.txt   # remove suffix, add _log.txt back

    printf 'Would move "%s" to "%s"\n' "$name" "$newname"
    # mv -i "$name" "$newname"
done

安全のため、実際のコマンドはコメントアウトしましたmv。まずは一度テスト実行して何が起こるかを確認してください。

シェルnullglobオプションはbashシェルを作成します取り除くlog_*.txt一致しないパターンを展開せずに残すのではなく、展開せずに残します。つまり、指定されたディレクトリにファイルがない場合、ループはまったく実行されません。

これは、現在のディレクトリ(現在のディレクトリを含む)の下の任意のファイルに再帰的に適用できます。多かれ少なかれループするパターンを変更するだけです:

shopt -s globstar nullglob

for name in ./**/log_*.txt; do
    dirpath=${name%/*}                      # get path of directory
    newname=$dirpath/${name#$dirpath/log_}  # remove prefix, add directory path back
    newname=${newname%.txt}_log.txt         # remove suffix, add _log.txt back

    printf 'Would move "%s" to "%s"\n' "$name" "$newname"
    # mv -i "$name" "$newname"
done

その他の変更には、globstarシェル オプションを有効にすることが含まれます**。これにより、サブディレクトリまで一致させることができます。また、ディレクトリ パスを選択して、新しい名前の先頭に追加できるようにしました。

findループへの入力を生成するためにも使用できます:

find . -type f -name 'log_*.txt' -exec sh -c '
    for name do
        dirpath=${name%/*}                      # get path of directory
        newname=$dirpath/${name#$dirpath/log_}  # remove prefix, add directory path back
        newname=${newname%.txt}_log.txt         # remove suffix, add _log.txt back

        printf "Would move \"%s\" to \"%s\"\n" "$name" "$newname"
        # mv -i "$name" "$newname"
    done' sh {} +

これにより、現在のディレクトリまたはその下で名前が一致するすべての通常ファイルが検索されlog_*.txt、それらのファイルがバッチでインラインsh -cスクリプトに渡されます。このインライン スクリプトは、基本的に上記の 2 番目のバリエーションのループと同じ作業を実行します。

答え2

zsh最善の方法は、のようなバッチ ファイル名変更ツールzmvmmvまたは の Perl ベースのバリアントのいずれかを使用することですrename

zsh << 'EOF'
autoload zmv
zmv 'log_(*).txt' '${1}_log.txt'
EOF

または

prename 's/log_(.*)\.txt/${1}_log.txt/' log*.txt

関連情報