如何在GNU makefile中将文件列表拆分为单独的行

如何在GNU makefile中将文件列表拆分为单独的行,makefile,gnu-make,freebsd,solaris-10,Makefile,Gnu Make,Freebsd,Solaris 10,我有一个包含文件列表的变量,文件列表用字符串\u NEWLINE\u分隔。我需要将该变量输出到一个文件中,以便每个文件都位于单独的行中。诀窍在于它需要在FreeBSD和Solaris上工作 这就是我现在正在尝试的: echo "lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf_NEWLINE_lib/alarms-1.2/priv/snmp_conf/agent.conf.src: lib

我有一个包含文件列表的变量,文件列表用字符串
\u NEWLINE\u
分隔。我需要将该变量输出到一个文件中,以便每个文件都位于单独的行中。诀窍在于它需要在FreeBSD和Solaris上工作

这就是我现在正在尝试的:

echo "lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf_NEWLINE_lib/alarms-1.2/priv/snmp_conf/agent.conf.src: lib/alarms/priv/snmp_conf/agent.conf.src_NEWLINE_lib/alarms-1.2/priv/snmp_conf/community.conf: lib/alarms/priv/snmp_conf/community.conf" | sed 's|_NEWLINE_|\'$'\n|g'
这适用于FreeBSD和Solaris上的shell。但是当在Solaris上的GNUmakefile中运行时,我得到了以下信息(请注意每行末尾的$:

如果我从sed中删除
\'$'
,那么它在Solaris上工作,但在FreeBSD上不工作。也许有一种方法可以根据执行makefile的系统来判断使用哪个版本

编辑: 多亏了bobbogo提出的解决方案,我创建了一个示例性的makefile,它提供了所需的结果,并且似乎正在FreeBSD和Solaris上工作:

one-line := lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf_NEWLINE_lib/alarms-1.2/\
priv/snmp_conf/agent.conf.src: lib/alarms/priv/snmp_conf/agent.conf.src_NEWLINE_lib/alarms-1.2/priv/snmp_conf/comm\
unity.conf: lib/alarms/priv/snmp_conf/community.conf

many-lines := { echo '$(subst _NEWLINE_,' && echo ',${one-line})'; }

.PHONY: all
all:
  $(shell $(many-lines) > test.txt)

免责声明:我没有使用Solaris或FreeBSD的经验。。。反正是这样

在make中,可以使用$(patsubst pattern,replacement,text)替换模式。 试试这个

FILENAMES := "lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf_NEWLINE_lib/alarms-1.2/priv/snmp_conf/agent.conf.src: lib/alarms/priv/snmp_conf/agent.conf.src_NEWLINE_lib/alarms-1.2/priv/snmp_conf/community.conf: lib/alarms/priv/snmp_conf/community.conf"

.PHONY: all

all:
    @echo $(patsubst _NEWLINE_,${\n},$(FILENAMES))

尝试了许多不同的解决方案,包括定义此答案中提到的
\n

真正的问题是跨平台执行
echo
命令时不一致,而且默认情况下
make
使用
sh
调用shell命令,这本身非常不灵活

由于这个答案,我找到了一个更好的方法:

更好的方法是使用
printf
而不是
echo

使用
\n
而不是
\u NEWLINE\u
构造字符串,以将输出文件中的各个部分分隔为单独的行:

some_string = lib/alarms-1.2/priv/snmp_conf/target_params.conf: lib/alarms/priv/snmp_conf/target_params.conf\nlib/alarms-1.2/priv/snmp_conf/community.conf: lib/alarms/priv/snmp_conf/community.conf\n
然后在makefile中按如下方式简单打印:

@printf "$(some_string)" >> $(some_file)

FreeBSD和Solaris都可以使用。

作为替代方案,我认为您的第一种方法可以使用,如果您只是将
$
翻一番以“转义”它:


如果这是GNU make,那么请在make中完成所有操作

one-line := lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf_NEWLINE_lib/alarms-1.2/priv/snmp_conf/agent.conf.src: lib/alarms/priv/snmp_conf/agent.conf.src_NEWLINE_lib/alarms-1.2/priv/snmp_conf/community.conf: lib/alarms/priv/snmp_conf/community.conf

define \n


endef

many-lines := $(subst _NEWLINE_,${\n},${one-line})
现在,
${many line}
正是您想要的。令人恼火的是,它很难在外壳线中使用。如果您这样做:

tgt:
    echo '${many-lines}'
make将为每一行调用一个单独的shell。第一次shell调用将获得一个未配对的
,并以错误退出

.ONESHELL:
tgt:
    echo '${many-lines}'
将以一种侵入性的方式工作。正确的修复方法是确保
${many line}
的每一行都具有有效的sh语法。有些人满嘴都是,比如:

echolines = $(subst ${\n},'${\n}echo ',echo '${many-lines}')
.PHONY: aa
aa:
    $(call echolines,${many-lines})

嘘。

这行不通,因为换行符不是与其他单词分开的空格。但是感谢您的尝试。顺便说一句,使用subst而不是patsubst也不起作用(因为\n未正确替换)。我猜你没有测试你的代码?不,没有。在Solaris上,它输出conf$nlib(用$n替换换行符)。谢谢,这是一个好的开始。我也在想类似的事情,但是我没有耐心去弄清楚回声在哪里。您是否能够将回音每行添加到文件中,因为这是最终目标?我认为在您的方法中,每一行都应该有格式:“echo'target:file'>>test.txt”,但我不知道在哪里添加“>>test.txt”部分。在我看来,最后一行似乎有问题,因为它不是以“\n”结尾的,而且不容易替换。我的意思是,在结尾添加换行符应该仍然是可能的,但它变得更加混乱。事实上,我认为我得到了
$echolines
错误,因为echo总是添加一个换行符(除了一些
echo
中的
-c
)。睡过觉后,更好的方法是将
str1\nstr2\nstr3\n
转换为
{echo'str1'&&echo'str2'&&echo'str3'}
。这是一个单一的shell调用,使用单一的间接寻址(
$(调用echolines2,${many lines})>$@
)会非常满意。我不确定我可能做错了什么,但有些东西仍然不起作用:
many lines:=echo'$(subst_NEWLINE,&&echo',${one line})
echolines=$(subst${\n},{$\n},{echo',${many lines})
$(调用echolines,${many lines})>test.txt
将一行打印到文件中,另两行打印到标准输出中。请正确阅读我之前的评论。您希望shell命令看起来像
{echo'str1'和&echo'str2'和&echo'str3';}>文件“
。注意那些卷发。它们导致所有的
echo
s都发生间接寻址。大括号中的AFAIK分组命令是bashshell的一个特性,而不是GNU make。因此,这将不起作用,因为make默认使用sh,而不是Bash。在某些字符串包含
%
字符的情况下,这是很脆弱的。在阅读了接受的答案后,我认为上面原始的
sed
方法是一种更简单的方法(请参见@Beta的答案)。不过要使用perl。perl是世界上最具可移植性的语言之一,并且总是胜过grep/sed/awk等人。
echo'${one line}'| perl-lpe的/_NEWLINE\u/\ n/g'
。(或者
perl-lpe'BEGIN{$$/=“{u NEWLINE}”
-timtowtdi,正如perl人所说的那样。)
.ONESHELL:
tgt:
    echo '${many-lines}'
echolines = $(subst ${\n},'${\n}echo ',echo '${many-lines}')
.PHONY: aa
aa:
    $(call echolines,${many-lines})