【问题标题】:fish shell: add newline before prompt only when previous output exists鱼壳:仅当先前的输出存在时,才在提示之前添加换行符
【发布时间】:2021-04-19 16:46:40
【问题描述】:

(免责声明:我使用的是 fish,但这应该同样适用于 bash)
我当前的 shell 在提示符之前打印一个换行符,以便我可以在命令输出之间轻松找到它。

# [...]
echo # newline before prompt
echo -s $arrow ' ' $cwd $git_info
echo -n -s '❯ '

然而,当没有先前的输出时,也会打印换行符,例如使用printf "\033c" 清除终端后(或首次打开终端时):

                           <--- bad newline: no previous output
➜ /some/dir            
❯ command1            
output...             
                           <--- good newline
➜ /some/dir          
❯ command2             

问题:有什么办法可以摆脱这种小小的审美烦恼?



编辑#1:

为了澄清:“没有以前的输出”是指我的控制台的内容是空的,即在(重新)初始化终端之后(因为that's all printf "\033c" does)。

【问题讨论】:

  • 是您唯一关心的“(重新)初始化”案例吗?我在想你也想在前一个命令没有输出时抑制额外的换行符(例如export PATH 或类似的东西)。
  • @NotTheDr01ds 是的,目前这是唯一的情况。我希望在(重新)初始化之后每个提示都以换行符 except 为前缀。

标签: bash terminal fish


【解决方案1】:

编辑:经过编辑后更便于携带。

这是我在 bash 中的做法:

__PROMPT_NEWLINE=$'\nVVV '
__set_missing_newline_fix()
{
    local CURPOS
    echo -en "\033[6n" # ANSI DSR
    read -s -d R CURPOS
    CURPOS=${CURPOS#*;}
    if [ $CURPOS -eq 1 ]; then
        __MISSING_NEWLINE_FIX=""
    else
        __MISSING_NEWLINE_FIX="$__PROMPT_NEWLINE"
    fi
}

PROMPT_COMMAND=__set_missing_newline_fix
PS1="\${__MISSING_NEWLINE_FIX}\w > "

请注意,它被配置为在我的提示符前加上 VVV,让我知道最后一个命令没有以换行符结尾。

演示:

$ source bashrc.sh
/tmp/so/newline > echo hello
hello
/tmp/so/newline > echo -n hello
hello
VVV /tmp/so/newline > 

诡计:

ANSI DSR 将导致终端写入其当前光标位置作为输入。由于我们是交互式 shell,因此该输入可用于 shell 的标准输入,因此我们只需 read -s 它(没有回显)。

在上面的链接中,您会看到响应的格式为CSI Pl; Pc R,因此我们告诉read 阅读并包含R-d R

然后我们使用 bash 的“删除匹配前缀”语法 ${CURPOS#*;} 提取 Pc 部分,该语法会删除分号 ; 之前的所有内容。

然后,如果光标位置不是 1,即我们不在换行符的开头,我们手动在提示符中添加换行符。

ANSI DSR 应该适用于所有与 ANSI 兼容的终端,但如果您点击链接,您会发现它并不是字面上的 \033[6n,而是 CSI 6 nCSIescape sequence 的开头。转义序列以 ASCII 27 ESC 字符(八进制 033)开头。

在我的原始答案中,我使用了\E,它的 bash 内置 echo -e 命令解析与 \033 相同,因此进行了上述编辑。

【讨论】:

  • DSR 行为是否依赖于外壳、终端或操作系统?当使用 fish 时,echo -en "\E[6n" 只会得到字面的回应。在 bash 中它总是产生;1R,不管它是不是第一个命令。
  • 帖子已编辑,但听起来您的终端报告不正确,是否也发生在其他终端?
  • 另外,你如何测试它总是产生什么?您需要在位置不是 1 时发出 DSR(以验证...)并将其从终端读取到类似 hexdump 的内容中,以便响应中的第一个 ESC 不会被外壳或终端吞噬。
  • @Luis-Beaucamp 首先,鱼echo 相当于echo -en "\e[6n"。但是,一旦我们克服了这一点,我仍然留下了鱼的read 似乎无法处理这种情况的障碍。它的“静默”模式不会抑制,而是(来自手册页)“屏蔽写入终端的字符,用星号替换它们。这对于读取密码或其他敏感信息等内容很有用。” 似乎 bash 的 read 在获得 ANSI 返回结果时会自动返回,但 fish 的则不会。我还没能解决这个问题。
  • 糟糕 - 错过了 @root 的编辑。 \033 也便于钓鱼并正确返回位置。不过,还没有弄清楚read 的问题。
【解决方案2】:

exec 事件在几年前被合并到 fish-shell 中。我想你可以在这里使用这些。它们像其他语言中的事件生命周期挂钩一样工作。 https://github.com/fish-shell/fish-shell/pull/1666

作为测试,我创建了一个名为 postexec-newline.fish 的文件,其中包含以下内容:

function postexec_test --on-event fish_postexec
   echo
end

发出source postexec-newline.fish 后,您所描述的行为会被观察到。当使用 C-l 清除屏幕时,换行符也不可见,我认为这是这样一个功能应该如何表现的。

如果您希望永久更改,此函数可以存在于 config.fish 中。

【讨论】:

  • 我认为这是最干净的解决方案,错误的换行符现在在新打开的会话中消失了。因为我正在使用printf "\033c" 清除终端,所以仍然在此处插入换行符。但这可能可以通过阻止此函数运行或包含某些条件来解决。
【解决方案3】:

Stock fish 已经处理了这个问题(并且多年来一直如此),不需要提示。

当输出没有以换行符结尾时,您会看到“缺少换行符”,如“¶”或“⏎”,后跟换行符,然后是提示符。

【讨论】:

  • 请重新阅读原始问题。这是关于创建一个 custom 提示,当上一个命令,但在上一个命令没有输出时不添加。默认的鱼提示从不添加 extra(分隔符)换行符;如您所说,如果先前的输出没有以一个结尾,则它只会添加一个换行符。这可确保提示始终以单独的行开始,但不会创建 OP 正在寻找的任何视觉分隔。
  • 没错,很抱歉造成混乱。我添加了一些说明。
猜你喜欢
  • 1970-01-01
  • 2012-08-11
  • 1970-01-01
  • 1970-01-01
  • 2020-06-11
  • 1970-01-01
  • 1970-01-01
  • 2011-10-22
  • 1970-01-01
相关资源
最近更新 更多