Bash/Prompt 自定义
Bash 有多个提示符,可以通过自定义来提高生产力、美观度以及极客感。
提示符
Bash 有五个可以自定义的提示符字符串
PS0在每个命令之后、任何输出之前显示。PS1是主提示符,在每个命令之前显示,因此它是大多数人自定义的对象。PS2是次要提示符,当命令需要更多输入时显示(例如多行命令)。PS3不常用。它是 Bashselect内建命令(用于显示交互式菜单)时显示的提示符。与其他提示符不同,它不会展开 Bash 转义序列。通常你会将其在使用了select的脚本中自定义,而不是在.bashrc中。PS4也不常用。它在调试 bash 脚本时显示,用于指示间接级别。第一个字符会重复出现以表示更深的级别。
所有提示符都可以通过将相应的变量设置为所需的字符串(通常在 ~/.bashrc 中)来自定义,例如
PS2='> '
技巧
虽然可以直接将提示符设置为普通字符串,但还有多种技巧可以让提示符更动态且实用。
Bash 转义序列
在打印提示符字符串时,Bash 会寻找特定的反斜杠转义字符并将其展开为特殊字符串。例如,\u 展开为当前用户名,\A 展开为当前时间。因此,PS1 设置为 '\A \u $ ' 将被打印为 17:35 username $ 。
请参阅 man 页 bash(1) § PROMPTING 或 Bash 参考手册以获取完整的转义序列列表。
Terminfo 转义序列
除了 Bash 识别的转义字符外,大多数终端还识别特殊的转义序列,这些序列影响终端本身而不是打印字符。例如,它们可能会改变随后打印字符的颜色,将光标移动到任意位置或清除屏幕。这些转义序列可能难以阅读且因终端而异,因此它们被记录在 terminfo 数据库中。要查看你的终端支持哪些功能,请运行
$ infocmp
功能名称(= 之前的部分)可以在 terminfo(5) 中查阅其功能描述。例如,setaf 设置其后打印文本的前景色。要获取某个功能的转义代码,可以使用 tput 命令。例如
$ tput setaf 2
打印将前景色设置为绿色的转义序列。
TERM 值。例如,如果你设置的是 xterm 而不是 xterm-256color,tput setaf 将仅支持 0-7 号颜色。为了将这些功能实际整合到你的提示符中,可以使用 Bash 的命令替换和字符串插值。例如
GREEN="\[$(tput setaf 2)\]"
RESET="\[$(tput sgr0)\]"
PS1="${GREEN}my prompt${RESET}> "
\[ \] 中。这有助于 Bash 忽略不可打印字符,从而正确计算提示符的大小。包裹在命令替换中将不起作用,在这种情况下必须使用 原始的 \1 \2。ANSI 转义序列
遗憾的是,某些有效的 ANSI 转义序列可能在你的终端 terminfo 数据库中缺失。这在支持 256 色等较新功能的转义序列中尤为常见。在这种情况下,你无法使用 tput,必须手动输入转义序列。
有关转义序列的示例,请参阅 Wikipedia:ANSI escape code。每个转义序列都以一个字面量转义字符开始,你可以使用 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 > '
\[ \] 无法用于包裹不可打印字符。相反,你可以使用八进制转义 \001 和 \002(例如使用 printf 或 echo -e)。PROMPT_COMMAND
如果设置了 PROMPT_COMMAND 变量或数组,它将在 PS1 显示之前被求值。这可以用来实现相当强大的效果。例如,它可以根据某些条件重新分配 PS1,或者在每次运行命令时对 Bash 历史记录执行某些操作。
PROMPT_COMMAND 通常不应直接用于向提示符打印字符。在 PS1 之外打印的字符不被 Bash 计算,这将导致其错误地放置光标并清除字符。请要么使用 PROMPT_COMMAND 设置 PS1,要么参考 嵌入命令。命令输入与输出之间的转义
你可以通过在 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。
示例
颜色
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 | 开启文本闪烁 |
| 加粗 | \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 > false
1 >
通过使用条件语句和函数,可以使其更有趣
exitstatus()
{
if [[ $? == 0 ]]; then
echo ':)'
else
echo 'D:'
fi
}
PS1='$(exitstatus) > '
:) > false
D: >
光标定位
可以在 PS1 内部在屏幕上移动光标,使提示符的不同部分出现在不同位置。然而,为了确保 Bash 将光标和输出放置在正确位置,在完成其他地方的打印后,必须将光标移回原位。这可以使用 tput 功能 sc 和 rc 来保存和恢复光标位置。移动光标的提示符通用模式为
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 为左上角)。cuu, cud, cuf 和 cub(上、下、前、后)相对于当前位置移动光标。例如 tput cuf 10 将光标向右移动 10 个字符。你可以在参数中使用 LINES 和 COLUMNS 变量,使光标相对于底部和右边缘移动。例如,要距离右下角 10 行和 5 列
$ tput cup $((LINES - 11)) $((COLUMNS - 6))
自定义终端窗口标题
终端窗口标题的自定义方式与提示符非常相似:在 shell 中打印转义序列。因此,用户通常将窗口标题自定义包含在提示符中。虽然这在技术上是 xterm 的功能,但许多现代终端都支持它。使用的转义序列是 ESC]2;新标题BEL,其中 ESC 和 BEL 是转义字符和响铃字符。使用 #Bash 转义序列,在提示符中更改标题看起来像这样
PS1='\[\e]2;new title\a\]prompt > '
当然,你的窗口标题字符串可以包含 嵌入命令 的输出或 $PWD 等变量,以便标题随每个命令而变化。