ファイルをコピーするときに引数リストが長すぎる


26

特定の拡張子のファイルをカウントする方法に関連した質問をしただけです。今、私はcpこれらのファイルを新しいdir

やっています、

cp *.prj ../prjshp/

そして

cp * | grep '\.prj$' ../prjshp/

しかし、彼らは同じエラーを与えています、

bash:/ bin / cp:引数リストが長すぎます

それらをコピーするにはどうすればよいですか?


回答:


36

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、サブディレクトリ内のファイルは見つかりません)。

3
ここで最も簡単なソリューションは、rsyncです。
ntk4

少し気味が悪いように、2番目のコマンドcp * | grep '\.prj$' ../prjshp/ は意味がありませんが*、最後のファイルがディレクトリ(別名cp SOURCE1 SOURCE2....DEST)であるファイルのリストに展開される場合、構文的に有効です。パイプは意味をなさないのは確かですが、シェルに関する限り構文的にも有効です- dup()ファイル記述子は問題なく、パイプのリーダー側は何も書き込まないためデータを取得cpしません。
Sergiy Kolodyazhnyy

findとrsyncの両方が、同じ引数リストの長すぎるエラーを生成しました。forループは、最も簡単な回避策でした。
Meezaan-ud-Din

確かにrsyncは大量コピーを行う方法ですが、Linuxがどこまで来たのか、このような愚かな欠陥/バグがあり、はい、それを欠陥/バグと考えています。
MitchellK

22

このコマンドはファイルを1つずつコピーし、それらが多すぎ*て単一のcpコマンドに展開できない場合でも機能します。

for i in *; do cp "$i" ../prjshp/; done

これは私のために動作します。
1rq3fea324wre

1
シンプルで効果的。プロジェクトのビデオから抽出した〜1/4百万のjpegを削除する同様の問題がありました。これは私が使用したアプローチです。
オタク長老

5

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を使用できます。

こちらもご覧ください:


2

私見では、ファイルの大群に対処するための最適なツールがあるfindxargs。をご覧くださいman find。をご覧くださいman xargsfind、その-print0スイッチを使用して、スイッチを使用して理解NULできるファイル名のリスト(ファイル名には任意の文字execpt NULまたはが含まれる場合があります/)を生成します。次に、許可されている最長のコマンド(ほとんどのファイル名、最後に半ファイル名がない)を構築して実行します。ファイル名がなくなるまでこれを繰り返します。実行して制限を確認します。xargs-0xargsxargsfindxargs --show-limits </dev/null

問題を解決するには、(およびを確認man cpして確認した後--target-directory=):

find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.