Bash/提示符自定义

出自 ArchWiki

Bash 有几个提示符可以自定义,以提高生产力、美观性和极客信誉。

提示符

Bash 有五个可以自定义的提示字符串

  • PS0 在每个命令之后,任何输出之前显示。
  • PS1 是主提示符,在每个命令之前显示,因此它是大多数人自定义的提示符。
  • PS2 是当命令需要更多输入时(例如,多行命令)显示的辅助提示符。
  • PS3 不常用。它是为 Bash 的 select 内置命令显示的提示符,用于显示交互式菜单。与其他提示符不同,它不展开 Bash 转义序列。通常,您会在使用 select 的脚本中自定义它,而不是在 .bashrc 中。
  • PS4 也不常用。它在调试 bash 脚本以指示间接级别时显示。第一个字符重复以指示更深的级别。

所有提示符都是通过将相应的变量设置为所需的字符串(通常在 ~/.bashrc 中)来自定义的,例如

PS2='> '

技巧

虽然可以简单地将提示符设置为纯字符串,但有多种技巧可以使提示符更动态和有用。

Bash 转义序列

打印提示字符串时,Bash 会查找某些反斜杠转义字符,并将它们展开为特殊字符串。例如,\u 展开为当前用户名,\A 展开为当前时间。因此,'\A \u $ ' 的 PS1 将打印为 17:35 username $

有关转义序列的完整列表,请参阅手册页 bash(1) § PROMPTINGBash 参考手册

Terminfo 转义序列

除了 Bash 识别的转义字符外,大多数终端还识别影响终端本身而不是打印字符的特殊转义序列。例如,它们可能会更改后续打印字符的颜色、将光标移动到任意位置或清除屏幕。这些转义序列可能有些难以辨认,并且可能因终端而异,因此它们记录在 terminfo 数据库中。要查看您的终端支持哪些功能,请运行

$ infocmp

功能名称(= 前面的部分)可以在 terminfo(5) 中查找,以获得对它们作用的描述。例如,setaf 设置之后打印的任何文本的前景色。要获取功能的转义代码,您可以使用 tput 命令。例如

$ tput setaf 2

打印将前景色设置为绿色的转义序列。

注意: 如果 tput 命令对您不起作用,请确保您已为您的 shell 设置了正确的 TERM 值。例如,如果您已设置 xterm 而不是 xterm-256color,则 tput setaf 将仅适用于颜色编号 0-7。

为了实际将这些功能合并到您的提示符中,您可以使用 Bash 的命令替换和字符串插值。例如

GREEN="\[$(tput setaf 2)\]"
RESET="\[$(tput sgr0)\]"

PS1="${GREEN}my prompt${RESET}> "
我的提示符>
注意: Bash 手册页建议将 tput 输出包装在 \[ \] 中。这有助于 Bash 忽略不可打印的字符,以便它可以正确计算提示符的大小。包装不适用于命令替换,在这种情况下,必须使用原始的 \1 \2

ANSI 转义序列

不幸的是,您的终端的 terminfo 数据库中可能缺少有效的 ANSI 转义序列。这对于较新功能(如 256 色支持)的转义序列尤其常见。在这种情况下,您不能使用 tput,您必须手动输入转义序列。

有关转义序列的示例,请参阅 Wikipedia:ANSI 转义码。每个转义序列都以文字转义字符开头,您可以使用 Bash 转义序列 \e 输入该字符。因此,例如,\e[48;5;209m 将背景设置为桃色(如果您有 256 色支持),而 \e[2;2H 将光标移动到屏幕左上角附近。

在不支持 Bash 转义序列的情况下(例如 PS3),您可以使用 Bash 的 printf 内置命令获取文字转义字符

ESC=$(printf "\e")
PEACH="$ESC[48;5;209m"

嵌入命令

如果您想将某些命令的输出添加到提示符中,您可能会想使用命令替换。例如,要将可用内存量添加到提示符中,您可以尝试

PS1="$(awk '/MemFree/{print $2}' /proc/meminfo) prompt > "
53718 prompt >
53718 prompt >
53718 prompt >

但这不起作用;显示的内存量每次都相同!这是因为该命令在首次设置 PS1 时运行一次,并且再也不会运行。诀窍是简单地通过转义 $ 或在单引号中定义它来阻止替换 - 无论哪种方式,它都会在实际显示提示符时被替换

PS1="\$(awk '/MemFree/{print \$2}' /proc/meminfo) prompt > "
# or
PS1='$(awk "/MemFree/{print \$2}" /proc/meminfo) prompt > '

为了防止长命令使您的 PS1 变得巨大,您可以定义函数

free_mem()
{
    awk '/MemFree/{print $2}' /proc/meminfo
}

PS1='$(free_mem) prompt > '
注意: 您可以在替换函数内部使用 terminfo/ANSI 转义序列,但不能使用 Bash 转义序列。特别是 \[ \] 不适用于包围不可打印的字符。相反,您可以使用八进制转义符 \001\002(例如,使用 printfecho -e)。

PROMPT_COMMAND

如果设置了 PROMPT_COMMAND 变量或数组,它将在 PS1 显示之前立即求值。这可以用于实现非常强大的效果。例如,它可以根据某些条件重新分配 PS1,或者在您每次运行命令时对您的 Bash 历史记录执行某些操作。

注意: PROMPT_COMMAND 通常不应用于直接将字符打印到提示符。在 PS1 之外打印的字符不被 Bash 计算在内,这将导致它错误地放置光标并清除字符。可以使用 PROMPT_COMMAND 设置 PS1 或查看 嵌入命令
提示: 如果 PROMPT_COMMAND 变得过于复杂,bash-preexecZshpreexecprecmd 钩子函数的 Bash 实现)可能会使其更易于维护。

命令输入和输出之间的转义

您可以通过不在 PS1 的末尾重置文本属性来影响 Bash 中的输入文本。例如,在 PS1 的末尾插入 tput blink 将使您键入的命令闪烁。但是,此效果也将持续到命令的输出中,因为当您按 Enter 键时,文本属性不会重置。

为了在您键入命令之后但在显示输出之前插入转义序列,您可以设置 PS0。或者,您可以捕获 Bash 的 DEBUG 信号,该信号在每个命令执行之前发送

$ trap 'tput sgr0' DEBUG

自定义 root 提示符

为了确保您知道何时以 root 身份运行,您可以自定义您的 root 提示符,使其清晰突出(也许是红色闪烁?)。这通常通过自定义 Bash 提示符来完成,但在 root 的主目录 /root 中。首先将骨架文件 /etc/skel/.bash_profile/etc/skel/.bashrc 复制到 /root,然后根据需要编辑 /root/.bashrc

示例

颜色

本文或章节需要语言、wiki 语法或样式改进。请参阅 Help:Style 以供参考。

原因:Color output in console 在列出颜色方面重复过多。应修剪至 zsh 长度。(在 Talk:Bash/Prompt_customization 中讨论)
提示: infocmp 显示 tput 可以使用的颜色数量,例如 colors#8

要查看终端支持的全部颜色范围,您可以使用带有 tput 的简单循环(将 setab 更改为 setaf 以获得文本前景色)

for C in {0..255}; do
    tput setab $C
    echo -n "$C "
done
tput sgr0
echo

如果这不起作用(并且您无法通过设置正确的 TERM 值来修复它),您可以测试不同的手动转义序列

# standard colors
for C in {40..47}; do
    echo -en "\e[${C}m$C "
done
# high intensity colors
for C in {100..107}; do
    echo -en "\e[${C}m$C "
done
# 256 colors
for C in {16..255}; do
    echo -en "\e[48;5;${C}m$C "
done
echo -e "\e(B\e[m"

要将手动转义从背景更改为前景,标准颜色范围是 30..37,高强度范围是 90..97,对于 256 色,48 应更改为 38。

常用功能

以下 terminfo 功能 对于提示符自定义非常有用,并且受到许多终端的支持。#1#2 是数值参数的占位符。

功能 转义序列 描述
文本属性
blink \e[5m 闪烁文本开启
bold \e[1m 粗体文本开启
dim \e[2m 暗淡文本开启
rev \e[7m 反色视频开启(切换文本/背景颜色)
sitm \e[3m 斜体文本开启
ritm \e[23m 斜体文本关闭
smso \e[7m 突出显示文本开启
rmso \e[27m 突出显示文本关闭
smul \e[4m 下划线文本开启
rmul \e[24m 下划线文本关闭
setab #1 \e[4#1m 设置背景颜色 #1 (0-7)
setaf #1 \e[3#1m 设置文本颜色 #1 (0-7)
sgr0 \e(B\e[m 重置文本属性
光标移动
sc \e7 保存光标位置
rc \e8 恢复保存的光标位置
clear \e[H\e[2J 清除屏幕并将光标移动到左上角
cuu #1 \e[#1A 将光标向上移动 #1
cud #1 \e[#1B 将光标向下移动 #1
cuf #1 \e[#1C 将光标向右移动 #1
cub #1 \e[#1D 将光标向左移动 #1
home \e[H 将光标移动到左上角
hpa #1 \e[#1G 将光标移动到第 #1
vpa #1 \e[#1d 将光标移动到第 #1 行,第一列
cup #1 #2 \e[#1;#2H 将光标移动到第 #1 行,第 #2
移除字符
dch #1 \e#1P 移除 #1 个字符(如退格)
dl #1 \e#1M 移除 #1
ech #1 \e#1X 清除 #1 个字符(不移动光标)
ed \eE[J 清除到屏幕底部
el \e[K 清除到行尾
el1 \e[1K 清除到行首

可视化退出代码

使用来自 嵌入命令 的相同技巧,您可以延迟特殊 Bash 变量(如 $?)的插值。因此,以下提示符显示上一个命令的退出代码

PS1="\$? > "
# or
PS1='$? > '
0 > true
0 > false
1 >

可以使用条件语句和函数使之更有趣

exitstatus()
{
    if [[ $? == 0 ]]; then
        echo ':)'
    else
        echo 'D:'
    fi
}
PS1='$(exitstatus) > '
:) > true
:) > false
D: >

定位光标

可以在 PS1 内部移动屏幕上的光标,使提示符的不同部分出现在不同的位置。但是,为了确保 Bash 将光标和输出定位在正确的位置,您必须在其他位置打印完成后将光标移回原始位置。这可以使用 tput 功能 scrc 来保存和恢复光标位置来完成。移动光标的提示符的通用模式是

PS1="\[$(tput sc; cursor-moving code) positioned prompt stuff $(tput rc)\] normal prompt stuff"

其中重新定位的提示符的整个块都包装在 \[ \] 中,以防止 Bash 将其计为常规提示符的一部分。

右对齐文本

在屏幕右侧打印文本的最简单方法是使用 printf

rightprompt()
{
    printf "%*s" $COLUMNS "right prompt"
}

PS1='\[$(tput sc; rightprompt; tput rc)\]left prompt > '
左侧提示符 > 右侧提示符

这会创建一个右对齐的可变大小字段 %*s,并将其大小设置为终端 $COLUMNS 的当前列数。

任意定位

cup 功能将光标移动到屏幕上的特定位置,例如 tput cup 20 5 将光标移动到第 20 行,第 5 列(其中 0 0 是左上角)。 cuucudcufcub(上、下、前、后)相对于其当前位置移动光标。例如,tput cuf 10 将光标向右移动 10 个字符。您可以在参数中使用 LINESCOLUMNS 变量,以相对于底部和右边缘移动光标。例如,要从右下角移动 10 行和 5 列

$ tput cup $((LINES - 11)) $((COLUMNS - 6))

自定义终端窗口标题

可以像自定义提示符一样自定义终端窗口标题:通过在 shell 中打印转义序列。因此,用户通常会在其提示符中包含窗口标题自定义项。虽然这在技术上是 xterm 的一项功能,但许多现代终端都支持它。要使用的转义序列是 ESC]2;新标题BEL,其中 ESCBEL 是转义字符和响铃字符。使用 #Bash 转义序列,在您的提示符中更改标题如下所示

PS1='\[\e]2;new title\a\]prompt > '

当然,您的窗口标题字符串可以包含来自 嵌入命令 或变量(如 $PWD)的输出,以便标题随每个命令更改。

参见