gnu make - 确定stdout是否为终端

时间:2013-01-31 12:21:27

标签: linux bash shell makefile

尝试做:

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

其中red_text定义为

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

打印“你必须清理”,其中“clean”字样用红色打印。

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

我需要更新red_text来处理此案件。为此,我认为我可以使用类似$(shell [ -t 1 ] ..)的内容,但问题是$(shell) stdout 永远不会是终端。

如果 stdout 不是终端,我如何更改red来处理这种情况?

3 个答案:

答案 0 :(得分:1)

本着的精神,当没有什么可以补充的时候达到完美,但是当没有什么可以带走的时候(Antoine de Saint Exupery),通过来解决问题首先使用颜色!真的。 糟透了有固有的问题。你可以假设一些糟糕的用户会在某些好日子里对终端造成错误。您遇到此交互式终端问题。你花时间解决一个问题,可以更好地编写酷炫功能而不是 eye candy 。你喜欢错误的一群人,即色情上瘾,而忽视了色彩挑战的人,比如红色/绿色盲人用户(其中​​有超过你和我的估计)。

答案 1 :(得分:0)

if test -t 1
then
    echo terminal
else
    echo file
fi

答案 2 :(得分:0)

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

在自定义make脚本中检测TTY

我们只需要编写一个简单的shell脚本即可拦截对本机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"