Bash:欺骗程序以为标准输出是交互式终端

9

我想通过管道运行一个程序的输出,但当它检测到stdout不是交互式shell时,它会表现得不同。

如何欺骗它以正常情况下的方式通过管道写入?


1
我听说 expect 是一个很好的程序来完成这个任务。 - BeniBela
还有一个用于此目的的script程序,请查看:https://dev59.com/dXM_5IYBdhLWcg3wZSE6#1402389 - Emmanuel Touzery
3个回答

18

我假设该程序会调用glibc函数isatty()来检查stdout是否是终端。对于使用终端上色输出或其他ANSI终端功能(如光标定位或行擦除/重绘)的程序,这是很常见的。

您可以使用LD_PRELOAD环境变量欺骗程序。LD_PRELOAD由ELF链接器处理,告诉它在所有其他库之前加载一个动态库。使用此功能,可以覆盖库函数,在您的情况下是glibc函数isatty()。您可以参考此文章中的示例。

我为您准备了一个示例:

首先创建文件libisatty.c:

/**
 * Overrides the glibc function. Will always return true.
 *
 * Note: Although this should be ok for most applications it can
 * lead to unwanted side effects. It depends on the question
 * why the programm calls isatty()
 */
int isatty(int param) {
    return 1;
}

然后将其编译为共享库:

gcc -shared -o libisatty.so  libisatty.c

它应该能成功构建。

现在是测试库的时候啦。 :) 我使用了命令ls --color=auto进行测试。ls调用isatty()来决定是否应将其输出着色。如果输出被重定向到文件或管道,则不会添加颜色。您可以使用以下命令轻松测试此功能:

ls --color=auto        # should give you colorized output
ls --color=auto | cat  # will give you monochrome output
现在我们将再次尝试使用 LD_PRELOAD 环境变量执行第二个命令:
LD_PRELOAD=./libisatty.so ls --color=auto | cat

您应该看到彩色输出。

顺便说一句,用户名很酷:uʍop ǝpısdn !!:D


警告:创建一个使用“LD_PRELOAD”技巧的shell函数并不完全可行,因为显然在shell函数中有其他可观察到的差异(我不确定它们是什么)。然而,通过创建一个别名“trick_tty”,并使用“alias trick_tty =“ LD_PRELOAD = <full path to> / libisatty.so“” ,然后执行“trick_tty <cmd> | less”可行。 - ntc2
1
对于MacOS用户,您可以执行相同的操作,但是不需要设置LD_PRELOAD,而是设置DYLD_INSERT_LIBRARIES和DYLD_FORCE_FLAT_NAMESPACE,像这样:DYLD_INSERT_LIBRARIES=./libisatty.so DYLD_FORCE_FLAT_NAMESPACE=y ls -G | cat(请注意,--color标志在Mac的ls上不起作用) - Christopher Shroba

2

使用script对我有效:

script outputfile.txt yourcommand
# any output ends up in outputfile.txt

我猜你可以用这个来进行管道操作:

script out.txt yourcommand ; cat out.txt | nextcommand

жЈҖжҹҘscriptзҡ„жәҗд»Јз Ғеә”иҜҘдјҡжҸӯзӨәеҮәзЁӢеәҸз”ЁжқҘеҲӨж–ӯе®ғ们жҳҜеҗҰеңЁдәӨдә’ејҸиҝҗиЎҢж—¶жүҖдҪҝз”Ёзҡ„йҷӨдәҶisattyд№ӢеӨ–зҡ„е…¶д»–дёңиҘҝгҖӮ - ntc2

0
你可以尝试这个:
./script.sh < `tty` > output

如果程序正在执行类似于isatty(0)的操作,这可能已经足够了。

3
请注意,所涉及的程序将在标准输出上运行isatty()函数。您将终端输入通过管道传输到标准输入中。这样做不会解决问题。 - hek2mgl
它可能实际上正在测试stdin而不是stdout。 - Arnaud Le Blanc
大多数在coreutils或utils-linux中的程序都是这样做的。例如:rm,mv,ls,nohup,cfdisk,more,pg等。 - Arnaud Le Blanc
请尝试运行命令 ls --color=auto < \tty` | cat`,你能看到颜色吗?我看不到。(我假设你已经执行了 dircolors 命令) - hek2mgl
注意,我必须离开一会儿。稍后再来看看。我认为这个问题很有趣。虽然我相信如果程序在标准输出上调用isatty,那么它并不容易。再见! :) - hek2mgl
我现在不在我的工作站上,但稍后会尝试一下。_A priori_,我倾向于像@hek2mgI那样思考,但谁知道呢。 - salezica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接