並べ替えが必要な100万を超えるファイルを含むフォルダーがありますが、mv
が常にこのメッセージを出力するため、実際には何もできません
-bash: /bin/mv: Argument list too long
このコマンドを使用して、拡張子のないファイルを移動しています。
mv -- !(*.jpg|*.png|*.bmp) targetdir/
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を超えている場合は、自分で問題を回避できることを期待しています。
オペレーティングシステムの引数渡し制限は、シェルインタープリター内で発生する拡張には適用されません。したがって、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
小さなスクリプトを書くのが最も簡単な場合があります。 Pythonの場合:
import glob, shutil
for i in glob.glob('*.jpg'):
shutil.move(i, 'new_dir/' + i)
以前に提供されたものよりも積極的なソリューションについては、カーネルソースをプルアップしてinclude/linux/binfmts.h
を編集してください
MAX_ARG_PAGES
のサイズを32より大きい値に増やします。これにより、カーネルがプログラムの引数に使用できるメモリの量が増加し、mv
またはrm
コマンドを指定して、 100万個のファイルなど、何をしていても。再コンパイル、インストール、再起動します。
注意してください!これをシステムメモリに対して大きすぎる値に設定し、多くの引数を指定してコマンドを実行すると、悪いことが起こります。マルチユーザーシステムに対してこれを行うには非常に注意してください。これにより、悪意のあるユーザーがすべてのメモリを使いやすくなります。
カーネルを手動で再コンパイルして再インストールする方法がわからない場合は、この答えが今のところ存在しないふりをすることをお勧めします。
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
この制限を回避するには、mv
を数回実行してもかまいません。
一度に部分を移動できます。たとえば、英数字のファイル名の長いリストがあるとします。
mv ./subdir/a* ./
うまくいきます。次に、別の大きな塊をノックアウトします。数回移動すると、mv ./subdir/* ./
の使用に戻ることができます
これが私の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/