繁体   English   中英

如何在linux中检测命令是否是交互式的

[英]How to detect if a command is interactive or not in linux

我想保存特定子流程的输出以供以后使用。 为此,我使用 tee 将输出显示到 stdout 和日志文件。 但是,当涉及到诸如 ncdu 或 htop 之类的交互式命令时,它当然无法将其正确写入文件。 因此,我希望能够在运行命令之前(我认为这是不可能的)或在读取此类命令的混乱日志文件时知道命令是否是交互式的。 我会假设有一些东西是交互式程序写入标准输出的,而普通命令不会,这将使我能够区分两者。

“混乱的输出”是与终端控制序列混合的输出,最常见的是ANSI 转义码

对于依赖 Curses 进行终端输出的二进制文件,在未设置 TERM 环境变量或设置为空字符串的情况下运行命令,会导致命令失败。 例如,

$ env TERM= htop
Error opening terminal: unknown.

这是因为这些标准库会检查 TERM 环境变量,以在终端数据库中找到相应的条目。 tput实用程序也使用该数据库,因此您可以使用tput clear清除终端,使用tput reset reset 将终端tput reset为默认状态(如果终端模式出现乱码,则很有用),等等。)

要确定command是否使用 ncurses/curses 或终端信息数据库( terminfo支持,您可以运行例如ldd command | grep -qe 'libn*curses' -e 'libtinfo' && echo Yes || echo No 。但是,这do 只说明这些命令是否正确支持不同的终端,而不是它们是否需要终端才能工作。

然后还有脚本和工具,比如ls --color ,它们不使用curses 或terminfo 支持,而是直接发出ANSI 控制序列。 (在ls的情况下,使用在LS_COLORS环境变量中定义的序列,但仅当输出到终端时。正如 mmeisson 在对原始问题的评论中所提到的,使用例如isatty(STDOUT_FILENO ) .)

htop的情况下,我们甚至不能使用哑终端(在运行命令之前将 TERM 设置为哑终端,即env TERM=dumb htop </dev/null &>output ),因为 htop 将任务列表呈现为所有空格! 很烦人。 至少在top ( env TERM=dump top </dev/null &>output ) 中,可以得到不错的、朴素的 ASCII 输出。

香草终端可能就足够了。 在执行子进程或命令之前,设置TERM=vanillaCOLUMNS=80 ,例如env TERM=vanilla COLUMNS=80 htop </dev/null &>output 但是,尽管您现在从htop获得更多输出,但仍然缺少换行符(因为 htop 仅使用光标移动,而 vanilla 终端没有这些)。

可能还有另一个终端适合您,例如,生成的所有转义序列都可以轻松检测和过滤掉,但它足够复杂,例如 htop 输出是完整的。 不过我一个都不认识。 您可以创建一个,并将其添加到 terminfo 数据库之一(例如/etc/terminfo/e/easy )。 (您可以使用ls -1 {/etc,/lib,/usr/share}/terminfo/?/* | sed -e 's|^.*/||g' | sort列出您机器上的所有 terminfo 文件。我的有 2700 多个。)

对于这类问题,有一个独特的、“适当的”解决方案,但这并不容易。

不是仅使用连接到子进程的管道,而是通过posix_openpt()grantpt()unlockpt()ptsname()使用适当的伪终端接口pty ,特别是 UNIX98 伪终端 然而,主端——你的进程——必须表现得像一个真正的终端,并处理它所支持的所有控制序列(通过将 TERM 环境变量设置为终端类型)。

有一些终端模拟器库和代码可以重用于您自己的项目; 我立即想到的是VTE (它使用 GTK+ GUI 小部件作为终端显示)和xterm本身; xterm 源代码中的ctlseqs.txt是 xterm 变体中使用的实际序列的绝佳列表。 另一个有用的项目是GNU screen

本质上,您的程序然后成为命令使用的终端,可以为命令提供输入,并在终端显示上执行命令想要执行的所有更改。 您需要做的就是以某种方式记录这些更改。 (这通常称为抓取,但作为伪终端主控,您实际上只需要决定您希望如何存储终端内容:作为电影般的播放、某种尺寸的单个屏幕或其他方式。)

总的来说,我认为最好的选择是使用TERM=xterm或变体,或者TERM=ansi ,然后过滤或替换部分/大部分/所有转义序列以获得您想要的输出。 将“move cursor to”命令替换为换行符,如果列大于 1(最左边的列),则后跟适当数量的空格; 和带有一个或多个换行符的“清除屏幕”。 它并不完美,但它应该可以作为一个简单的状态机(逐个字符读取终端输出,并使用标准 <stdio.h> 函数发出过滤后的输出),并且对于大多数用途来说都足够好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM