这让 vim 疯狂:
$strace -o >(vim -; stty sane) file.out; stty sane
我已经输入了stty sane
已处理的替换以及下一个命令,但它们都没有完成其工作。一旦我将 strace 命令通过管道传输到 vim,那么 vim 就不再表现正常(我知道 vim 只能接受stdin
并且 strace 向 提供输出stderr
,但那么该-o
标志是做什么用的?)有什么帮助吗?
答案1
太长了;博士
strace -o '|vim -' file.out
关于-o
-o
存在是为了提供与 stdout 或 stderr 不同的附加输出通道。没有-o
strace
打印到其 stderr,这也是命令的 stderr(file.out
在您的情况下)。能够将两个流分开是件好事,-o
使这成为可能。
问题
你的方法有缺陷,与你想象的不同,stty sane
无济于事。让我重复一下命令:
strace -o >(vim -; stty sane) file.out; stty sane
它有两个普遍的问题:
- 启用作业控制后,就会出现一个概念前台进程组。在任何时候,终端都会识别出前台的一组。不在前台进程组中的进程正式处于后台。
后台进程无法从其控制终端读取数据;SIGTTIN
如果它尝试,它就会收到。该信号将阻止它,除非进程忽略它或以不同方式处理。
后台进程可以写入其控制终端,但如果它尝试并stty tostop
启用,则该进程将接收SIGTTOU
.该信号将阻止它,除非进程忽略它或以不同方式处理。如果不停止,尽管tostop
有信号,该进程仍能够写入终端。
上述机制可以防止后台进程窃取终端的输入,但仍然保持它们与终端的连接,因此可以将它们带到前台并进行操作。然后从终端读取。这对于启动进程的终端很有用。与其他终端可能的交互与处于前台还是后台无关,无论如何其他终端都是“外部”的;因此,只要进程具有足够的权限,就可以从不是其控制终端的终端读取或写入。
在您的情况下,最初前台进程组是与 shell 关联的进程组;然后用strace
;然后与stty
(外面的>()
);最后再次使用外壳。
在 Bash 内部的进程>()
被分配给处理>()
.这意味着只有在(外部的)完成并且shell 的进程组被置于前台vim
之后,您才能从终端读取数据。这就带来了第二个问题。strace
stty
>()
- 当
vim
最终能够从终端读取时,shell 也会尝试读取。比较这两个:
strace -o >(vim -) true
移动光标。一些输入将转到
vim
,一些输入将转到 shell。他们都在前台。strace -o >(vim -) true; sleep 1000
尝试移动光标。现在不同了,因为
sleep
它在前台。sleep
用Ctrl+终止C,行为将会改变。
在任何情况下,您都可以通过vim
从另一个控制台杀死来恢复(killall vim
除非有另一个控制台vim
并且您想保留它)。
这就是你观察到的疯狂。罪魁祸首超出了stty sane
、reset
或的范围tput reset
。
琐事
以下命令会产生不同的疯狂:
<<<"foo" tee >(vim -) <<<"foo" tee > >(vim -)
第一种情况>()
由主 shell 处理并vim
放置在 shell 的进程组中。一旦退出就可以从终端读取数据tee
(尽管 shell 会干扰);我们之前已经见过这个了strace
。
第二种情况>()
由处理 的 shell 处理>
。它是一个将成为 (exec to) 的子 shell tee
。实际上vim
被放置在一个进程组中tee
。只要tee
停留在前台,它就可以从终端读取数据。
因为tee
几乎立即退出,所以第一个vim
几乎立即能够从终端读取,而第二个vim
几乎立即无法读取。
- 我的测试表明,
>(vim -)
Zsh 位于vim
另一个进程组中,而不是处理>()
.那么vim
永远不能在前台。似乎还有其他差异,但我不会详细说明。
通用解决方案(不依赖于strace
)
如果禁用作业控制(使用
set +m
或通过在子 shell 中运行代码),则所有新进程都将属于终端视为前台的组。如果您延迟返回 shell,则 shell 不会通过从终端读取数据来干扰。尝试这个:(strace -o >(vim -) file.out; sleep 99999)
退出后使用Ctrl+C终止。如果您点击此组合,则不会受到影响,因为像 一样配置终端。sleep
vim
vim
sleep
vim
stty -isig
这个解决方案并不是很优雅。我将其放在这里是因为它显示了禁用作业控制和不允许 shell 读取输入会如何影响事物。
- 或者,您需要启动并保持
vim
在前台。不使用>(vim -)
。使用vim …
或… | vim -
.
最直接的方法是创建一个常规文件(在您的情况下strace -o somefile file.out
)并稍后打开它(vim somefile
)。
解决方案具体针对strace
避免创建文件并使用vim <(strace …)
or 的解决方案strace … | vim -
是可能的。我想如果您足够聪明(并且有/proc
?),那么您可以操纵文件描述符并通过管道传输-o
到标准输出,而无需与原始标准输出合并。
正确的事情然而是使用strace
提供的-o
:
-o filename --output=filename
将跟踪输出写入文件
filename
而不是 stderr。 […]如果参数以|
or开头!
,则参数的其余部分将被视为命令,并且所有输出都通过管道传输到它。这可以方便地将调试输出传送到程序,而不影响已执行程序的重定向。 […]
(来源,强调我的)
你的命令变成:
strace -o '|vim -' file.out
注意|
必须加引号或转义,否则 shell 将尝试构建管道。另一种选择是!
,在某些 shell 中需要受到保护以及。
strace
跑sh
,sh
跑vim
。上述问题就解决了。分别:
这三个进程属于同一个进程组。如果
strace
位于前台,则 也位于前台vim
。交互式(外部)shell 仅在退出后尝试从终端
strace
读取。sh
vim