将脚本的标准输出重定向为 tmux 会话中运行的另一个脚本/命令的标准输入

将脚本的标准输出重定向为 tmux 会话中运行的另一个脚本/命令的标准输入

我正在适应马库斯·穆勒的回答 我上周问的一个问题 - 一个脚本,它将其标准输出重定向到 tmux 会话,以渲染 ANSI 转义序列,然后捕获窗格渲染作为脚本的真实输出。我知道它根本没有用,因为您可以将其直接打印到标准输出并获得相同的结果,但这只是一个演示,可以将代码带到一个更大的项目,更复杂的解释,我需要这个功能:

#!/bin/zsh

tmpdir="$(mktemp -d)"
fifo="${tmpdir}/fifo"
mkfifo "$fifo"
tmux new-session -d -s aux "while true; do cat ${fifo}; done"

exec 3>&1 1>"$fifo"


echo foo
echo bar
tput home
echo -n b

exec 1>&3 3>&-

tmux capture-pane -t "aux" -p -S0 -E1
tmux kill-session -t aux
rm -rf $tmpdir

其输出(并且必须):

boo
bar

我对简化代码感兴趣。是否可以使用任何与 stdin 重定向相关的技巧,而不是需要维护的 fifo?我可以使用单行程序以某种方式继续打印所有内容并在完成后自动关闭会话吗?

我尝试使用 tmux 管道窗格、缓冲区、发送键和 run-shell,但无法成功。特别是当会话采用标准输入时,就像您在控制台上编写命令一样,而不是像正在运行的命令/脚本的脚本的标准输入一样

我觉得它必须以某种方式简化。

答案1

是否可以使用任何与标准输入重定向相关的技巧,而不是需要维护的 fifo?

询问分配给新会话的唯一窗口的唯一窗格的tmux内容。tty然后打印到它。

#!/bin/zsh

tmux new-session -d -s aux 'tail -f /dev/null' || exit 1
tty="$(tmux display-message -p -t aux -F '#{pane_tty}')"

{
  echo foo
  echo bar
  TERM=tmux tput home
  echo -n b
} > "$tty"

tmux capture-pane -t aux -p -S0 -E1
tmux kill-session -t aux

tmux display-message -p一般来说,从中获取信息非常有用。在您的情况下,您可以直接从 ; 获取有关 tty 的信息tmux new-session;并且您不需要该tty变量。您无需执行任何预备步骤即可设置重定向,如下所示:

{...} >"$(tmux new-session -d -s aux -P -F '#{pane_tty}' 'tail -f /dev/null')" || exit

Notetail -f /dev/null只是一个保持 tmux 窗格“活动”的命令。该脚本不会尝试向该命令发送任何内容,而是打印到正确的 tty。这个答案没有解决标题(“将脚本的标准输出重定向为 tmux 会话中运行的另一个脚本/命令的标准输入”),但它为您提供了您想要的功能。

答案2

我不清楚你到底在问什么。您是否接受使用 tmux 处理转义序列的概念,但只想消除 FIFO?

尝试只产生你想要的输出在tmux中:

tmux new-session -d -s aux "echo foo; echo bar; tput home; echo -n b"

tmux capture-pane -t aux -p -S0 -E1
tmux kill-session -t aux

并忽略 FIFO 的内容。

PS 您应该引用 shell 变量,例如$fifo和 $tmpdir。您实际上不需要引用由非空白字符组成的常量字符串,例如"aux".

答案3

我认为你不能做类似的事情,因为tmux会关闭它收到的所有高于 2 的 fd(以及分离时的 0、1、2)。

请参阅(或等效)输出或源中的调用close_range(3, 4294967295, 0)中的等效项。straceclosefrom(STDERR_FILENO + 1)

因此,您需要以其他方式传递数据,或者通过像您一样的命名管道或环境变量(但不能包含 NUL 字符)或嵌入由tmux套接字、临时文件、共享内存运行的 shell 代码中,其中没有一个真的会更简单或更可靠。

但您的方法存在许多问题:

  • 您使用的是固定会话名称,这意味着脚本无法可靠地使用,除非您可以保证不会同时运行该脚本的两个调用。您可以tmux选择会话名称并通过选项检索-Pnew-session
  • 你不做任何同步:当你运行时capture-pane,不能保证它cat会完成甚至开始。当您检索窗格的内容时,您正在执行循环+终止,而不是告诉 tmux 会话退出。
  • $fifo您将在新会话中运行的 shell 代码中嵌入内容。最好作为环境变量传递。
  • 您没有检查mktemp或 的退出状态mkfifo
  • tmux 将读取用户的数据~/.tmux.conf,这可能会干扰处理
  • 您需要传递TERM=tmux到环境tput(或 zsh 的内置echoti等效项),因为这是一个tmux最终解释的终端仿真器,而不是您运行脚本的主机终端,因此您需要向其发送转义序列而不是主机终端会理解。
  • 使用-S0 -E1,您仅捕获窗格的前 2 行可见行
  • 您可以告诉 tmux 创建一个与主机终端窗口大小相同的窗格
  • 您可能还想获取回滚缓冲区的内容,以防输出不适合一个屏幕。

所以,也许是这样的:

#! /bin/zsh -

tmpdir=$(mktemp -d) || exit
trap 'rm -rf -- $tmpdir' EXIT INT TERM HUP QUIT

in=$tmpdir/in out=$tmpdir/out
mkfifo -- $in $out || exit
session=$(
  IN=$in OUT=$out tmux -f /dev/null new-session -PEd -x ${COLUMNS:-80} -y ${LINES:-24} '
    cat -- "$IN"
    echo done > "$OUT"
    read may_I_exit < "$IN"
    '
) || exit

cat "$@" > $in || exit

read can_I_retrieve_the_output < $out || exit
tmux capture-pane -t $session -pS-
echo you may exit > $in

那么例如:

$ (TERM=tmux; print -rln foo bar$terminfo[home]b) | ./capture | hexdump -C
00000000  62 6f 6f 0a 62 61 72 0a  0a 0a 0a 0a 0a 0a 0a 0a  |boo.bar.........|
00000010  0a 0a 0a 0a 0a 0a 0a 0a  0a 0a 0a 0a 0a 0a 0a 0a  |................|
*
00000040  0a 0a                                             |..|
00000042

(注意所有 0x0a (LF) 字节,因为我的终端窗口有 60 行高)。

(免责声明:我自己不是 tmux 用户,所以很可能有更聪明或更正确的方法来做到这一点)


1 尽管就该功能而言home,它在实践中不太可能产生影响,因为您不太可能使用转义序列与自己home的仿真 ( ) 不同的终端。tmux\e[H

相关内容