シェルスクリプトでは、検索ベースの mv の名前変更が失敗します。

シェルスクリプトでは、検索ベースの mv の名前変更が失敗します。

ファイル名とファイルの一部の名前を変更する簡単なスクリプトを作成したいと思います。このコマンドはコマンドラインで正しく実行されますが、次のようになります。

find . -type f -name "*old*" -exec bash -c 'mv "$0" "${0/old/new}"' {} \; 2>&1 | grep -v "Permission denied" | grep -v "are the same file"

シェルスクリプトで再実装してみました。

#!/bin/bash
# renamefiles.sh - type renamefiles <old> <new>
# 
find . -type f -name "*$1*" -exec bash -c 'mv "$0" "${0/$1/$2}"' {} \; 2>&1 | grep -v "Permission denied" | grep -v "are the same file"

ユーザーが入力すると、

$ renamefiles old new

エラーが発生します(エラーメッセージ抑制が無効になっています)。

mv: './File0_old.txt' and './File0_new.txt' are the same file
mv: './Another_old_file.txt' and './Another_new_file.txt' are the same file

答え1

2つの考えられる解決策から始めて、エラーが発生する理由を説明します。

解決策 1::

サブシェルで使用できるように、これら2つの変数を「一時的に」エクスポートします。

#!/bin/bash
# renamefiles.sh - type renamefiles <old> <new>

export from="$1" && export to="$2" && find . -type f -name "*$1*" -exec bash -c 'mv "$0" "${0/$from/$to}"' "{}" \; 2>&1 | grep -v "Permission denied"


exit 0

したがって、$ 1をfrom変数にエクスポートし、$ 2をto変数としてエクスポートします。これにより、サブシェルがそれを読み取ることができます。

解決策2::(一時エクスポートを好まない人はより簡単で好むかもしれません)

#!/bin/bash
# renamefiles.sh - type renamefiles <old> <new>

find . -type f -name "*$1*" -exec bash -c 'mv "$0" "${0/$1/$2}"' "{}" "$1" "$2" \; 2>&1 | grep -v "Permission denied"

exit 0

解決策は、サブシェルがスクリプト自体であるかのように正常にアクセスできるように{}(現在の find 結果)の外部に「$1」および「$2」のみを渡すことで構成されます(例外は bash に渡される -c オプションです)。 、最初の引数は$ 1ではなく$ 0になります。これは使ってみた後で分かる内容のようです。

2つのソリューションの共通点:

  1. 常にスクリプトの最後に終了コマンドを含めます。
  2. 渡されたすべての変数({}を含む)の周囲には引用符があります。これにより、スペースによって変数が誤って解釈されるのを防ぐことができます。後でスクリプトがいつ使用されるかわからないので、[ここに]にスペースがあるファイル名や値がないと確信している場合でもこれを実行してください。

エラーの原因::

エラー(「同じファイルです」)が表示される理由は非常に簡単です。

スクリプトでは、mvコマンドは、指定されたファイル "$ 0"を文字列置換の結果によって提供される他のファイル( "$ {0 / $ 1 / $ 2}")に置き換えます。したがって、あなたの場合、$ 1と$ 2はサブシェルコマンドには提供されていないため、「$ {0 / /}」を実行したのと同じで、何もしません。その結果、古いファイル名と新しいファイル名が同じであるため、エラーが発生します。

関連情報