回答:
cp *.prj ../prjshp/
は正しいコマンドですが、サイズ制限に遭遇するまれなケースに遭遇しました。試した2番目のコマンドは意味がありません。
1つの方法はcp
、ファイルをチャンクで実行することです。find
このコマンドは、これを行う方法を知っています:
find -maxdepth 1 -name '*.prj' -exec mv -t ../prjshp {} +
find
現在のディレクトリとその下のディレクトリを再帰的に走査します。-maxdepth 1
深さ1で停止すること、つまりサブディレクトリに再帰しないことを意味します。-name '*.prj'
は、指定されたパターンに一致する名前のファイルに対してのみ動作することを意味します。パターンを囲む引用符に注意してくださいfind
。シェルではなくコマンドによって解釈されます。-exec … {} +
すべてのファイルに対して指定されたコマンドを実行することを意味します。必要に応じてコマンドを複数回呼び出し、コマンドラインの制限を超えないように注意します。mv -t ../prjshp
指定されたファイルをに移動します../prjshp
。この-t
オプションは、find
コマンドの制限のためにここで使用されます。見つかったファイル(で記号化された{}
)はコマンドの最後の引数として渡され、その後に宛先を追加することはできません。別の方法はを使用することrsync
です。
rsync -r --include='*.prj' --exclude='*' . ../prjshp
rsync -r … . ../prjshp
現在のディレクトリを../prjshp
再帰的にコピーします。--include='*.prj' --exclude='*'
一致するファイルをコピーし*.prj
、他のすべてを除外することを意味します(サブディレクトリを含むため.prj
、サブディレクトリ内のファイルは見つかりません)。cp * | grep '\.prj$' ../prjshp/
は意味がありませんが*
、最後のファイルがディレクトリ(別名cp SOURCE1 SOURCE2....DEST
)であるファイルのリストに展開される場合、構文的に有効です。パイプは意味をなさないのは確かですが、シェルに関する限り構文的にも有効です- dup()
ファイル記述子は問題なく、パイプのリーダー側は何も書き込まないためデータを取得cp
しません。
このコマンドはファイルを1つずつコピーし、それらが多すぎ*
て単一のcp
コマンドに展開できない場合でも機能します。
for i in *; do cp "$i" ../prjshp/; done
Argument list too long
エラーに直面したときに留意すべき3つの重要なポイントがあります。
コマンドライン引数の長さは、ARG_MAX
変数によって制限されます。POSIX定義では、「... [m] 環境データを含むexec関数への引数の最大長さ」(強調を追加)です。 -built-itコマンドは、exec()
そのコマンドのプロセスを生成するためにのいずれかを呼び出す必要があり、それが機能する場所ARG_MAX
です。さらに、コマンド自体への名前またはパス(たとえば、/bin/echo
)が役割を果たします。
シェルの組み込みコマンドはシェルによって実行されます。つまり、シェルはexec()
一連の関数を使用しないため、ARG_MAX
変数の影響を受けません。
以下のような特定のコマンド、xargs
およびfind
を認識しているARG_MAX
変数と繰り返しその制限の下でアクションを実行します
上記の点から、および関連する質問に対するKusalanandaの優れた回答に示されているようにArgument list too long
、環境が大きい場合にも発生する可能性があります。そのため、各ユーザーの環境は異なる可能性があり、バイト単位の引数サイズが関連することを考慮すると、単一の数のファイル/引数を見つけるのは困難です。
重要なことは、ファイルの数ではなく、使用するコマンドexec()
に関数ファミリと接線方向(スタックスペース)が含まれるかどうかに焦点を当てることです。
シェルビルトインを使用する
前に述べたように、シェル組み込み関数はに免疫があるARG_MAX
ようなものである制限、for
ループ、while
組み込みのループ、echo
およびビルトインprintf
-すべてのものは十分に実行されます。
for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done
関連する質問ファイルの削除について、のような解決策がありました:
printf '%s\0' *.jpg | xargs -0 rm --
これはシェルのビルトインを使用することに注意してくださいprintf
。externalを呼び出す場合printf
、を含むためexec()
、多数の引数で失敗します。
$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long
バッシュ配列
jlliagre の回答によると、bash
配列に制限を課していないため、danjpreronの回答に示されているように、ファイル名の配列を作成し、ループの反復ごとにスライスを使用することもできます。
files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do
cp -t /path/to/new_dir/ "${files[@]:I:1000}"
done
ただし、これにはbash固有で非POSIXであるという制限があります。
スタックスペースを増やす
ときどき、スタックスペースを増やすことを提案する人がいますulimit -s <NUM>
。Linuxでは、ARG_MAX値は各プログラムのスタックスペースの1/4です。つまり、スタックスペースを増やすと、引数のスペースが比例して増えます。
# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $(( $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304
Linux Journalを引用しているFranck Dernoncourtの回答によると、引数の最大メモリページの値を大きくしてLinuxカーネルを再コンパイルすることもできますが、それは必要以上の作業であり、引用されたLinux Journalの記事に記載されている悪用の可能性を開きます。
シェルを避ける
別の方法は、Ubuntuでデフォルトで使用されている、python
または使用するpython3
ことです。以下のpython + here-docの例は、40,000アイテムの範囲のどこかにあるファイルの大きなディレクトリをコピーするために私が個人的に使用したものです。
$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
> if os.path.isfile(f):
> shutil.copy(f,'./newdir/')
> EOF
再帰的なトラバーサルには、os.walkを使用できます。
私見では、ファイルの大群に対処するための最適なツールがあるfind
とxargs
。をご覧くださいman find
。をご覧くださいman xargs
。find
、その-print0
スイッチを使用して、スイッチを使用して理解NUL
できるファイル名のリスト(ファイル名には任意の文字execpt NUL
またはが含まれる場合があります/
)を生成します。次に、許可されている最長のコマンド(ほとんどのファイル名、最後に半ファイル名がない)を構築して実行します。ファイル名がなくなるまでこれを繰り返します。実行して制限を確認します。xargs
-0
xargs
xargs
find
xargs --show-limits </dev/null
問題を解決するには、(およびを確認man cp
して確認した後--target-directory=
):
find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/