Bash 命令提示符定制指南

11 min read

众所周知,Bash 是大多数 GNU/Linux 的默认 shell。作为每个 Linuxer 每天都要接触到的东西,一个漂亮的 Bash 自然是会让人更加的心情舒畅。今天就来教教大家如果自定义 Bash 的命令提示符


bash 默认的命令提示符长这个模样:

mogeko@ubuntu:~$

其中的 mogeko:是我的用户名,ubuntu:是我的主机名,~:是当前目录。

非常的不美观,并且浪费空间 (用户名和主机名并不是我所关注的东西却占了很大的空间)。好在我们可以通过设置 SP1:来定制我们的命令提示符

关于 SP1 和 SP2

简单来说:

  • SP1:bash 中命令输入的主提示符,如 mogeko@ubuntu:~$

  • SP2:当命令特别长的时候,换行输入时的提示符,默认是 >


可以在终端中使用 echo 命令查看系统默认参数:

mogeko@ubuntu:~$ echo $PS1
\u@\h:\W$
mogeko@ubuntu:~$ echo $PS2
>

定制命令提示符

举个栗子

所谓定制命令提示符,实际上就是在 ~/.bashrc:文件中改变变量 PS1:的值。

在开始之前,我强烈建议你预先备份 ~/.bashrc:文件。

例如,我们不想关心主机名,只要一个用户名就够了;只需要在 ~/.bashrc:文件的末尾加上以下命令:

export PS1="\u:\W$"

使用命令 :wq:退出并保存

然后执行以下命令使刚才的修改生效:

source ~/.bashrc

\u:\W$

通过上述例子我们可以看见,用户名 实际上就是参数 \u:的转义;同样的,主机名 对应的参数是 \h当前目录路径 对应的参数是 \W

我们想定制命令提示符,只需要将各参数按顺序排列组合就可以了。

SP1 参数

如果想知道设置 SP1 可以使用哪些参数只需要执行一下 man bash,然后找到 PROMPTING 部分的说明就可以了:

PROMPTING
       When executing interactively, bash displays the primary prompt PS1 when it is ready to read a command, and the secondary prompt PS2 when it needs more input to complete a command. Bash allows these prompt strings to be customized by inserting a number of backslash-escaped special characters that are decoded as follows:
              \a an ASCII bell character (07)
              \d the date in "Weekday Month Date" format (e.g., "Tue May 26")
              \D{format}
                     the format is passed to strftime(3) and the result is inserted into the prompt string; an empty format results in a locale-specific time representation.
                     The braces are required
              \e an ASCII escape character (033)
              \h the hostname up to the first ‘.’
              \H the hostname
              \j the number of jobs currently managed by the shell
              \l the basename of the shell’s terminal device name
              \n newline
              \r carriage return
              \s the name of the shell, the basename of $0 (the portion following the final slash)
              \t the current time in 24-hour HH:MM:SS format
              \T the current time in 12-hour HH:MM:SS format
              \@ the current time in 12-hour am/pm format
              \A the current time in 24-hour HH:MM format
              \u the username of the current user
              \v the version of bash (e.g., 2.00)
              \V the release of bash, version + patch level (e.g., 2.00.0)
              \w the current working directory, with $HOME abbreviated with a tilde (uses the value of the PROMPT_DIRTRIM variable)
              \W the basename of the current working directory, with $HOME abbreviated with a tilde
              \! the history number of this command
              \# the command number of this command
              \$ if the effective UID is 0, a #, otherwise a $
              \nnn the character corresponding to the octal number nnn
              \\ a backslash
              \[ begin a sequence of non-printing characters, which could be used to embed a terminal control sequence into the prompt
              \] end a sequence of non-printing characters
       The command number and the history number are usually different: the history number of a command is its position in the history list, which may include commands restored from the history file(see HISTORY below), while the command number is the position in the sequence of commands executed during the current shell session. After the string is decoded, it is expanded via parameter expansion, command substitution, arithmetic expansion, and quote removal, subject to the value of the promptvars shell option(see the description of the shopt command under SHELL BUILTIN COMMANDS below).

简单整理并翻译了一下:

  • \a:一个 ASCII 响铃字符 (07)
  • \d:“Weekday Month Date” 格式的日期 (比如:“Tue May 26”)
  • \D{format}:通过向 strftime 传值后回显自定义日期格式
  • \e:ASCII 转义字符 (033)
  • \h:主机名的第一部分,截取到第一个 ’.’
  • \H:主机的全称
  • \j:在当前 shell 中管理的进程数
  • \l:shell 的终端设备名称的基名
  • \n:换行符
  • \r:回车符
  • \s:shell 的名称,当前脚本的名称,不包括路径
  • \t:HH:MM:SS 格式的 24 小时制时间
  • \T:HH:MM:SS 格式的 12 小时制时间
  • \@:am/pm 格式的 12 小时制时间
  • \A:HH:MM 格式的 12 小时制时间
  • \u:当前用户的用户名
  • \v:bash 的的版本 (如:-00)
  • \V:bash 版本,包括补丁级别 (如:-00.0)
  • \w:当前工作目录,包括路径
  • \W:当前工作目录的基名,不包括路径
  • \!:当前命令的历史记录编号
  • \#:命令编号 (只要您键入内容,它就会在每次显示时累加)
  • \$:如果您不是超级用户 (root),则显示 ”$“;如果您是超级用户,则显示 ”#”
  • \nnn:对应于八进制数 nnn 的字符
  • \\:反斜杠
  • \[:开始一系列非打印字符,可以将终端控制序列嵌入到提示符中
  • \]:结束一系列非打印字符

为命令提示符着色

上面的参数可以改变命令提示符的内容。其实,命令提示符的颜色也是可以被修改的。

修改字符颜色需要用到以下两个标记:

  • \e[F;Bm:开始标记

  • \e[0m:结束标记

为字符设置颜色,只需要用两个标记将字符框起来就可以了:

\e[F;Bm需要设置颜色的部分\e[0m

其中 F 为字体颜色,编号 3037;B 为背景色,编号 4047。

示例颜色FB
   黑色3040
   红色3141
   绿色3242
   黄色3343
   蓝色3444
   粉紫色3545
   天蓝色3646
   白色3747

例如 :

export PS1="\[\e[32m\]\u\[\e[m\]\[\e[33m\]\\$\[\e[m\] "

效果:

  1. mogeko$

返回错误值

你也可以使用命令提示符来查看上一条命令执行的结果。

只需要在 ~/.bashrc 中创建一个函数:

function nonzero_return() {
 RETVAL=$?
 [ $RETVAL -ne 0 ] && echo "$RETVAL"
}

然后在调用 SP1 的时候中执行这个函数:

export PS1="\`nonzero_return\` "

Git 分支信息

你甚至可以在命令提示符中查看 Git 的分支信息 (如果当前目录下有 Git 仓库的话)。

~/.bashrc 中创建函数:

## get current branch in git repo
function parse_git_branch() {
 BRANCH=`git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
 if [ ! "${BRANCH}" == "" ]
 then
  STAT=`parse_git_dirty`
  echo "[${BRANCH}${STAT}]"
 else
  echo ""
 fi
}

## get current status of git repo
function parse_git_dirty {
 status=`git status 2>&1 | tee`
 dirty=`echo -n "${status}" 2> /dev/null | grep "modified:" &> /dev/null; echo "$?"`
 untracked=`echo -n "${status}" 2> /dev/null | grep "Untracked files" &> /dev/null; echo "$?"`
 ahead=`echo -n "${status}" 2> /dev/null | grep "Your branch is ahead of" &> /dev/null; echo "$?"`
 newfile=`echo -n "${status}" 2> /dev/null | grep "new file:" &> /dev/null; echo "$?"`
 renamed=`echo -n "${status}" 2> /dev/null | grep "renamed:" &> /dev/null; echo "$?"`
 deleted=`echo -n "${status}" 2> /dev/null | grep "deleted:" &> /dev/null; echo "$?"`
 bits=''
 if [ "${renamed}" == "0" ]; then
  bits=">${bits}"
 fi
 if [ "${ahead}" == "0" ]; then
  bits="*${bits}"
 fi
 if [ "${newfile}" == "0" ]; then
  bits="+${bits}"
 fi
 if [ "${untracked}" == "0" ]; then
  bits="?${bits}"
 fi
 if [ "${deleted}" == "0" ]; then
  bits="x${bits}"
 fi
 if [ "${dirty}" == "0" ]; then
  bits="!${bits}"
 fi
 if [ ! "${bits}" == "" ]; then
  echo " ${bits}"
 else
  echo ""
 fi
}

在调用 SP1 的时候中执行这个函数:

export PS1="\`parse_git_branch\` "

我的命令提示符配置

分享一下我个人使用的命令提示符配置 (复制粘贴到 ~/.bashrc 的末尾):

## PS1&PS2
## get current branch in git repo
function parse_git_branch() {
 BRANCH=`git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
 if [ ! "${BRANCH}" == "" ]
 then
  STAT=`parse_git_dirty`
  echo "[${BRANCH}${STAT}]"
 else
  echo ""
 fi
}

## get current status of git repo
function parse_git_dirty {
 status=`git status 2>&1 | tee`
 dirty=`echo -n "${status}" 2> /dev/null | grep "modified:" &> /dev/null; echo "$?"`
 untracked=`echo -n "${status}" 2> /dev/null | grep "Untracked files" &> /dev/null; echo "$?"`
 ahead=`echo -n "${status}" 2> /dev/null | grep "Your branch is ahead of" &> /dev/null; echo "$?"`
 newfile=`echo -n "${status}" 2> /dev/null | grep "new file:" &> /dev/null; echo "$?"`
 renamed=`echo -n "${status}" 2> /dev/null | grep "renamed:" &> /dev/null; echo "$?"`
 deleted=`echo -n "${status}" 2> /dev/null | grep "deleted:" &> /dev/null; echo "$?"`
 bits=''
 if [ "${renamed}" == "0" ]; then
  bits=">${bits}"
 fi
 if [ "${ahead}" == "0" ]; then
  bits="*${bits}"
 fi
 if [ "${newfile}" == "0" ]; then
  bits="+${bits}"
 fi
 if [ "${untracked}" == "0" ]; then
  bits="?${bits}"
 fi
 if [ "${deleted}" == "0" ]; then
  bits="x${bits}"
 fi
 if [ "${dirty}" == "0" ]; then
  bits="!${bits}"
 fi
 if [ ! "${bits}" == "" ]; then
  echo " ${bits}"
 else
  echo ""
 fi
}

## return
function get_return() {
    RETVAL=$?
    # Normal
    [ $RETVAL -eq 0 ] && echo -e "\033[32m▶ \033[0m"
    # Ctrl + C
    [ $RETVAL -eq 130 ] && echo -e "\033[32m▶ \033[0m"
    # Ctrl + Z
    [ $RETVAL -eq 148 ] && echo -e "\033[32m▶ \033[0m"

    # Error
    [ $RETVAL -ne 0 ] && [ $RETVAL -ne 130 ] && [ $RETVAL -ne 148 ] && echo -e "\033[31m✗ \033[0m"
}

export PS1="\`get_return\`\[\e[36m\][\W]\[\e[m\]\[\e[35m\]\`parse_git_branch\`\[\e[m\]\[\e[33m\]\\$\[\e[m\] "
export PS2="\[\e[32m\]>\[\e[m\] "

具有以下特点:

  • 第一位的是上一条命令的返回值 (正常情况下为: 运行出错为:)
  • 第二位的是当前的目录
  • 第三位的是当前目录的 Git 分支信息 (当前目录下没有 Git 仓库时自动隐藏)
  • 换行输入时的提示符:>

效果:

 [~]$ cd ~/Project/blog
 [blog][master]$ gi
 -bash: gi: 未找到命令
 [blog][master]$ \
 > \
 > touch temp.txt
 [blog][master]$