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
) 的内容。不过,当仅运行somecmd
或echo "$(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)")
less
cat
当它的标准输出不是 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
运行$(...)
前将其移至单独的进程组/作业并执行二进制文件。
当您按下^Z
inside时less
,它将被正常处理less
,但要么无法停止它(如果 shell 及其less
工作是会话领导者),要么只会导致less
,但不会导致其父进程停止。
当你按^C
inside时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