GNU Make 中变量的减法和回显差异
Subtract and Echo Difference of Variables in GNU Make
我在回显 运行 任务之前的时间和任务结束的时间:
test:
@echo $(shell date)
@JUNIT_REPORT_PATH=test/report.xml ./node_modules/.bin/mocha test/integration
@echo $(shell date)
有没有办法将日期存储在 2 个变量中,然后显示它们之间经过的时间?
我假设它看起来像这样:
test:
$begin = $(shell date)
<do stuff>
$end = $(shell date)
@echo $end - $begin
任何指向有关 Make 的良好文档的指针也将不胜感激。
谢谢!
简答:你想要的都有可能:
test:
@begin=$$(date +%s); \
<do stuff in same shell: do not forget the ";" and the "\">; \
end=$$(date +%s); \
echo $$((end - begin))
冗长的回答:很抱歉解释冗长,我认为您的问题是值得的,因为您的示例解决方案有很多问题需要解决。
我假设您使用 bash,您的 make 调用 bash 来执行您的食谱,并且您的 date
命令来自 GNU coreutils。否则,接下来的操作可能无法按预期工作。如果你在 Mac OS X 下问另一个关于如何使用最新版本的 bash 和 GNU coreutils 而不是默认的问题。
回到你的问题。让我们研究一下您的示例解决方案:
test:
$begin = $(shell date)
<do stuff>
$end = $(shell date)
@echo $end - $begin
并逐步解决需要解决的问题。有几点需要考虑和理解:
1) 如何使用make变量:make变量赋值name = value
,扩展$(name)
或${name}
。请注意,如果您有一个单字母变量名称 A
,您也可以使用 $A
扩展它。因此,当 make 扩展示例配方的第一行时,$begin = $(shell date)
结果取决于 make 变量 b
是否存在以及是否具有非空值。因此,make 会将行扩展为 fooegin = <value>
或 egin = <value>
。让我们通过删除第一个 $
来解决这个问题:begin = $(shell date)
.
2) make 变量可以赋值的地方: 不在食谱中。因此,在您编写的配方中,begin
和 end
是 shell 变量。 shell 变量被分配 name=value
,而不是 name = value
。没有 spaces。在您的示例中,当 make 将 begin = <value>
传递给 shell 时,您将收到 begin: command not found
错误。让我们通过删除 spaces 来解决这个问题:begin=$(shell date)
.
3)日期的使用方法:默认格式不适合算术。使用 date +%s
到 return 当前日期 - 时间作为时间戳(自纪元以来的秒数)。让我们用 begin=$(shell date +%s)
.
来解决这个问题
4) 如何使用bash算术展开:$((end - begin))
计算shell变量end
和begin
。让我们用 @echo $((end - begin))
替换食谱的最后一行来解决这个问题。可是等等!这是行不通的,因为 make 会在将它传递给 shell 之前尝试扩展它,你猜怎么着,它会将 (end - begin
视为 make 变量名并将其扩展为空,因为没有这样的 make多变的。所以make会通过
echo )
到shell,你会得到一个语法错误。要解决这个问题,我们必须加倍 $
符号:@echo $$((end - begin))
。请注意,make 有时会扩展不止一次。在这些情况下,您需要更多 $
符号:@echo $$$$((end - begin))
用于双重扩展。但这不是你的情况。
5) 当 make 扩展它的变量和函数时: make 在它需要它们的值时扩展它的变量和函数,而不是当你想的时候。这是在执行食谱之前。因此,在您的示例中,begin
和 end
将具有相同的值,无论 <do stuff>
花费什么时间,它将是 make expanded [=52= 的瞬间日期和时间].尝试:
test:
@echo $(shell date +%s)
@sleep 2
@echo $(shell date +%s)
看到两个时间戳是一样的。让我们通过不使用 $(shell)
来解决这个问题:begin=$$(date +%s)
(注意 $$
)。 make 会将其扩展为 begin=$(date +%s)
并将其传递给 shell。 shell 将按照您的想法进行操作,并且 begin
shell 变量将在 shell 调用时分配当前时间戳。
6) make 如何处理食谱:每行在扩展后传递给不同的shell。是的,在您的示例中,您将至少有 4 个不同的 shell 调用。更多 if <do stuff>
隐藏多行。这里的问题是,我们的 begin
和 end
shell 变量对于将执行最后一行的 shell 不存在,您将得到另一个 shell错误。让我们通过将所有 shell 命令放在同一行来解决此问题,并用 ;
shell 分隔符分隔:
test:
@begin=$$(date +%s); sleep 2; end=$$(date +%s); echo $$((end - begin))
宾果!它开始工作。请注意,初始 @
禁用整个 shell 命令列表的回显。
但是,对于很长的食谱,这不是很方便。别担心,你可以换行但是 last 字符必须是 \
来告诉 make 忽略换行符:
target:
@<do this>; <do that>; baz=1; \
<and also this>; \
if <condition>; then \
<do this>; \
else \
<do that>; \
fi; \
<and this>; \
echo $$baz
前面的配方在单个 shell 调用中执行。 shell变量baz
从头到尾都是同一个变量。初始 @
仍然禁用整个 shell 命令列表的回显。尝试:
test:
@begin=$$(date +%s); \
sleep 2; \
end=$$(date +%s); \
echo $$((end - begin))
并看到它像单行版本一样工作。 警告:\
必须真的只是 在换行符之前。在 \
之后添加一两个 space 非常容易。如果这样做,您将收到 shell 错误。最糟糕的是,几乎无法理解和调试。因此,如果您对此类食谱有奇怪的错误,请搜索行尾的 space。如果您知道如何执行此操作,请告诉您的编辑器突出显示 makefile 中行尾的空白。
我们快完成了。总而言之,
test:
@begin=$$(date +%s); \
<do stuff in same shell: do not forget the ";" and the "\">; \
end=$$(date +%s); \
echo $$((end - begin))
应该做你想做的。 make first notes the initial @
and disables the echoing, 然后它将配方扩展为一个命令列表:
begin=$(date +%s); <do stuff>; end=$(date +%s); echo $((end - begin))
并在唯一调用中将其传递给 shell。
最后但重要的评论:因为只有一个 shell 调用,所以它也快得多。如果你有复杂的 makefile 来做复杂的事情,它会产生很大的不同。如果可以,请始终使用它,并加快处理速度。
如果你只是想知道经过的时间,我建议为此使用 /usr/bin/time
(而不是 time
的 shell 版本),例如
/usr/bin/time -f "%E real,%U user,%S sys" g++ something
可悲的是,在一个计时器下做多件事变得丑陋:
/usr/bin/time -f "%E real,%U user,%S sys" sh -c 'g++ a; g++ b'
我只想在这里分享 /usr/bin/time。应该适用于简单的食谱。
可以在makefile中进行如下算术运算。
NUMBER1 := 10
NUMBER2 := 5
#Addition
ADD := $(shell echo ${NUMBER1}+${NUMBER2} | bc)
#Subtraction
SUBTRACT := $(shell echo ${NUMBER1}-${NUMBER2} | bc)
#Multiplication
MULTIPLY := $(shell echo ${NUMBER1}*${NUMBER2} | bc)
#Division
DIVIDE := $(shell echo ${NUMBER1}/${NUMBER2} | bc)
#Division (Floating Point)
DIVIDEF := $(shell echo "scale=3; ${NUMBER2}/${NUMBER1}" | bc)
#Modulo
MODULO := $(shell echo ${NUMBER1}%${NUMBER2} | bc)
#Comparison Greater Than
COMPARISON1 := $(shell echo ${NUMBER1}\>=${NUMBER2} | bc)
#Comparison Smaller Than
COMPARISON2 := $(shell echo ${NUMBER2}\<=${NUMBER2} | bc)
all:
@echo Addition ${ADD}
@echo Subtraction ${SUBTRACT}
@echo Multiplication ${MULTIPLY}
@echo Division ${DIVIDE}
@echo Division - Floating Point ${DIVIDEF}
@echo Modulo ${MODULO}
@echo Comparison Greater Than ${COMPARISON1}
@echo Comparison Smaller Than ${COMPARISON2}
礼貌:Makefile Tricks: Arithmetic – Addition, Subtraction, Multiplication, Division, Modulo, Comparison
我在回显 运行 任务之前的时间和任务结束的时间:
test:
@echo $(shell date)
@JUNIT_REPORT_PATH=test/report.xml ./node_modules/.bin/mocha test/integration
@echo $(shell date)
有没有办法将日期存储在 2 个变量中,然后显示它们之间经过的时间?
我假设它看起来像这样:
test:
$begin = $(shell date)
<do stuff>
$end = $(shell date)
@echo $end - $begin
任何指向有关 Make 的良好文档的指针也将不胜感激。
谢谢!
简答:你想要的都有可能:
test:
@begin=$$(date +%s); \
<do stuff in same shell: do not forget the ";" and the "\">; \
end=$$(date +%s); \
echo $$((end - begin))
冗长的回答:很抱歉解释冗长,我认为您的问题是值得的,因为您的示例解决方案有很多问题需要解决。
我假设您使用 bash,您的 make 调用 bash 来执行您的食谱,并且您的 date
命令来自 GNU coreutils。否则,接下来的操作可能无法按预期工作。如果你在 Mac OS X 下问另一个关于如何使用最新版本的 bash 和 GNU coreutils 而不是默认的问题。
回到你的问题。让我们研究一下您的示例解决方案:
test:
$begin = $(shell date)
<do stuff>
$end = $(shell date)
@echo $end - $begin
并逐步解决需要解决的问题。有几点需要考虑和理解:
1) 如何使用make变量:make变量赋值name = value
,扩展$(name)
或${name}
。请注意,如果您有一个单字母变量名称 A
,您也可以使用 $A
扩展它。因此,当 make 扩展示例配方的第一行时,$begin = $(shell date)
结果取决于 make 变量 b
是否存在以及是否具有非空值。因此,make 会将行扩展为 fooegin = <value>
或 egin = <value>
。让我们通过删除第一个 $
来解决这个问题:begin = $(shell date)
.
2) make 变量可以赋值的地方: 不在食谱中。因此,在您编写的配方中,begin
和 end
是 shell 变量。 shell 变量被分配 name=value
,而不是 name = value
。没有 spaces。在您的示例中,当 make 将 begin = <value>
传递给 shell 时,您将收到 begin: command not found
错误。让我们通过删除 spaces 来解决这个问题:begin=$(shell date)
.
3)日期的使用方法:默认格式不适合算术。使用 date +%s
到 return 当前日期 - 时间作为时间戳(自纪元以来的秒数)。让我们用 begin=$(shell date +%s)
.
4) 如何使用bash算术展开:$((end - begin))
计算shell变量end
和begin
。让我们用 @echo $((end - begin))
替换食谱的最后一行来解决这个问题。可是等等!这是行不通的,因为 make 会在将它传递给 shell 之前尝试扩展它,你猜怎么着,它会将 (end - begin
视为 make 变量名并将其扩展为空,因为没有这样的 make多变的。所以make会通过
echo )
到shell,你会得到一个语法错误。要解决这个问题,我们必须加倍 $
符号:@echo $$((end - begin))
。请注意,make 有时会扩展不止一次。在这些情况下,您需要更多 $
符号:@echo $$$$((end - begin))
用于双重扩展。但这不是你的情况。
5) 当 make 扩展它的变量和函数时: make 在它需要它们的值时扩展它的变量和函数,而不是当你想的时候。这是在执行食谱之前。因此,在您的示例中,begin
和 end
将具有相同的值,无论 <do stuff>
花费什么时间,它将是 make expanded [=52= 的瞬间日期和时间].尝试:
test:
@echo $(shell date +%s)
@sleep 2
@echo $(shell date +%s)
看到两个时间戳是一样的。让我们通过不使用 $(shell)
来解决这个问题:begin=$$(date +%s)
(注意 $$
)。 make 会将其扩展为 begin=$(date +%s)
并将其传递给 shell。 shell 将按照您的想法进行操作,并且 begin
shell 变量将在 shell 调用时分配当前时间戳。
6) make 如何处理食谱:每行在扩展后传递给不同的shell。是的,在您的示例中,您将至少有 4 个不同的 shell 调用。更多 if <do stuff>
隐藏多行。这里的问题是,我们的 begin
和 end
shell 变量对于将执行最后一行的 shell 不存在,您将得到另一个 shell错误。让我们通过将所有 shell 命令放在同一行来解决此问题,并用 ;
shell 分隔符分隔:
test:
@begin=$$(date +%s); sleep 2; end=$$(date +%s); echo $$((end - begin))
宾果!它开始工作。请注意,初始 @
禁用整个 shell 命令列表的回显。
但是,对于很长的食谱,这不是很方便。别担心,你可以换行但是 last 字符必须是 \
来告诉 make 忽略换行符:
target:
@<do this>; <do that>; baz=1; \
<and also this>; \
if <condition>; then \
<do this>; \
else \
<do that>; \
fi; \
<and this>; \
echo $$baz
前面的配方在单个 shell 调用中执行。 shell变量baz
从头到尾都是同一个变量。初始 @
仍然禁用整个 shell 命令列表的回显。尝试:
test:
@begin=$$(date +%s); \
sleep 2; \
end=$$(date +%s); \
echo $$((end - begin))
并看到它像单行版本一样工作。 警告:\
必须真的只是 在换行符之前。在 \
之后添加一两个 space 非常容易。如果这样做,您将收到 shell 错误。最糟糕的是,几乎无法理解和调试。因此,如果您对此类食谱有奇怪的错误,请搜索行尾的 space。如果您知道如何执行此操作,请告诉您的编辑器突出显示 makefile 中行尾的空白。
我们快完成了。总而言之,
test:
@begin=$$(date +%s); \
<do stuff in same shell: do not forget the ";" and the "\">; \
end=$$(date +%s); \
echo $$((end - begin))
应该做你想做的。 make first notes the initial @
and disables the echoing, 然后它将配方扩展为一个命令列表:
begin=$(date +%s); <do stuff>; end=$(date +%s); echo $((end - begin))
并在唯一调用中将其传递给 shell。
最后但重要的评论:因为只有一个 shell 调用,所以它也快得多。如果你有复杂的 makefile 来做复杂的事情,它会产生很大的不同。如果可以,请始终使用它,并加快处理速度。
如果你只是想知道经过的时间,我建议为此使用 /usr/bin/time
(而不是 time
的 shell 版本),例如
/usr/bin/time -f "%E real,%U user,%S sys" g++ something
可悲的是,在一个计时器下做多件事变得丑陋:
/usr/bin/time -f "%E real,%U user,%S sys" sh -c 'g++ a; g++ b'
我只想在这里分享 /usr/bin/time。应该适用于简单的食谱。
可以在makefile中进行如下算术运算。
NUMBER1 := 10
NUMBER2 := 5
#Addition
ADD := $(shell echo ${NUMBER1}+${NUMBER2} | bc)
#Subtraction
SUBTRACT := $(shell echo ${NUMBER1}-${NUMBER2} | bc)
#Multiplication
MULTIPLY := $(shell echo ${NUMBER1}*${NUMBER2} | bc)
#Division
DIVIDE := $(shell echo ${NUMBER1}/${NUMBER2} | bc)
#Division (Floating Point)
DIVIDEF := $(shell echo "scale=3; ${NUMBER2}/${NUMBER1}" | bc)
#Modulo
MODULO := $(shell echo ${NUMBER1}%${NUMBER2} | bc)
#Comparison Greater Than
COMPARISON1 := $(shell echo ${NUMBER1}\>=${NUMBER2} | bc)
#Comparison Smaller Than
COMPARISON2 := $(shell echo ${NUMBER2}\<=${NUMBER2} | bc)
all:
@echo Addition ${ADD}
@echo Subtraction ${SUBTRACT}
@echo Multiplication ${MULTIPLY}
@echo Division ${DIVIDE}
@echo Division - Floating Point ${DIVIDEF}
@echo Modulo ${MODULO}
@echo Comparison Greater Than ${COMPARISON1}
@echo Comparison Smaller Than ${COMPARISON2}
礼貌:Makefile Tricks: Arithmetic – Addition, Subtraction, Multiplication, Division, Modulo, Comparison