英文:
Proxy exec.Cmd Stdout / Stderr without losing TTY
问题
我有以下代码,执行任意的 shell 命令并将 stdout
和 stderr
导向终端:
c := exec.Command("/bin/sh", "-c", cmd)
c.Stdin = os.Stdin
c.Stdout = os.Stdout
c.Stderr = os.Stderr
然而,我需要在打印之前处理输出,所以我用一个代理的 io.Writer 接口进行了封装:
type ProxyWriter struct {
file *os.File
}
func NewProxyWriter(file *os.File) *ProxyWriter {
return &ProxyWriter{
file: file,
}
}
func (w *ProxyWriter) Write(p []byte) (int, error) {
// ... 先对字节进行处理
fmt.Fprintf(w.file, "%s", string(p))
return len(p), nil
}
所以现在的原始代码变成了:
c := exec.Command("/bin/sh", "-c", cmd)
c.Stdin = os.Stdin
c.Stdout = NewProxyWriter(os.Stdout)
c.Stderr = NewProxyWriter(os.Stderr)
这个方法大部分情况下都有效,但是 stdout
和 stderr
不再被视为 TTY。之前的样式或颜色输出不再生效。
我确认了这不是 ProxyWriter
导致格式混乱的问题,通过将命令设置为以下内容进行输出,可以正确显示带颜色的文本:
c := exec.Command("echo", "3[0;31mTEST3[0m")
一个更明确的测试是:
c := exec.Command("/bin/sh", "-c", "if [ -t 1 ] ; then echo \"terminal\"; else echo \"not a terminal\"; fi")
输出结果为:
not a terminal
有没有办法在不失去 TTY 状态的情况下封装命令的 stdout / stderr?
英文:
I've got the following code that executes an arbitrary shell command and pipes the stdout
and stderr
to the terminal.:
c := exec.Command("/bin/sh", "-c", cmd)
c.Stdin = os.Stdin
c.Stdout = os.Stdout
c.Stderr = os.Stderr
However, I need to process the output before I print it, so I've wrapped it with a proxy io.Writer interface:
type ProxyWriter struct {
file *os.File
}
func NewProxyWriter(file *os.File) *ProxyWriter {
return &ProxyWriter{
file: file,
}
}
func (w *ProxyWriter) Write(p []byte) (int, error) {
// ... do something with bytes first
fmt.Fprintf(w.file, "%s", string(p))
return len(p), nil
}
So the original code is now:
c := exec.Command("/bin/sh", "-c", cmd)
c.Stdin = os.Stdin
c.Stdout = NewProxyWriter(os.Stdout)
c.Stderr = NewProxyWriter(os.Stderr)
This works for the most part, however, stdout
and stderr
no longer seem to qualify as a TTY. Any previously styled or colored output is no longer styled or colored.
I've confirmed that this is not an issue of my ProxyWriter
simply messing with the formatting by setting the command to the following, which outputs the colored text correctly.
c := exec.Command("echo", "3[0;31mTEST3[0m")
A more explicit test is:
c := exec.Command("/bin/sh", "-c", "if [ -t 1 ] ; then echo \"terminal\"; else echo \"not a terminal\"; fi")
Which outputs:
not a terminal
Is there anyway I can wrap the commands stdout / stderr without losing the TTY status?
答案1
得分: 1
将以下代码段翻译为中文:
func (w *ProxyWriter) Write(p []byte) (int, error) {
// ... do something with bytes first
fmt.Fprintf(w.file, "%s", string(p))
return len(p), nil
}
翻译为:
func (w *ProxyWriter) Write(p []byte) (int, error) {
// ... 首先对字节进行一些操作
return w.Write(p)
}
fmt.Fprintf
有一些逻辑来避免终端断行。
英文:
Replace
func (w *ProxyWriter) Write(p []byte) (int, error) {
// ... do something with bytes first
fmt.Fprintf(w.file, "%s", string(p))
return len(p), nil
}
To
func (w *ProxyWriter) Write(p []byte) (int, error) {
return w.Write(p)
}
fmt.Fprintf
have some logic to avoid terminal breaking.
答案2
得分: 0
你需要一个完整的 tty 实现,以使人工的标准输出符合终端的要求。mattn 建议的库似乎是一个不错的选择。
然而,根据你尝试保留颜色的命令,可能还有另一个简单得多的选项。大多数带有颜色输出的 GNU 实用程序,如 ls
、grep
和 dmesg
,都有一个 --color
标志,可以覆盖 TTY 检测。
例如:
$ ls --color=always
$ ls -lsh | grep --color=always drwx
我经常使用这种方法将带有颜色的 dmesg 输出通过 less 进行分页:
$ sudo dmesg --color=always | less -R
你也可以相反地禁用颜色输出:
ls --color=never
英文:
You will need a full tty implementation to make an artificial stdout qualify as a terminal. The library that mattn suggested seems like a good candidate.
However, another much simpler option may be available depending on the command you are trying to keep colorization for. Most GNU utilities with color output such as ls
, grep
, and dmesg
have a --color
flag which can override the TTY detection.
For example:
$ ls --color=always
$ ls -lsh | grep --color=always drwx
I use this frequently to pipe colored dmesg output through less so that I can paginate it:
$ sudo dmesg --color=always | less -R
You can also do the opposite and disable color output:
ls --color=never
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论