更正 shell 提示符的 fd

Correct fd for a shell prompt

我正在用 C 进行自定义 shell,我想知道应该在哪个 fd 上编写提示。

mycoolshell $

查看其他经典 shells,我发现 dash 使用 STDERR 作为其提示。 cshtcsh 使用 STDOUT。对于 bashzsh 和 BSD sh,我找不到任何东西。我用了

% dash 2>file
echo qwe
echo qwe
% cat file
(dashprompt$)
(dashprompt$)

查看dash的提示符fd。与 csh 1>file 的 csh 相同,但我对其他人不走运。

是否有标准或 POSIX fd?可以使用 STDIN 吗?

如果您希望 Posix 兼容,您需要将提示写入 stderr。 (请参阅下面的 PS1 环境变量的规范。)

不管严格的Posix兼容性,stdin肯定是不正确的,因为它可能不允许写操作。 stdout 也不是一个好主意,因为它通常是 line-buffered。某些 shell(包括 zsh,我相信)将提示写入连接到当前终端(例如 /dev/tty)的文件描述符,这可能是 stderr 打开的内容好像没有重定向,尽管它不一定是相同的文件描述符。但是使用 /dev/tty 或等价物是 non-standard.

仅当 shell 为 交互式 时才会打印提示。根据 Posix,如果以以下两种方式之一调用它,则 shell 是交互式的:

If the -i option is present, or if there are no operands and the shell's standard input and standard error are attached to a terminal, the shell is considered to be interactive. (sh utility, Options)

显然,如果您使用 shell 来执行脚本,您不希望它发出提示。所以你需要一些机制来判断 shell 是被交互使用还是被用作脚本处理器; Posix 的要求似乎相当准确。 (请参阅 isatty() 库函数以了解执行此测试的一种方法。)

这也说明了为什么当 stderr 被重定向到文件时您的测试未能捕获提示。重定向 stderr 会导致 shell 变为 non-interactive,因此不会出现提示。要正确进行测试,您需要使用 -i 选项强制 shell 进行交互。

Posix 要求可以通过更改 PS1 环境变量的值来修改提示。这是 Posix 必须要说的,包括将提示打印到 stderr 的要求:(强调)

PS1

Each time an interactive shell is ready to read a command, the value of this variable shall be subjected to parameter expansion and written to standard error. The default value shall be "$ ". For users who have specific additional implementation-defined privileges, the default may be another, implementation-defined value. The shell shall replace each instance of the character '!' in PS1 with the history file number of the next command to be typed. Escaping the '!' with another '!' (that is, "!!" ) shall place the literal character '!' in the prompt. (Shell command language, Shell Variables)

大多数 shell 允许在 PS1 中进行更丰富的替换。但该值受参数扩展影响的事实允许进行广泛的定制。这意味着(与通常的变量扩展不同)出现在 PS1 变量值中的参数和命令引用在每次打印提示时都会被扩展。