我知道 zsh 有一个 preexec 钩子,它在命令执行之前触发,但是是否有一个钩子在命令启动之后但在输出显示到屏幕上之前触发?我问这个问题是因为我想根据是否有/将有实际输出来显示一些内容。
例如:
cd dir
不输出任何
ls dir
内容输出东西
在该示例中,我想Output:\n
在 ls 目录显示之前显示:,但不显示何时cd
运行,因为它没有显示。我希望它看起来像:
~ $: cd dir
~/dir $: ls .
Output:
total 464
drwxr-xr-x+ 55 eddie staff 1.8K May 2 11:07 .
drwxr-xr-x 6 root admin 204B Apr 22 13:48 ..
~/dir $
我不只是想包装 cd 和 ls。这些都是例子。我希望它是为了每一个命令。
答案1
不可能有任何预显示钩子,因为当命令将某些内容输出到终端或任何其他文件时,根本不涉及 shell。
该钩子必须位于由命令行启动的任何进程或线程对向 tty 设备打开的文件描述符进行第一次写入系统调用时运行的命令中。或者,您必须使用某种 IPC 机制重定向命令的 stdout 和 stderr(可能必须是伪 tty 对,以便最大限度地减少对命令行为的影响),并Output:
在收到某些内容时插入 。无论如何,这将是相当复杂且相当具有侵入性的。
现在,这不是我会做的事情,但你可以尝试这样的事情:
preexec() {
printf '%-*s' $COLUMNS "Output:"
read -sdR $'pos_before?\e[6n'
}
precmd() {
read -sdR $'pos_after?\e[6n'
[[ $pos_after != $pos_before ]] || printf '\r'
}
set +o promptsp
也就是说,在运行命令输出之前Output:
,将光标移动到屏幕的右边缘(因此,如果有的话,下一个写入的内容将位于下一行的开头),并将当前光标位置记录在$pos_before
.
在下一个提示之前,再次查询光标位置,如果它没有移动,则将光标移回行首,以便下一个提示覆盖该位置Output:
。我们禁用它promptsp
,因为它会干扰这一点。
¹这种基于 pty 的方法可以通过expect
以下方式实现:
#! /usr/bin/expect -f
set x 0
set timeout -1
stty raw -echo
log_user 0
spawn -noecho zsh
# tell zsh to send a special sequence before the prompt (precmd)
# and a different one before each command (preexec)
send {precmd() printf "\1\1"; preexec() printf "\2\2";set +o promptsp}
send "\r"
# wait for the second prompt
expect "\1\1"
# set our x flag when the preexec string has been output and reset
# it upon precmd. write "Output:" if some character is received while
# the flag is up.
expect {
-re "^\2\2" {
set x 1; exp_continue
}
-re "^\1\1" {
set x 0; exp_continue
}
-re "^.\[^\1\2\]*" {
if {$x} {send_user "Output:\n"; set x 0}
send_user -- "$expect_out(buffer)"
exp_continue
}
-i $user_spawn_id -re .+ {
send -- $expect_out(buffer); exp_continue
}
}