「mv: 引数リストが長すぎます」を解決しますか?

「mv: 引数リストが長すぎます」を解決しますか?

mv整理が必要なファイルが100万個以上あるフォルダがあるのですが、このメッセージが常に表示されるため、何もできません。

-bash: /bin/mv: Argument list too long

拡張子のないファイルを移動するために次のコマンドを使用しています:

mv -- !(*.jpg|*.png|*.bmp) targetdir/

答え1

xargs仕事のためのツールです。あるいはfind-exec … {} +これらのツールは、一度に渡せる数の引数を使用して、コマンドを複数回実行します。

どちらの方法も、可変引数リストが最後にある場合は実行が簡単になりますが、ここではそうではありません。 の最後の引数はmv宛先です。 GNU ユーティリティ (つまり、非組み込み Linux または Cygwin) では、-tのオプションがmv、宛先を最初に渡すために役立ちます。

ファイル名に空白や が含まれず\"'、¹ で始まっていない場合は-、 への入力としてファイル名を指定するだけですxargs(echoコマンドは bash の組み込みコマンドなので、コマンド ラインの長さ制限は適用されません。 が表示される場合は!: event not found、 を使用してグロブ構文を有効にする必要がありますshopt -s extglob)。

echo !(*.jpg|*.png|*.bmp) | xargs mv -t targetdir --

-0オプションをxargs使用すると、デフォルトの引用符形式の代わりに null 区切りの入力を使用できます。

printf '%s\0' !(*.jpg|*.png|*.bmp) | xargs -0 mv -t targetdir --

あるいは、 を使用してファイル名のリストを生成することもできますfind。サブディレクトリへの再帰を避けるには、 を使用します-type d -prune。リストされたイメージ ファイルに対してアクションが指定されていないため、他のファイルのみが移動されます。

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec mv -t targetdir/ {} +

(シェルのワイルドカード方式とは異なり、ドット ファイルも含まれます。)

GNU ユーティリティがない場合は、中間シェルを使用して引数を正しい順序で取得できます。この方法は、すべての POSIX システムで機能します。

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec sh -c 'mv "$@" "$0"' targetdir/ {} +

zshでは、mv組み込み:

setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/

mvまたは、やその他の名前で外部コマンドを参照し続けたい場合は、次のようにします。

setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- ^*.(jpg|png|bmp) targetdir/

またはkshスタイルのglobを使用する場合:

setopt ksh_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- !(*.jpg|*.png|*.bmp) targetdir/

あるいは、GNUmvzargs:

autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/ --

¹xargs実装によっては、ファイル名も現在のロケールで有効なテキストでなければなりません。ファイル名が_入力の終わりを示すものと見なされる場合もあります ( で回避できます-E '')

答え2

Linuxカーネルで作業するだけで十分な場合は、次のようにします。

ulimit -S -s unlimited

これは、Linux カーネルに約 10 年前にスタック サイズに基づいて引数の制限を変更するパッチが含まれていたため機能します。https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b6a2fea39318e43fee84fa7b0b90d68bed92d2ba

無制限のスタックスペースが必要ない場合は、例えば次のようにします。

ulimit -S -s 100000

スタックを 100 MB に制限します。スタック領域を通常のスタック使用量 (通常は 8 MB) と使用するコマンド ラインのサイズに設定する必要があることに注意してください。

実際の制限は次のように照会できます。

getconf ARG_MAX

コマンドラインの最大長をバイト単位で出力します。たとえば、Ubuntuのデフォルトでは、これは2097152約2MBです。無制限のスタックで実行すると、4611686018427387903これはちょうど2^62、つまり約46000TBになります。コマンドラインがそれ、あなた自身で問題を回避できることを期待しています。

実行sudo中に を使用すると、 が実際に実行する前にスタック サイズをリセットするため、この問題を修正できないことに注意してください。これを回避するには、 を使用してルート シェルを開始し、 を実行して、最後にそのルート シェルで を使用せずにコマンドを実行する必要があります。sudo mv *.dat somewhere/.ulimitsudomvsudo -sulimit -S -s unlimitedsudo

答え3

場合によっては、Python などの小さなスクリプトを書くのが最も簡単なこともあります。

import glob, shutil

for i in glob.glob('*.jpg'):
  shutil.move(i, 'new_dir/' + i)

答え4

xargsオペレーティング システムの引数渡し制限は、シェル インタープリタ内で行われる展開には適用されません。したがって、またはを使用するだけでなくfind、シェル ループを使用して処理を個々のコマンドに分割することもできますmv

for x in *; do case "$x" in *.jpg|*.png|*.bmp) ;; *) mv -- "$x" target ;; esac ; done

これは POSIX シェル コマンド言語の機能とユーティリティのみを使用します。このワンライナーはインデントによりわかりやすくなり、不要なセミコロンが削除されています。

for x in *; do
  case "$x" in
    *.jpg|*.png|*.bmp) 
       ;; # nothing
    *) # catch-all case
       mv -- "$x" target
       ;;
  esac
done

関連情報