web-dev-qa-db-ja.com

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

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

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

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

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

xargs は、ジョブのツールです。それ、または find with -exec … {} +。これらのツールはコマンドを数回実行し、一度に渡すことができるのと同じ数の引数を使用します。

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

ファイル名に空白も\"'もない場合は、xargsへの入力としてファイル名を指定するだけです(echoコマンドはbash組み込みなので、コマンドラインの長さの制限はありません):

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 builtin をロードできます。

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スタイルのグロブを使用:

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/

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

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

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

getconf ARG_MAX

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

16

オペレーティングシステムの引数渡し制限は、シェルインタープリター内で発生する拡張には適用されません。したがって、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
9
Kaz

小さなスクリプトを書くのが最も簡単な場合があります。 Pythonの場合:

import glob, shutil

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

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

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

注意してください!これをシステムメモリに対して大きすぎる値に設定し、多くの引数を指定してコマンドを実行すると、悪いことが起こります。マルチユーザーシステムに対してこれを行うには非常に注意してください。これにより、悪意のあるユーザーがすべてのメモリを使いやすくなります。

カーネルを手動で再コンパイルして再インストールする方法がわからない場合は、この答えが今のところ存在しないふりをすることをお勧めします。

5
Perkins

Catchブロックの代わりに"$Origin"/!(*.jpg|*.png|*.bmp)を使用したより簡単なソリューション:

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 

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

これは完全に機能したこの方法の例です

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

この制限を回避するには、mvを数回実行してもかまいません。

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

mv ./subdir/a* ./

うまくいきます。次に、別の大きな塊をノックアウトします。数回移動すると、mv ./subdir/* ./の使用に戻ることができます

1
Kristian

これが私の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/
0
Ako