Fish 与其他 shell(sh、bash、zsh)中的命令替换处理

Fish 与其他 shell(sh、bash、zsh)中的命令替换处理

该程序mc午夜指挥官)并不打算用于命令替换,但我想知道为什么当这个程序(通常看起来像其他curses程序)用作命令替换目标时,不同的shell表现不同。

在里面鱼壳,发出以下命令将打印字符串“无法获取终端设置:设备的 ioctl 不适当 (25)\n\n”。

echo (mc) # fish command substitution syntax

另一个值得注意的有趣的事情是,此命令返回后,您可以发出命令jobs来查看mc进程是否已挂起。

sh、bash 和 zsh 中的等效命令会挂起 shell,我所说的挂起是指我无法使用 Cz/Cc 终止或挂起该命令。我可以从命令中恢复的唯一方法是终止我的 shell 进程并重新启动它(从不同的 shell)。

echo $(mc) # hangs in sh, bash, zsh

鱼壳为什么​​不挂?它如何以不同的方式处理诅咒或命令替换等以产生这种行为?我觉得这很重要,因为它可能是其他贝壳所拥有的理想属性;任何命令都不应该意外地导致 shell 完全无法运行。

编辑:回答这个问题的另一种方法是说明为什么以下程序在 Fish shell 中用作命令替换目标时会被挂起。与 一样mc,它使用curses,但它被修改为在检测到stdin 不是tty 时重新打开stdin。这使得诅咒模式可以在除鱼之外的所有贝壳中使用。

#define _XOPEN_SOURCE
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    // start curses mode
    SCREEN* s = NULL;
    FILE* out = stdout;
    if(!isatty(fileno(stdout))) {
        out = fopen("/dev/tty", "w");
        // Should really test `out` to make sure that worked.
        setbuf(out, NULL);
    }
    // Here, we don't worry about the case where stdin has been
    // redirected, but we could do something similar to out
    // for input, opening "/dev/tty" in mode "r" for in if necessary.
    s = newterm(NULL, out, stdin);

    printw("test");
    getch();

    endwin(); // end curses mode
    delscreen(s);
    puts("/home/matt");
    return 0;
}

请参阅此问题以获取更多信息:https://stackoverflow.com/questions/17450014/ncurses-program-not-working-rightly-when-used-for-command-substitution。这段代码是我在 Stackoverflow 上发布的原始问题的解决方案。该解决方案适用于除鱼之外的所有贝壳。

编辑:尝试通过在 gdb 中启动 Fish 来查找更多信息。使用以下 gdb 命令序列并得到以下输出:

(gdb) break main
Breakpoint 1 at 0x41fe70: file fish.cpp, line 417.
(gdb) run -c echo (mc)
Starting program: /home/matt/builds/fish-shell/fish -c echo (mc)
Cannot get terminal settings: Inappropriate ioctl for device (25)

调试器在此暂停,不再产生任何输出。这很奇怪,因为 main 中的断点从未命中。在执行命令替换之前,fish shell 需要解析其命令行参数,但我不确定线程​​如何影响在调试器中观察这一点。

答案1

好吧,我想我知道发生了什么事。

您在执行时在 zsh 和 bash 中观察到的行为echo $(mc)是由mc.

当您正常运行时,按下+mc时它不会做出反应,因为它会忽略.结束的方法是按 F10 并 Enter。CtrlcSIGINTmc

当您运行时,echo $(mc)输入会进入进程mc,因此难怪当您按Ctrl+时c什么也不会发生,因为mc忽略了SIGINT。但是当你运行echo $(mc)并按 F10 并 Enter 时,它就会做出反应。 (当我再次按 F10 并 Enter 时,它实际上退出了;它应该在第一次尝试时退出,但我不知道为什么它没有退出。)

从这个事实我推断出 Midnight Commander 正常运行,但它的输出被放入 shell 缓冲区中,因此稍后可以被echo.我们应该认为正常的事情。

另外你认为 bash 和 zsh 挂了,但我想说它们不是挂着而是在等待echo命令返回。但echo只能在mc返回时返回,只有按下 F10 和 Enter 时才会返回。这样我们还可以解释当您按Ctrl+时会发生什么z。通过按组合键mc进入睡眠状态,就像您mc正常跑步但echo仍在等待睡眠一样mc

所以一切都是正常行为。除了鱼的行为之外。 Fish 似乎启动了echo ($command)在后台运行的命令。这是有道理的,因为这样的命令通常不需要任何输入。对于为什么以及如何我没有答案。但输入后可以看到它在后台运行

echo (mc)
jobs

相关内容