ファイルの移動/コピー機能で「*」ワイルドカードを使用すると、一度に 1 つのファイルしか移動されないのはなぜですか?

ファイルの移動/コピー機能で「*」ワイルドカードを使用すると、一度に 1 つのファイルしか移動されないのはなぜですか?
function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}
mv1 *.png

.pngすべてのファイルを移動するのではなく、最初に見つかったファイルのみを移動します。

ワイルドカードに一致するすべてのファイルにコマンドを適用するにはどうすればよいでしょうか?

答え1

mv1 *.pngまずワイルドカード パターンを*.png一致するファイル名のリストに展開し、次にそのファイル名のリストを関数に渡します。

次に、関数内とは$1、関数の最初の引数を取り、空白を含む場所で分割し、ワイルドカード文字を含み、少なくとも 1 つのファイル名に一致する空白で区切られた部分を、一致するファイル名のリストで置き換えます。複雑に聞こえますか? 実際複雑です。この動作はたまにしか役に立たず、多くの場合は問題になります。この分割と一致の動作は、$1二重引用符の外側で発生した場合にのみ発生するため、修正は簡単です。二重引用符を使用します。変数置換は常に二重引用符で囲む正当な理由がない限りは。

たとえば、現在のディレクトリにA* algorithm.pngと という2 つのファイルが含まれている場合graph1.png、 は関数の最初の引数として を、 は2 番目の引数としてmv1 *.png渡します。次には とに分割されます。パターンは に一致し、ワイルドカード文字は含まれません。したがって、関数は引数 、、、および で実行されます。関数を次のように修正すると、A* algorithm.pnggraph1.png$1A*algorithm.pngA*A* algorithm.pngalgorithm.pngmv-nA* algorithm.pngalgorithm.pngtargetdir-v

function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}

すると最初のファイルが正しく移動されます。

プロセスへ全て引数を指定すると、最初の引数だけでなくすべての引数を処理するようにシェルに指示します。"$@"関数に渡される引数の完全なリストを意味するために を使用できます。

function mv1 { mv -n "$@" "targetdir" -v |wc -l ;}

これはほぼ正しいのですが、ファイル名が 文字で始まる場合は-mvがその引数をオプションとして扱うため、やはり失敗します。--に を渡してmv、「この時点以降はオプションはありません」と伝えます。これは、ほとんどのコマンドがサポートする非常に一般的な規則です。

function mv1 { mv -n -v -- "$@" "targetdir" |wc -l ;}

残る問題は、mvパイプの左側のコマンドの終了ステータスが無視されるため、失敗した場合、この関数は成功ステータスを返すことです。bash (または ksh) では、を使用してset -o pipefailパイプラインを失敗させることができます。このオプションを設定すると、同じシェルで実行されている他のコードが失敗する可能性があることに注意してください。そのため、関数内でローカルに設定する必要があります。これは、bash 4.4 以降で可能です。

function mv1 {
  local -
  set -o pipefail
  mv -n -v -- "$@" "targetdir" | wc -l
}

以前のバージョンでは設定がpipefail脆弱だったため、代わりに明示的に確認する方がよいでしょうPIPESTATUS

function mv1 {
  mv -n -v -- "$@" "targetdir" | wc -l
  ((!${PIPESTATUS[0] && !${PIPESTATUS[1]}}))
}

答え2

$1は関数の最初の引数で、ここでは に一致する最初のファイルです。の代わりにこれを使用する必要がある*.pngと思います。"$@"$1

答え3

を使用する必要がありますmv1 \*.png

関数と対話する場合、Linux ターミナルはアスタリスクをコマンドに直接渡すのではなく、最初に一致するパラメータを選択してコマンドに渡します。

アスタリスクが直接通過できるようにするには、バックスラッシュを使用してアスタリスクをエスケープする必要があります。

関連情報