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


64

並べ替えが必要な100万を超えるファイルを含むフォルダーがありますが、mvこのメッセージは常に出力されるため、実際には何もできません。

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

このコマンドを使用して、拡張子のないファイルを移動しています。

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

回答:


82

xargs仕事のためのツールです。それか、find-exec … {} +。これらのツールは、一度に渡すことができる限り多くの引数を使用して、コマンドを数回実行します。

両方の方法は、変数引数リストが最後にある場合に実行しやすくなりますが、ここではそうではありませんmv。最終的な引数は宛先です。GNUユーティリティ(つまり、非組み込みLinuxまたはCygwin)では、宛先を最初に渡す-tオプションmvが便利です。

ファイル名に空白やが含まれていない場合は\"'、ファイル名を入力として単純に提供できますxargsechoコマンドはbashビルトインであるため、コマンドラインの長さの制限を受けません)。

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

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

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/

またはlet mvおよび他の名前が外部コマンドを参照し続ける場合:

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

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

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

または、GNU mvおよびzargs

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

1
最初の2つのコマンドは「-bash:!:event not found」を返し、次の2つのコマンドはファイルをまったく移動しませんでした。知っておくべきならCentOS 6.5を使用しています
ドミニク

1
@Dominique質問で使用したのと同じグロビング構文を使用しました。shopt -s extglob有効にする必要があります。findコマンドのステップを見逃していたので、修正しました。
ジル「SO-は悪であることを停止」

findコマンドでこれを取得しています。「find:無効な式。前に何もない二項演算子「-o」を使用しました。」私は今、他のものを試してみます。
ドミニク

@Dominique投稿したfindコマンド(現在)は動作します。コピー&ペーストするときは、一部を中断している必要があります。
ジル「SO-悪であるのをやめる」

Gilles、findコマンドについては、「not」演算子を使用しないのはなぜ!ですか?奇数の末尾よりも明確で理解しやすい-oです。たとえば、! -name '*.jpg' -a ! -name '*.png' -a ! -name '*.bmp'
CivFan

13

Linuxカーネルでの作業で十分な場合は、単に行うことができます

ulimit -s 100000

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

更新:勇気があるなら、あなたは言うことができます

ulimit -s unlimited

また、十分なRAMがあればシェルの拡張でも問題ありません。


それはハックです。スタック制限をどのように設定するかをどのように知っていますか?これは、同じセッションで開始された他のプロセスにも影響します。
クサラナナンダ

1
ええ、それはハックです。ほとんどの場合、この種のハッキングは1回限りです(とにかく大量のファイルを手動で移動する頻度は?)。プロセスがすべてのRAMを消費しないことが確実な場合は、設定することができulimit -s unlimited、実質的に無制限のファイルに対して機能します。
ミッコランタライネン

ではulimit -s unlimited、実際のコマンドラインの上限2 ^ 31または2 GBです。(MAX_ARG_STRLENカーネルソース。)
ミッコランタライネン

9

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

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

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

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

100万を超えるファイルmvを使用すると、find@ Gillesが投稿したPOSIX ソリューションを使用して必要な数個のプロセスではなく、100万を超えるプロセスが生成されます。つまり、この方法では不必要なCPUチャーンが大量に発生します。
CivFan

@CivFan別の問題は、変更されたバージョンが元のバージョンと同等であることを自分自身に納得させることです。いくつかの拡張機能を除外するcaseための*拡張の結果に関するステートメントは、元の!(*.jpg|*.png|*.bmp)式と同等であることが簡単にわかります。find答えは等価ではないという事実にあります。サブディレクトリに降ります(-maxdepth述語が表示されません)。
カズ

-name . -o -type d -prune -oサブディレクトリへの下降から保護します。-maxdepth明らかにPOSIX準拠ではありませんが、私のfindmanページには記載されていません。
CivFan

リビジョン1にロールバックされました。質問では、ソース変数または宛先変数について何も言われていないため、回答に不要な問題が追加されます。
カズ

5

以前に提供されていたものよりも積極的なソリューションについては、カーネルソースをプルアップして編集してください include/linux/binfmts.h

のサイズをMAX_ARG_PAGES32より大きい値に増やします。これにより、カーネルがプログラム引数に許可するメモリ量が増加し、それにより、100万個のファイルまたは実行しているものにコマンドmvまたはrmコマンドを指定できます。再コンパイル、インストール、再起動します。

気をつけて!これをシステムメモリに対して大きすぎる値に設定してから、多くの引数を指定してコマンドを実行すると、悪いことが起こります!これをマルチユーザーシステムに対して非常に慎重に行うと、悪意のあるユーザーがすべてのメモリを使い果たすのが容易になります。

カーネルを手動で再コンパイルして再インストールする方法がわからない場合は、現時点ではこの答えが存在しないふりをするのが最善です。


5

"$origin"/!(*.jpg|*.png|*.bmp)catchブロックの代わりに使用するより単純なソリューション:

for file in "$origin"/!(*.jpg|*.png|*.bmp); do mv -- "$file" "$destination" ; done

@Score_Underに感謝

複数行のスクリプトの場合、次のことができます(ドロップさ;れる前に注意してくださいdone):

for file in "$origin"/!(*.jpg|*.png|*.bmp); do        # don't copy types *.jpg|*.png|*.bmp
    mv -- "$file" "$destination" 
done 

すべてのファイルを移動するより一般化されたソリューションを実行するには、ワンライナーを実行できます。

for file in "$origin"/*; do mv -- "$file" "$destination" ; done

インデントを行うと、次のようになります。

for file in "$origin"/*; do
    mv -- "$file" "$destination"
done 

これにより、元のすべてのファイルが取得され、それらが1つずつ宛先に移動されます。$fileファイル名にスペースまたはその他の特殊文字が含まれている場合は、引用符で囲む必要があります。

完全に機能するこのメソッドの例を次に示します

for file in "/Users/william/Pictures/export_folder_111210/"*.jpg; do
    mv -- "$file" "/Users/william/Desktop/southland/landingphotos/";
done

forループで元のglobのようなものを使用して、要求されているものにより近いソリューションを取得できます。
Score_Under

元のグロブとはどういう意味ですか?
ホワイトキャット

それが少しわかりにくい場合は申し訳ありませんが、質問のグロブを参照していました:!(*.jpg|*.png|*.bmp)。それをグロブ"$origin"/!(*.jpg|*.png|*.bmp)することでforループに追加できます。これにより、Kazの答えで使用されるスイッチが不要になり、forループの単純なボディを維持できます。
Score_Under June

素晴らしいスコア。私はあなたのコメントを取り入れ、私の答えを更新しました。
ホワイトキャット

3

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

import glob, shutil

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

1

mv数回実行してもかまわない場合は、使用中にその制限を回避できます。

部分を一度に移動できます。たとえば、英数字のファイル名の長いリストがあるとします。

mv ./subdir/a* ./

動作します。次に、別の大きなチャンクをノックアウトします。カップルが移動した後、使用に戻ることができますmv ./subdir/* ./


0

これが私の2セントです。これを .bash_profile

mv() {
  if [[ -d $1 ]]; then #directory mv
    /bin/mv $1 $2
  elif [[ -f $1 ]]; then #file mv
    /bin/mv $1 $2
  else
    for f in $1
    do
      source_path=$f
      #echo $source_path
      source_file=${source_path##*/}
      #echo $source_file
      destination_path=${2%/} #get rid of trailing forward slash

      echo "Moving $f to $destination_path/$source_file"

      /bin/mv $f $destination_path/$source_file
    done
  fi
}
export -f mv

使用法

mv '*.jpg' ./destination/
mv '/path/*' ./destination/
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.