在 Bash 中钩取所有命令输出

3

我只是想把终端中的所有输出文本传输到espeak。例如,设置完成后,我应该能够键入echo hi并听到“hi”说出,或者键入ls并听到我的目录内容列出。

目前我找到的唯一有希望捕获输出的方法来自这里:http://www.linuxjournal.com/content/bash-redirections-using-exec

这是我目前的进展:

npipe=/tmp/$$.tmp
mknod $npipe p
tee /dev/tty <$npipe | espeak &
espeakpid=$!
exec 1>&-
exec 1>$npipe
trap "rm -f $npipe; kill $espeakpid" EXIT

它能够正常工作(同时打印一堆“完成”任务),但是创建命名管道、使用trap删除并使用tee打印输出似乎有点混乱。是否有更简单的方法?


1
只需使用 exec > >(tee bash.log) 即可轻松挂钩所有命令输出。但是,为了正确处理终止、IPC(而不会填充文件)等等,以上是一种正确的方式。 - clt60
@jm666 谢谢,有没有一种通过管道而不是文件进行重定向的方法? - jozxyqk
2个回答

3
这是一种方式:
exec > >(exec tee >(exec xargs -n 1 -d '\n' espeak -- &>/dev/null))

如果你想恢复到原始输出流,请按以下方法操作:
exec 3>&1  ## Store original stdout to fd 3.
exec 4> >(exec tee >(exec xargs -n 1 -d '\n' espeak -- &>/dev/null))  ## Open "espeak" as fd 4.
exec >&4  ## Redirect stdout to "espeak".
exec >&3  ## Redirect back to normal.
  • 我使用xargs -n 1,因为espeak只有在到达EOF时才会执行操作,因此我们每行召唤它的一个实例。这当然可以自定义,但这是你要找的答案。当然,while read循环也可以是此选项。
  • 我还使用exec的进程替换来确保我们摆脱不必要的子 shell。

你能解释一下这个代码的作用和原理吗?它是否会重定向所有终端输出? - Llamageddon
@Llamageddon 你需要对文件描述符有基本的了解。我建议你查阅一些维基或者教程来学习它。stdin(0),stdout(1)和stderr(2)默认链接到终端,但可以重定向到其他文件和流。Bash本身是一个进程,exec命令可用于操作Bash的打开文件描述符。由bash打开的子进程一次性继承Bash的文件描述符状态。还要阅读Bash手册并查找exec命令的文档。 - konsolebox
@Llamageddon,还要在Bash手册中查找“进程替换”。 - konsolebox
为什么不是一个好主意?因为有一些程序可以区分标准输出和其他描述符。 - Llamageddon
@Llamageddon我建议创建一个新的问题帖子,这样对实现该行为感兴趣并且知识渊博的人可以帮助你。我只是建议使用DEBUG和PROMPT_COMMAND,但我不知道它们是否真正有效。我也不感兴趣研究脚本以实现你所期望的功能...更不用说你要寻找的解决方案实际上是用于Zsh而不是Bash。 - konsolebox
显示剩余5条评论

0

看起来比那容易多了 - 我刚测试了一下,它可以工作:

$ echo "these are not the droids you are looking for" | espeak --stdin 

--stdin 标志是关键。来自 espeak 的手册页面:

   --stdin
          Read text input from stdin instead of a file

如果你想要听到非常长的输出,那么当你遇到“参数列表过长”的错误时,我猜你可以使用xargs


1
我已经很熟悉将输出导入到espeak中(就像我的示例一样)。但是,我遇到的问题是捕获bash shell中任何命令的输出,同时不干扰输出本身。例如 bash | tee /dev/tty | espeak,不幸的是这会创建一个新的shell,而我更愿意避免这种情况。 - jozxyqk

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