4

试图做:

help:
    @echo "you must $(call red_text,clean)"

其中 red_text 定义为

red_text = $(shell tput setaf 1; echo -n "$1"; tput sgr0)

这将打印“您必须清洁”,其中“清洁”一词以红色打印。

问题是当 make 的输出被管道传输时(例如到更少)。在这种情况下,我不应该使用颜色,而是打印 $1。

我需要更新red_text以处理此案。为此,我认为我可以使用类似的东西,$(shell [ -t 1 ] ..)但问题是标准输出$(shell)永远不是终端。

当stdout不是终端时,如何更改red以处理这种情况?

4

3 回答 3

0
if test -t 1
then
    echo terminal
else
    echo file
fi
于 2013-01-31T12:51:17.890 回答
0

本着完美的精神,不是没有什么可添加的,而是没有什么可带走的(Antoine de Saint Exupery) ,首先不使用颜色来解决问题!真的。它很烂有固有的问题。你对终端做出假设,这些终端在某个好日子对一些可怜的用户来说是错误的。您遇到了这个交互式终端问题。您花时间解决一个问题,而这些问题本可以花在编写酷炫的功能而不是花哨的东西上。你取悦了错误的一群人,即色瘾者,而忽视了色差问题,比如红/绿盲用户(其中​​比你和我估计的要多)。

于 2013-01-31T16:43:17.587 回答
0

正如您所指出的,无法测试$(shell ...)输出是标准终端还是管道/文件。然而,我们可以做一些事情,每件事都有自己的优点/缺点。

make在自定义脚本中检测 TTY

我们只需编写一个简单的 shell 脚本来拦截对 native 的调用make,检测 TTY,并适当地定义一个变量。该解决方案的主要优点:

  • 简单,
  • 适用于echo$(info ...)命令$(shell ...)
  • 配方无需修改,
  • 每次回声都不会产生额外的进程,这在某些平台上可能会很慢(例如 Cygwin)。
ifdef IS_TTY
# DO NOT ADD TRAILING COMMENTS OR THIS WILL FAIL BECAUSE OF TRAILING SPACES
ESC := $(shell printf '\e')
R := $(ESC)[31m
Z := $(ESC)[0;0m
endif

$(info [info  ] $Rred$Z black)
$(shell echo >&2 "[shell ] $Rred$Z black")
if_fancy:
    @echo "[recipe] $Rred$Z black"

添加到路径中的自定义 make 脚本示例(例如/usr/local/bin/make):

#! /bin/bash

[ -t 1 ] && IS_TTY=1 || IS_TTY=0
exec /usr/bin/make IS_TTY=$IS_TTY "$@"

此方法在所有这些情况下都提供了预期的输出

make                  # Colored
make >&2              # Colored
make | cat            # NOT colored
make >tmp && cat tmp  # NOT colored

在配方中检测并递归调用 Makefile

在某些情况下,可能无法拦截调用以进行调用或将其替换为自定义脚本。在此解决方案中,我们通过在第一遍中检测 TTY,然后使用适当的变量集递归调用相同的 Makefile 来解决该限制。

ifndef IS_TTY
.SILENT:
%:
    @[ -t 1 ] && IS_TTY=1 || IS_TTY=0; $(MAKE) IS_TTY=$$IS_TTY "$@"
xyz:
    @[ -t 1 ] && IS_TTY=1 || IS_TTY=0; $(MAKE) IS_TTY=$$IS_TTY
else

ifeq ($(IS_TTY),1)
# DO NOT ADD TRAILING COMMENTS OR THIS WILL FAIL BECAUSE OF TRAILING SPACES
ESC := $(shell printf '\e')
R := $(ESC)[31m
Z := $(ESC)[0;0m
endif

$(info [info  ] $Rred$Z black)
$(shell echo >&2 "[shell ] $Rred$Z black")
recursive:
    @echo "[recipe] $Rred$Z black"

endif

这种方法的缺点是它在 Makefile 中添加了相当多的垃圾。处理递归可能很棘手。在上面的例子中,我只应用了一些基本的递归机制,在make有或没有目标的情况下应该可以工作,并且还应该传播变量。扩展这将是另一个问题的主题;-)

在每个回声处去除转义序列

我们在每个 echo 命令中测试输出是否是 TTY,如果不是,我们会去除转义序列(使用https://superuser.com/questions/380772/removing-ansi-color-codes-from-text-stream) . 为了减少在线上的垃圾邮件,我们使用了一个 make 变量$(STRIPESC)。解决方案非常简单,但它只适用于echo命令,并且在每次回显时都会产生一个额外的进程。它还需要使用回声编辑每个配方。

# DO NOT ADD TRAILING COMMENTS OR THIS WILL FAIL BECAUSE OF TRAILING SPACES
ESC := $(shell printf '\e')
R := $(ESC)[31m
Z := $(ESC)[0;0m
STRIPESC:=( [ -t 1 ] && cat || sed 's/\x1b\[[0-9;]*m//g' )

# $(info ...) not supported
# $(shell echo >&2 ...) not supported
if_tty:
  @echo "[recipe] $Rred$Z black" | $(STRIPESC)

使用外部 echo 命令去除转义序列

这类似于上面的解决方案,只是语法更轻量级。优点/缺点是相同的。此外,在下面的示例中,我在 makefile 本身中生成了外部回显。在标准构建中,您将在某些标准路径中将此命令作为外部工具提供。

# DO NOT ADD TRAILING COMMENTS OR THIS WILL FAIL BECAUSE OF TRAILING SPACES
ESC := $(shell printf '\e')
R := $(ESC)[31m
Z := $(ESC)[0;0m

$(shell echo "#! /bin/bash" > echotty)
$(shell echo '[ -t 1 ] && echo "$$@" || echo "$$@" | sed "s/\x1b\[[0-9;]*m//g"' >> echotty)
$(shell chmod a+x echotty)

# $(info ...)
# $(shell echo >&2 ...)
.PHONY: echotty
echotty:
  @./echotty "[recipe] $Rred$Z black"
于 2020-06-03T18:31:53.730 回答