为什么执行命令替换时不会显示 less?

为什么执行命令替换时不会显示 less?

cd我有一个交互式脚本,它返回一个在父进程中使用的路径。像这样:

$ cd $(myscript)

在脚本执行期间,我希望能够在less.不幸的是,在命令替换期间使用less似乎不起作用:

$ echo $(less <<< 'some text') # Not working
some text

所以我的问题是:为什么它不起作用?有解决办法吗?

我试图在脚本执行期间显示交互式帮助屏幕。我需要将该脚本放在命令替换中,因为这是我 cd 到父进程中生成的路径的唯一方法。可能是更好的方法,但这是迄今为止我最好的选择。

答案1

一般来说,echo $(somecmd)对于任何somecmd.的输出somecmd将扩展为 的参数echo,然后echo获取其参数并将其打印到其输出。通常,您可以直接运行somecmd

这两者之间的区别是常见的:分词和通配符。echo $(somecmd)将压缩空白并扩展任何看起来像文件名 glob ( *.txt) 的内容。不过,当仅运行somecmdecho "$(somecmd)"使用引号运行时,这些当然不会发生删除除最后一个换行符之外的所有内容。您的echo扩展反斜杠也有可能转义。

至于less,其目的是让交互的浏览文件或管道输入,这几乎需要终端。当您在命令替换中运行它时,less的输出会连接到管道,并且它的行为会恢复到与cat. (似乎,我在手册页中找不到有关确切行为的提及。)


现在,如果您确实需要一个在命令替换中运行的脚本,允许用户浏览一些输出,并仍然通过命令替换返回一些其他文本,您可以创造性地使用重定向(解释 mosvy 的评论):

$ cat myscript
#!/bin/bash
echo this is the output

cat >&2 <<EOF
you can
browse this
with less
EOF
$ foo=$( ./myscript 2> >(less > /dev/tty))

该脚本的标准输出转到命令替换,其错误输出转到less,因此foo设置为this is the output。我不确定这是否有任何用处,因为您将无法以foo交互方式修改值,至少不能仅使用less.

另外,这样的运行less会扰乱作业控制,因此在运行时按 Ctrl-C 会产生奇怪的行为等。


在这里,最好只使用临时文件。将myscript结果写入名为参数(即echo "$result" > "$1")的文件,然后运行:

tmp=$(mktemp)
myscript "$tmp"
cd "$(cat "$tmp")"
rm "$tmp"

现在myscript可以以正常方式启动less或另一个交互式程序,并且作业控制不应该出现问题。

答案2

(trap : INT; echo "$(less <<< 'some text' >/dev/tty; echo done)")

lesscat当它的标准输出不是 tty 时,就会变成一种(不)花哨的东西,就像在命令替换中使用时一样(其中它的标准输出是管道)[1]。

在交互式 bash 中进行测试时(默认情况下启用作业控制),(...)子 shell 将确保该less命令作为终端中正确前台作业的一部分运行,否则可能无法使用以下命令挂起它^Z,用 终止它^C,它会以其他方式出现错误行为 [2]。

trap : INT将导致子 shell 忽略 a ^C,而不将该配置传递给其子 shell。

如果该命令是 shell 脚本的一部分,则所有进程都将作为同一进程组/作业的一部分运行,但(...)对于限制陷阱的范围可能仍然有用INT

[1] 当它的 stdout 不是 tty 时,你甚至不能用它来打印很好地转义的二进制数据:

$ printf '\xee' | less -FXR
<EE>
$ printf '\xee' | less -FXR | cat
�$ 

[2] 可能发生的情况有很多种,但都是不好的。例如,在

$ ls -d $(less <<<'some text' >/dev/tty; pwd)

bash将分叉一个单独的进程来执行二进制文件,并将进程替换作为它的子进程ls运行$(...)将其移至单独的进程组/作业并执行二进制文件。

当您按下^Zinside时less,它​​将被正常处理less,但要么无法停止它(如果 shell 及其less工作是会话领导者),要么只会导致less,但不会导致其父进程停止。

当你按^Cinside时less,它​​会被 捕获并处理less,但 aSIGINT也会被发送到主 shell(这是终端上的前台作业),这将导致它返回下一个提示符,以及 bash 的 readline 并less竞争输入和更改终端属性彼此不知情,导致完全混乱。

笔记

所有这些都适用于任何程序从命令替换运行,而不仅仅是示例$(less ...),例如。

$ cat >foo <<'EOT'; chmod 755 foo
t=$(mktemp)
vi "$t" </dev/tty >/dev/tty 2>&1
echo "$t"
EOT
$ cat $(./foo)
# ^Z doesn't work in vi

相关内容