在 makefile 中,我有
@echo "$(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES)" | tr ' ' '\n' >> $@
问题是它$(CLEAN_FILES)
很大,所以当我运行 make 时,我得到
make: execvp: /bin/sh: Argument list too long
我使用的是 Xubuntu 18.10。
编辑:我应该提供更多背景信息。我正在研究的是一个 make 规则(我正在使用 GNU make)来自动生成文件.hgignore
。这是完整的 make 规则:
.hgignore : .hgignore_extra
@echo "Making $@"
@rm -f $@
@echo "# Automatically generated by Make. Edit .hgignore_extra instead." > $@
@tail -n +2 $< >> $@
@echo "" >> $@
@echo "# The following files come from the Makefile." >> $@
@echo "syntax: glob" >> $@
@echo "$(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES)" | tr ' ' '\n' >> $@
@chmod a-w $@
.PHONY : .hgignore
编辑2:根据@mosvy的建议,我也尝试过
.hgignore : .hgignore_extra
@echo "Making $@"
@rm -f $@
@echo "# Automatically generated by Make. Edit .hgignore_extra instead." > $@
@tail -n +2 $< >> $@
@echo "" >> $@
@echo "# The following files come from the Makefile." >> $@
@echo "syntax: glob" >> $@
$(file >$@) $(foreach V,$(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES),$(file >>$@,$V))
@true
@chmod a-w $@
.PHONY : .hgignore
运行make .hgignore
此命令后,我不再收到“参数列表太长”错误,但生成的 .hgignore 文件仅包含该syntax: glob
行之前的输出,之后不包含任何内容。
答案1
正如 @schily 已经解释的那样,这不是 shell 问题,不能使用xargs
、引用、分割成更多 echo;
等来解决。 make 操作中的所有文本都作为参数传递给单个execve(2)
,并且它不能长于操作系统允许的最大大小。
如果您使用 GNU make(Linux 上的默认设置),您可以使用它file
和foreach
功能:
TEST = $(shell yes foobar | sed 200000q)
/tmp/junk:
$(file >$@) $(foreach V,$(TEST),$(file >>$@,$V))
@true
.PHONY: /tmp/junk
这会将所有由$(TEST)
换行符分隔的单词打印到名为 的文件中$@
。它基于 make 的类似示例手动的。
你的 Makefile 可能会被重新设计成更易于管理的东西,不需要花哨的 GNU 功能,但很难从你发布的代码片段中看出如何做。
更新:
对于问题的确切片段,可以这样做:
.hgignore : .hgignore_extra
$(info Making $@)
$(file >[email protected])
$(file >>[email protected],# Automatically generated by Make. Edit .hgignore_extra instead.)
$(shell tail -n 2 $< >>[email protected])
$(file >>[email protected],)
$(file >>[email protected],# The following files come from the Makefile.)
$(file >>[email protected],syntax: glob)
$(foreach L, $(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES), $(file >>[email protected],$L))
@mv -f [email protected] $@
@chmod a-w $@
.PHONY : .hgignore
我对其进行了一些更改,因此它首先写入.hgignore.new
,如果一切顺利,然后才转移.hgignore.new
到.hgignore
。您必须将缩进空格改回制表符,因为这哑的界面正在破坏空白。
答案2
在UNIX系统适用以下规则:
以下数据构成进程的初始堆栈:
- 所有环境字符串的 strlen() 总和 + 每个字符串的最后一个 nul 字符
- 所有参数字符串的 strlen() 总和 + 每个字符串的最后一个 nul 字符
- 环境数组:n+1 环境字符串 * sizeof char *
- argv 数组:n+1 个参数字符串 * sizeof char *
- 一些额外的数字
所有这些数据不得超过ARG_MAX
。
在历史上的 UNIX 上, 的ARG_MAX
值为10240或者20480 字节。
SunOS-4.0(1987 年 12 月发布)将该限制提高到1MB
Solaris-7.0(于 1997 年发布)引入了 64 位支持,并且为了避免 64 位系统的实际较小限制(由于较大的env
和argv
数组而导致char *
),ARG_MAX
被提升为2MB对于 64 位程序。
顺便说一句:现代 POSIX 兼容操作系统包括对getconf
程序的支持并getconf ARG_MAX
打印实际值。在 64 位 Linux 上,返回2MB所以 Linux 上的第一个视图似乎采用 SunOS 增强功能......
现在让我们看看make
:
该make
程序通过以下方式调用命令Makefiles
:
sh -ce command
哪里command
是一个单一参数那就是扩大您在 的操作行中看到的字符串Makefile
。
SunPro Make
在 20 世纪 90 年代初引入了一项优化:
- 如果命令行不包含 shell 元字符,
make
它本身会标记命令行并通过以下方式调用命令:execv()
以避免 shell 调用的开销。
后来,gmake
又smake
采用了这种优化。
smake
2012年引入了另一项优化:
- 如果 shell 命令是由
echo
一个以 a 结尾的简单命令引入的;
,并且如果以下命令行不包含 shell 元字符,则 会echo
内联到并执行 viasmake
之后的命令,以减少现代构建系统的开销通常用于抑制命令回显,而是使用简化的调用来使输出更易于理解(例如,参见 1993 年 2 月引入的 Schily Makefile 系统)。;
execv()
@
make
echo
make
这些规则都不make
适用于您的make
命令行,因为您的命令行包含 shell 元字符。所以你的整个命令是通过以下方式调用的:
sh -ce command
其中command
是一个字符串,其中包含由 makefile 引起的总大小。
现在看来,Linux 内核既不兼容 UNIX,也不兼容 POSIX,并且强制实施了 UNIX 上从未存在的附加限制。这个额外的限制似乎是基于单个字符串的最大长度。
如果这是真的,这将取消 Linux 的资格,因为它将阻止 Linux 与make
.
您是否考虑过向 Linux 内核人员提交错误报告?
答案3
在这种情况下,你的问题是你为什么要这样做?看起来您正在尝试创建某种 .ignore 文件,其中包含可能是程序输出或 glob 的所有文件。在前一种情况下,我建议将程序输出直接传递到tr
(如果绝对必要的话),然后传递到文件,而不通过中间变量。如果你使用 glob 那么你可以使用一个非常简单的for
环形:
for file in *
do
echo "$file" >> out.txt
done
(如有必要,请在 Makefile 中转义。)
笼统地说:
- 对于任何大数据块,使用管道和重定向而不是变量。它们确实非常快(因为它们的处理速度与管道中的每个程序处理输入的速度一样快)。
- 使用
xargs
当你真的需要的时候。 - 避免无用的
echo
s 和cat
s。
答案4
看这个答案,特别是这句话:
一个参数的长度不得超过 MAX_ARG_STRLEN (131072)。如果您生成像“sh -c '使用长参数生成'”这样的长调用,这可能会变得相关。
另一方面,要传递的参数总数的限制相当高,因此也许只是将它们作为单独的参数传递,因为最终结果echo
是相同的?
这只是"
从原始命令中删除 s:
@echo $(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES) | tr ' ' '\n' >> $@
希望这足以将参数保持在参数数量(相当高)和每个参数长度(这将不再是问题,因为每个参数现在都很短)的限制内。
更新:删除引号并不能真正完全解决这个问题,因为make
在 shell 中运行每个命令时,使用相当于sh -c '...'
整个命令成为单个参数的位置,因此它仍然会绑定到单个参数的限制长度,即 131,072 x86 平台上的 Linux 上的字节。