为啥在传递引用的参数时会得到“/bin/sh:参数列表太长”?
Posted
技术标签:
【中文标题】为啥在传递引用的参数时会得到“/bin/sh:参数列表太长”?【英文标题】:Why do I get "/bin/sh: Argument list too long" when passing quoted arguments?为什么在传递引用的参数时会得到“/bin/sh:参数列表太长”? 【发布时间】:2012-07-13 14:00:35 【问题描述】:一个命令行可以传递给sh -c ''
多长时间? (在 bash 和 bourne shell 中)
这个限制远低于操作系统的限制(在现代 Linux 的情况下)。
例如:
$ /bin/true $(seq 1 100000)
$ /bin/sh -c "/bin/true $(seq 1 100000)"
bash: /bin/sh: Argument list too long
我该如何规避这个问题?
更新
我想指出getconf
在这里帮不上忙(因为这不是系统限制):
$ seq 1 100000 | wc -c
588895
$ getconf ARG_MAX
2097152
更新 #2
现在我明白了这里的意义所在。这不是 shell 限制,而是系统限制,而是针对每个参数的长度,而不是针对整个 arglist。
$ /bin/true $(seq 1 100000)
$ /bin/true "$(seq 1 100000)"
bash: /bin/true: Argument list too long
感谢 CodeGnome 的解释。
【问题讨论】:
相关:Does "argument list too long" restriction apply to shell builtins? 另见Argument list too long error for rm, cp, mv commands 【参考方案1】:创建一个以#!/bin/sh
作为第一行的文件,然后将其余命令放在后续行中? :)
更重要的是,您还可以使用 -s
选项从 STDIN 读取命令,这样您就可以生成长命令行并将其输入到 /bin/sh -s
【讨论】:
即使没有-s
,你也可以这样做;对我知道那个;可能这是最好的解决方案,除非您需要使用 shell 标准输入来处理其他事情
我认为 CodeGnome 找到了解决方案。看看他的回答。
排序 - 除了仍然存在最大命令行长度,并且“for $(生成大量参数的命令);do;done”结构是一个可以超过该长度的命令行长度。在 STDIN 上输入命令(无论是 -s
还是 for arg in $(</dev/stdin)
)确实是获得实际无限“参数”支持的唯一方法。【参考方案2】:
TL;DR
单个参数必须短于 MAX_ARG_STRLEN。
分析
根据this link:
并且作为自 2.6.23 以来的附加限制,一个参数不得长于 MAX_ARG_STRLEN (131072)。如果您生成像“sh -c 'generated with long arguments'”这样的长调用,这可能会变得相关。
这正是 OP 确定的“问题”。虽然允许的参数数量可能非常大(请参阅getconf ARG_MAX
),但当您将带引号的命令传递给 /bin/sh 时,shell 会将带引号的命令解释为单个字符串。在 OP 的示例中,正是这个单个字符串超出了 MAX_ARG_STRLEN 限制,而不是扩展参数列表的长度。
具体实现
参数限制是特定于实现的。但是,this Linux Journal article 建议了几种解决方法,包括增加系统限制。这可能并不直接适用于 OP,但它在一般情况下仍然有用。
做点别的事情
OP 的问题实际上并不是真正的问题。问题是强加一个不能解决现实问题的任意约束。
您可以使用循环轻松解决这个问题。例如,使用 Bash 4:
for i in 1..100000; do /bin/sh -c "/bin/true $i"; done
工作得很好。它肯定会很慢,因为您在每次通过循环时都会生成一个进程,但它肯定会绕过您遇到的命令行限制。
描述你真正的问题
如果循环无法解决您的问题,请更新问题以描述您实际尝试使用非常长的参数列表解决的问题。探索任意行长限制是一项学术活动,而不是 Stack Overflow 的主题。
【讨论】:
这不是系统限制!这是外壳限制! 我在带有2.6.31
的系统上没有MAX_ARG_STRLEN
@DennisWilliamson:我也是(3.2.0-rc7-686-pae);但无论如何,参数长度是重点。唯一的问题是如何找到它。
@CodeGnome:有些系统没有MAX_ARG_STRLEN
(我和丹尼斯的系统)。所以我们需要找到另一种方法来找到这个值。
@DennisWilliamson execve(2) 说“在内核 2.6.23 及更高版本上……每个字符串的限制是 32 页(内核常量 MAX_ARG_STRLEN),最大字符串数是 0x7FFFFFFF。”因此,您的系统似乎确实有它,但它是一个内核常量。【参考方案3】:
在我的操作系统上,它是二分法获得的最大长度
/bin/sh -c "/bin/true $(perl -e 'print"a"x131061')"
所以它给出 131071
但是没有理由让一行这么长;如果是因为参数太多,可以用“$@”代替;例如
/bin/sh -c 'command "$@"' -- arg1 arg2 .. argn
【讨论】:
【参考方案4】:我没有收到该错误消息。我的秘密?单引号:
/bin/sh -c '/bin/true $(seq 1 100000)'
如果我使用双引号,每个 shell 都会出现该错误:
$ /bin/sh -c "/bin/true $(seq 1 100000)"
-bash: /bin/sh: Argument list too long
$ /bin/bash -c "/bin/true $(seq 1 100000)"
-bash: /bin/bash: Argument list too long
$ /bin/ksh -c "/bin/true $(seq 1 100000)"
-bash: /bin/ksh: Argument list too long
$ /bin/zsh -c "/bin/true $(seq 1 100000)"
-bash: /bin/zsh: Argument list too long
当使用双引号时,参数列表在当前 shell 中被扩展,这一事实证明了 Bash 是发出错误“-bash: ...”的那个,而不管使用哪个 shell 来运行命令。顺便说一下,在我的系统上,sh
是 Dash。
即使对于其他“主机”外壳也是如此:
$ dash
$ /bin/bash -c '/bin/true $(seq 1 100000)'
$ /bin/bash -c "/bin/true $(seq 1 100000)"
dash: /bin/bash: Argument list too long
病人:医生,我这样做会很痛。” 医生:不要那样做。
【讨论】:
丹尼斯,单/双引号不是重点。我在这里使用$(seq 1 100000)
仅用于演示目的;你如何产生这个长字符串并不重要;当然我在这里需要双引号,因为我需要很长的字符串。主要问题是sh -c ''
的论点可以持续多久
@IgorChubin:答案是,如果它很重要,那么你做错了。顺便说一句,在我的系统上,它是131071。
@IgorChubin:见“做点别的”here。
真是个好主意 :) 但无论如何,我想 CodeGnome 已经找到了正确的答案。真正的重点是参数的长度。
为了完整性:/bin/bash -c '/bin/true "$(seq 1 100000)"'
也因为 shell 如何标记字符串而中断。 +1 用于解决这个 X/Y 问题中的 Y,不过,我认为发布相关问题的解决方案很有用。以上是关于为啥在传递引用的参数时会得到“/bin/sh:参数列表太长”?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 GridSearchCV 中使用 StandardScaler 时会得到不同的结果?