web-dev-qa-db-ja.com

CP:コピーユーティリティの最大ソースファイル数引数

/ src /の下に無数のファイルがあることを考慮してください

cp /src/* /dst/

cpが正常に処理するファイルの数は?

11
Mike

これは、システムとバージョン、引数の数とサイズ、環境変数名の数とサイズに大きく依存します。

Unixでは伝統的に、(getconf ARG_MAXによって報告された)制限は、以下の累積サイズに多かれ少なかれありました。

  • 引数文字列の長さ(終了'\0'を含む)
  • これらの文字列へのポインタの配列の長さ。したがって、64ビットシステムでは通常、引数あたり8バイト
  • 環境文字列の長さ(末尾の'\0'を含む)。環境文字列は慣例によりvar=valueのようなものです。
  • これらの文字列へのポインタの配列の長さ。したがって、64ビットシステムでは通常、引数あたり8バイト

cpも引数としてカウントされます(最初の引数です)。

Linuxでは、バージョンによって異なります。最近の動作は変わり、固定スペースではなくなりました。

Linux 3.11で確認すると、getconf ARG_MAXはスタックサイズに設定された制限の4分の1を報告するようになりました。それが512kiB未満の場合は128kiB)。

(以下のzsh構文):

$ limit stacksize
stacksize       8MB
$ getconf ARG_MAX
2097152
$ limit stacksize 4M
$ getconf ARG_MAX
1048576

その制限は、引数と環境文字列の累積サイズといくつかのオーバーヘッドにあります(ページ境界での配置の考慮に疑いがあります)。ポインターのサイズは考慮されません。

限界を探して、私は得ます:

$ /bin/true {1..164686}
$ /bin/true {1..164687}
zsh: argument list too long: /bin/true
$ x= /bin/true {1..164686}
$ x=1 /bin/true {1..164686}
zsh: argument list too long: /bin/true

その場合の分割前の最大累積サイズは次のとおりです。

$ (env _=/bin/true x=;print -l /bin/true {1..164686}) | wc -c
1044462

だからといって、100万個の空の引数を渡せるわけではありません。 64ビットシステムでは、100万個の空の引数によって8MBのポインターリストが作成されます。これは、私のスタックサイズ4MiBを超えます。

$ IFS=:; /bin/true ${=${(l.1000000..:.)${:-}}}
zsh: killed     /bin/true ${=${(l.1000000..:.)${:-}}}

(E2BIGエラーではないことがわかります。execveシステムコール以降にある場合でも、どの時点でプロセスが強制終了されるかはわかりません)。

また、(Linux 3.11でも)スタックのサイズに関係なく、単一の引数または環境文字列の最大サイズは128kiBであることに注意してください。

$ /bin/true ${(l.131071..a.)${:-}} # 131072 OK
$ /bin/true ${(l.131072..a.)${:-}} # 131073 not
zsh: argument list too long: /bin/true
$ /bin/true ${(l.131071..a.)${:-}} ${(l.131071..a.)${:-}} # 2x 131072 OK
18

これは、システム間で変化する可能性のあるARG_MAXの値に依存します。システム実行の値を見つけるには(例として私の結果を表示):

_$ getconf ARG_MAX
2097152
_

これはcpやシェルとは関係ありません。これはカーネルによって課される制限であり、引数が_ARG_MAX_よりも長い場合、(exec())コマンドを実行しません。したがって、cpに指定した引数リストの長さがARG_MAXより大きい場合、cpコマンドはまったく実行されません。

それからあなたの主な質問に答えるために、cpはあまり多くの引数で実行されることはないので、ファイルを処理しません。また、これは引数の数ではなく長さに依存することにも触れておきます。ファイル名が非常に少ないが非常に長い場合でも、同じ問題が発生する可能性があります。


これらのエラーを回避する方法は、コマンドをループで実行することです。

_for file in /src/*; do cp "$file" /dst/; done
_
14
terdon