find コマンドを使用したファイル名の変更

find コマンドを使用したファイル名の変更

10K mp4ファイルのリストがありますが、いくつかは?タグが含まれています。 _(下線)に置き換える必要があります。

findコマンドを使用してこれらのファイルを見つけることができますが、名前を変更できるかどうかはわかりません。

find . -name "*\?*.mp4" -exec mv {} XXXXXXX \;

findから返されたリストを繰り返す必要があり、交換する必要がある各リストを繰り返す必要があると思いますか? _を使用しますが、どうすればいいかわかりません。

答え1

これには使用しないでくださいfind。どちらの方向に行っても、ファイルごとに1つのようなものが必要ですfindmvプロセスがたくさん必要ですが、役に立たない。言うまでもなく、そうするのはもっと難しいです。とにかくそれは私の意見です。

私はストリーミングまたはバッチ処理ツールを使用しており、以下を好む傾向がありますpax

cd -P . && mkdir ../newmp4 &&
pax -rwls'|?|_|g' . "${PWD%/*}/newmp4"

ディレクトリを作成できる場合、../newmp4この小さなスクリプトはハードリンクを使用して作成したディレクトリにルートを持つツリーをミラーリングするだけでなく、.すべてのファイル名を下線に置き換えます。?

これにより得られる利点の1つ両方その後もツリーは存在し続けます。現在、ディレクトリのルートにあるファイルは影響を受けません。ツリーのミラーリングされたバージョンでのみ変更されます。したがって、削除するツリーのバージョンを決定する前に結果を確認できます。手順中に問題が発生しても害はありません。

ただし、これにはハードリンクをサポートし、少なくとも+ 2サブディレクトリだけ残っている空のinodeを持つファイルシステムが必要です。ただし、すべてのサブディレクトリが同じファイルシステムマウントを共有しているとします... ..

rsync私はそれほど単純ではないと思いますが、実際に同じ効果を得ることは可能だと思います。持っていない場合pax (あなた本物 しなければならない)、あなたはそれを得ることができます。ミラビロス維持する(私の知る限り)最大一般的に使用されるバージョンLinuxシステムで。

答え2

個人的には、これはPerlの要件にすぎないと思います。特殊文字の安全性と名前の変更は、fork execではなくシステムコールです。

find . -name "*\?*.mp4" -print0|perl -n0e 'next if /\?.*\//;$o=$_;s!\?!_!g;next if -e;rename $o, $_'

未定義の動作を防ぎ、名前変更の競合を確認するには、? を含むディレクトリのファイルを無視します。以下の詳細な説明をご覧ください。

find . \               # find files below the current directory
    -name "*\?*.mp4" \ # having a ? in the name and ending in .mp4
    -print0\           # print the file names separated by nulls
|perl \                # direct the output of find to the input of perl
    -n \               # loop over standard input while assigning it to $_
    -0 \               # lines from standard input are separated by null
    -e \               # evaluate the next argument as perl commands
    'next if /\?.*\//; # skip this file if the directory name includes ?
    $o=$_;             # make a copy of the file name of preserve the name
    s!\?!_!g;          # change the ?s in the filename to _s
    next if -e;        # skip file if there is a file by that name
    rename $o, $_'     # do the rename

答え3

私が見つけた最も簡単な方法は、他のものをパイプで接続してコマンドを生成してsedからコマンドを実行することですsh

find . -name '*\?*.mp4' -print | sed 's/.*/"&";h;y/?/_/;x;G;s/\n/ /;s/^/mv /' | sh -s

findのように説明が非常に必要ですsh。このsedコマンドには説明が必要な場合があります。

  • s/.*/"&"/スペースまたは特殊文字を処理するには、入力を二重引用符で囲みます。
  • hバッファを「保留」バッファにコピーし、コピーを保存します。
  • y/?/_/tr \? _コマンドラインと同じです。
  • x"hold"バッファの内容をモードバッファに置き換えます。
  • G「\n」区切り文字を使用して、パターンバッファの末尾に「hold」バッファを追加します。今、"stringwith?\nstringwith_"があります。
  • s/\n/ /改行文字を変更してください。
  • s/^/mv /事前コマンドmv

これにより、古い名前と新しい名前を使用して見つかったmv各ファイルのコマンドが生成されます。find

答え4

そしてzsh

autoload zmv # best in ~/.zshrc
zmv '(**/)(*\?*.mp4)' '$1${2//\?/_}'

これは、隠されたディレクトリを除いて、隠されたディレクトリの内部を見ていません。タイプ(一般、ディレクトリ、シンボリックリンク...)に関係なく、これらのファイルの名前を変更します。

ファイル/ディレクトリを隠す、mp4ファイルの名前を変更する(通常のファイルの場合)のみを考慮し、大文字と小文字を区別しないようにするには、次のようにします。

zmv '(**/)(*\?*.(#i)mp4)(#q.D)' '$1${2//\?/_}'

GNU近似(zmv以前のように競合とオーバーライドを確認しない):

find . -iname '*\?*.mp4' -type f -exec bash -c '
  for file do
    base=${file##*/}
    mv -i "$file" "${file%/*}/${base//\?/_}"
  done' {} +

関連情報