gnu make - 确定stdout是否为终端。

问题描述 投票:3回答:3

试图做到。

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

其中red_text被定义为

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

这将打印 "you must clean",其中 "clean "一词被打印成红色。

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

我需要更新 red_text 来处理这个案子。为此,我想我可以使用类似 $(shell [ -t 1 ] ..) 但问题是 stdout$(shell) 绝不是终端。

我如何才能改变 red 办理时 stdout 是不对终端?

linux bash shell makefile
3个回答
1
投票

的精神,在这里打印出 "你必须清洁",其中字"..."。完美不是在没有任何东西可以增加的时候达到的,而是在没有任何东西可以拿走的时候达到的(安托万-德-圣埃克苏佩里)。,解题方法是 不在第一时间使用颜色! 真的吗?它 有固有的问题。你对终端做了一些假设,而这些假设对于某些可怜的用户来说,在某一天会是错误的。你遇到了这个交互式终端的问题。你把时间花在解决一个问题上,而这个问题本可以更好地花在编程酷功能上,而不是花在 养眼. 你取悦了错误的人群,即色度上瘾者,而忽视了色度上有问题的人,如红绿盲用户(其中有比你我估计更多的人)。


0
投票
if test -t 1
then
    echo terminal
else
    echo file
fi

0
投票

正如你所指出的,没有办法从以下方面进行测试 $(shell ...) 无论输出的是标准终端还是管道文件。然而,我们可以做一些事情,每件事情都有自己的proscons。

在自定义中检测TTY make 脚本

我们只需写一个简单的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。

在某些情况下,可能无法拦截调用make或用自定义脚本替换。在这个解决方案中,我们通过在第一次传递中检测到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的转义序列

我们在每条echo命令中测试输出是否是TTY,如果不是,我们将转义序列剥离(使用 https:/superuser.comquestions380772removing-ansi-color-code-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命令来删除转义序列。

这与上面的解决方案类似,只是语法更轻巧一些。proscons是一样的。另外,在下面的例子中,我在makefile中自己生成了外部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

$(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"
© www.soinside.com 2019 - 2024. All rights reserved.