如何诱使命令认为其输出将发送到终端


38

给定一条命令,该命令在其输出将输出到终端时会更改其行为(例如,产生彩色输出),如何在保留更改的行为的同时在管道中重定向该输出?必须有一个我不知道的实用程序。

某些命令(如grep --color=always)具有选项标志来强制执行该行为,但问题是如何解决仅依赖于测试其输出文件描述符的程序。

如果有关系的话,我的外壳bash在Linux上。


您必须知道命令如何测试其输出文件描述符,并以某种方式返回与从输出终端看到的结果一致的结果。
安德鲁·亨利

Answers:


21

通过使用,您可能会得到所需的东西unbuffer

unbuffer是一个tcl/ expect脚本。如果需要,请查看源。另请注意man上的CAVEATS部分。

还要注意,它不执行别名,例如:

alias ls='ls --color=auto'

除非有人添加StéphaneChazelas指出的技巧:

如果执行alias unbuffer='unbuffer '(注意尾随空格),则别名将在后面展开unbuffer


注意已确认的别名-它也不能与其他任何shell构造(例如内置函数和函数)一起使用。
阿米尔2015年

4
如果执行alias unbuffer='unbuffer '(注意尾随空格),则别名将在后面展开unbuffer
斯特凡Chazelas

unbuffer它是!sudo apt install expect-不清楚。
loxaxs

22

工具集的历史

您不是第一个想要这种工具的人。人们一直想要这样的工具已有30年了。它们已经存在了将近这么长时间。

此类工具的最早工具是Daniel J. Bernstein的“ pty”软件包,Rich Salz将其描述为“ Ginsu刀”,Bernstein在1990年代初写信以便欺骗nethack(原文如此!)。“ pty”软件包的第4版发布于1992年comp.sources.unix(第25卷,第127至135期)。它仍然可以在万维网上找到。Paul Vixie当时对此进行了描述:

我能说什么 它切成薄片,切成小方块,洗碗,walk狗。它“行之有效”,这意味着,如果您按照说明进行操作,您将得到一个有效的包装,而无需拉扯头发或咬牙切齿或进行其他标准的移植活动。

伯恩斯坦后来在1999年4月7日或之前用“ ptyget”软件包对此进行了更新,他宣布:

我整理了一个新的伪tty分配器ptyget。Alpha版本位于ftp://koobera.math.uic.edu/pub/software/ptyget-0.50.tar.gz。有一个小巧的邮件列表。要加入,请向发送空消息 djb-ptyget-requ...@koobera.math.uic.edu。我从头开始设计ptyget的界面。它比pty更具模块化。现在,基本的pty界面分为三部分:

  • ptyget:一个微小的低级程序(程序包中唯一的setuid程序),该程序分配一个新的伪tty并将其传递给您选择的程序
  • ptyspawn:另一个小程序,在伪tty下运行子进程,等待其退出并等待停止
  • ptyio:另一个只是稍大一点的程序,它来回移动数据

pty现在已经拼出了旧的Ginsu刀ptybandage,这是的同义词ptyget ptyio -t ptyspawn; pty -d,用于将网络程序附加到伪tty的拼写是ptyrun,它是ptyget ptyio ptyspawn; 的同义词。并且nobuf是的同义词 ptyget ptyio -r ptyspawn -23x。我将会话管理功能拆分为一个单独的程序包。

那个单独的软件包就是“ sess”软件包。

顺便提一句,“ ptyget”是Berstein自己从未发布的“ redo”构建系统的早期版本,并且是该版本的少数已发布实例之一。 dependon是明显的前兆redo-ifchange

用法

ptybandage

ptybandage人们通常在登录会话中想要的是。它的主要用例是使程序对它们的标准输入,输出或错误是否连接到终端很敏感,即使它们实际上位于外壳管道中,还是将其标准文件描述符重定向到文件。

它需要一个命令来运行(当然,它必须是一个适当的外部命令),并以某种方式运行它,使其认为其标准输入,输出和错误已附加到终端,并将这些终端连接到ptybandage的原始标准输入,输出和错误。

它处理在作业控制外壳下运行的细微差别,确保终端STOP字符不仅停止,ptybandage而且还停止连接到内部终端的程序的运行。

ptyrun

ptyrun人们通常在TCP网络服务器中想要的。它的主要用例是远程执行环境,它们本身没有设置终端,运行的程序在没有终端的情况下无法按需运行。

它不希望在作业控制外壳下运行,并且如果正在运行的命令收到停止信号,则只需重启即可。

可用工具集

Dru Nelson同时发布了“ pty”第4版和“ ptyget”。

Paul Jarc发布了ptyget的固定版本,该版本尝试以操作系统实际上不再提供的原始版本处理特定于操作系统的伪终端设备ioctl。

nosh源软件包随附有workalike ptybandangeptyrun脚本,它们使用Laurent Bercot的execline工具和nosh软件包自己的伪终端管理命令。从nosh版本1.23开始,这些内容已预先包装在nosh-terminal-extras软件包中。(早期版本仅将其提供给从源代码构建的人。)

一些示例用途

Jurjgen Oskam ptybandage在AIX使用将此处文档中的输入提供给显式打开的程序,并读取其控制终端以输入密码提示:

$ ptybandage dsmadmc << EOF> uit.txt
约斯卡姆
密码
查询会话
查询过程
退出
紧急行动

安迪·布拉德福德(Andy Bradford)使用ptyrun daemontools和ucspi-tcp下的OpenBSD,使bgplgsh交互式路由器控制程序可以通过网络访问,同时使其认为自己正在与终端通信:

#!/ bin / sh
执行2>&1
exec envuidgid rviews tcpserver -vDRHl0 0 23 ptyrun / usr / bin / bgplgsh

进一步阅读


感谢您的历史教训,但是您建议的工具在现代Linux发行版中不可用,因此没有用。
阿米尔(Amir)

4
有很多超链接,并且答案的整个部分都专门介绍最肯定可以使用这些工具的位置
JdeBP

1
您对此有何看法expect
CMCDragonkai

20

您可以使用socat在连接pty的情况下启动您的进程,并获取socat将pty的另一端连接到文件。您所问的是哪个AFAIU:

socat EXEC:"my-command",pty GOPEN:mylog.log

此方法将导致isattyby my-command的返回,true并且仅依赖于此的过程将被愚弄以输出控制代码。请注意,某些进程(尤其是grep)还会检查TERM环境变量的值,因此您可能需要将其设置为合理的值,例如"xterm"


13

还有贴了很好的解决方案在这里超级用户通过KarlC

编译一个小的共享库:

echo "int isatty(int fd) { return 1; }" | gcc -O2 -fpic -shared -ldl -o isatty.so -xc -

然后告诉您的命令isatty(3)动态加载此覆盖:

LD_PRELOAD=./isatty.so mycommand

这可能不适用于那里的每个命令,甚至可能以意外的方式破坏某些命令,但在大多数情况下可能会起作用。


2
对于MacOS用户,您可以使用DYLD_INSERT_LIBRARIES=./isatty.so DYLD_FORCE_FLAT_NAMESPACE=y mycommand
Christopher Shroba,2017年

12

使用script(1)怎么样?

例如:

script -q -c 'ls -G' out_file

ls输出保存到out_file保留的颜色代码。


这在这里不起作用。颜色如何保存?我是否应该使用一种工具来输出out_file其颜色?
基拉2015年

1
我使用@Kira查看带有ANSI颜色转义序列的文件less -R。不过,在这种情况下,我希望输出继续在管道中进行,最终最终在我的终端中结束。使用cat进行说明,它是像script -q -c 'ls -G' /dev/null | cat,这抑制了typescript文件完全,只剩下程序的输出。
阿米尔2015年

为了避免创建文件,只需使用破折号(-)作为script输出文件,例如:script -q -c 'ls -G' -
Franklin Piat

0

基于@Amir的答案,下面是一个脚本,该脚本在运行时生成并包含该库:

#!/bin/bash
set -euo pipefail

function clean_up {
  trap - EXIT # Restore default handler to avoid recursion
  [[ -e "${isatty_so:-}" ]] && rm "$isatty_so"
}
# shellcheck disable=2154 ## err is referenced but not assigned
trap 'err=$?; clean_up; exit $err' EXIT HUP INT TERM

isatty_so=$(mktemp --tmpdir "$(basename "$0")".XXXXX.isatty.so)
echo "int isatty(int fd) { return 1; }" \
  | gcc -O2 -fpic -shared -ldl -o "$isatty_so" -xc -
# Allow user to SH=/bin/zsh faketty mycommand
"${SH:-$SHELL}" -c 'eval $@' - LD_PRELOAD="$isatty_so" "$@"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.