p的设计与实现:动态修改bash、zsh的提示符

本文介绍了一款名为p的工具,它允许用户通过终端图形界面选择不同的bash和zsh提示符主题。文章详细讲解了如何修改bash和zsh的提示符,并提供了使用shell和Python实现的具体代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近看了TJ大神的node版本管理工具——n的源码(shell编写),收获颇多。我根据自己的需求编写了一个叫做p的工具,默认给出了bash、zsh提示符的多个主题,可以通过终端中的图形界面手动进行选择(相当于动态修改PS1变量)。这个工具开始是用shell实现,基本功能完成后,很多细节无法处理好,所以又用python写了一个比较完善的实现。

这是该工具的demo(动态gif):

输入图片说明

p的下载地址:https://github.com/someus/p

下面分析下它的实现。

如何修改bash的提示符

很简单,修改PS1变量就行了。不过为了让字体有颜色、显示用户名等,需要加一些特殊的字符。

我们看一下依次执行下面两个赋值语句的效果:

PS1="% "
PS1="\[\e[1;32m\][\W]\$\[\e[0m\] "

输入图片说明

\[\e[1;32m\]1代表粗体,32代表绿色。\[\e[0m\]则是重置颜色的意思。\e是escape的意思,有时候会写成\033,这是8进制,正好和ASCII表中的escape字符对应。

提示符支持很多颜色,例如31代表红色、33代表黄色,等等。

\W是当前路径,一般还会使用\u\h等。\u是用户名,\h是主机名,。

支持的转义字符有很多:


    \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
    \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

颜色支持和转义字符可以参考:

Color Bash Prompt - ArchLinux wiki

如何修改zsh的提示符

可以修改PS1,也可以修改PROMPT变量,不过颜色表示和转移字符都有所不同。另外,必须在配置文件~/.zshrc中加入下面的内容:

autoload -U colors && colors

看一下下面的赋值语句的效果:

PS1="$ "
PROMPT="%{$fg_no_bold[yellow]%}%1~ %{$reset_color%}%# "

输入图片说明

具体可以参考:

Zsh - Archlinux wiki

Zsh/Guide - gentoo wiki

终端中如何进入全屏

先看下shell该怎么做:

#!/usr/bin/env bash
enter_fullscreen() {  
  tput smcup    # Save the display
  stty -echo    # 不回显输入的字符
}

leave_fullscreen() {  
  tput rmcup    # Restore the display
  stty echo     # 回显输入的字符
}

enter_fullscreen  
echo "已经进入全屏"  
sleep 4

leave_fullscreen

上面定义的函数中enter_fullscreen用于进入全屏,leave_fullscreen用于退出全屏。

运行上面的脚本,可以看到:

输入图片说明

基于上面的代码,我们可以把各种效果的提示符打印出来:

#!/usr/bin/env bash
enter_fullscreen() {  
  tput smcup    # Save the display
  stty -echo    # 不回显输入的字符
}

leave_fullscreen() {  
  tput rmcup    # Restore the display
  stty echo     # 回显输入的字符
}

enter_fullscreen  

echo -e "$ "
echo -e "\e[1;32m[/home]\$\e[0m "

sleep 4
leave_fullscreen

有相同作用的python代码是:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import time
import os

def enter_fullscreen():
    print '\033[?1049h\033[H'
    os.system("stty -echo")

def leave_fullscreen():
    print '\033[?1049l'
    os.system("stty echo")

enter_fullscreen()
print "$ "
print "\033[1;32m[/home]\$\033[0m "
time.sleep(4)
leave_fullscreen()

注意,\e替换成了\033

检测方向键和回车的输入

这个功能要求程序可以监测上下方向键、回车键的敲击事件。

shell的实现:

#!/usr/bin/env bash
UP=$'\033[A'
DOWN=$'\033[B'

stty -echo # 关闭输入字符的回显

while true; do
    read -n 3 c
    case "$c" in
        $UP)
            echo "上"
            ;;
        $DOWN)
            echo "下"
            ;;
        "")
            echo "回车"
            break
            ;;
    esac
done

stty echo # 开启输入字符的回显

运行效果:

输入图片说明

python的实现:

# -*- coding: UTF-8 -*-
import termios, fcntl, sys, os

UP   = '\033[A'
DOWN = '\033[B'

fd = sys.stdin.fileno()
oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)

oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)

try:
    while 1:
        try:
            c = sys.stdin.read(3)
            if c == UP:
                print '上'
            elif c == DOWN:
                print '下'
            elif c == '\n':
                print '回车'
                break
        except IOError: pass
finally:
    termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)

运行效果和上面类似。

如何储存提示符的字符串值

使用键值对或者二维数组存储,提示符字符串值和样例需要成对出现。对于键值对,python中可以用字典,shell可以用关联数组(bash必须是版本4或者以上)。

以python为例,可以这样编写:

COLORS  = dict(
        txtblk='\033[0;30m', # Black - Regular
        txtred='\033[0;31m', # Red
        txtgrn='\033[0;32m', # Green
        txtylw='\033[0;33m', # Yellow
        txtblu='\033[0;34m', # Blue
        txtpur='\033[0;35m', # Purple
        txtrst='\033[0m'     # Text Reset  - Reset
    )
PROMPT_BASH = (
        dict(
            prompt  = '\[{txtgrn}\]\$ \[{txtrst}\]'.format(**COLORS), 
            example = '{txtgrn}$ {txtrst}ls -l'.format(**COLORS)
            ),
        dict(
            prompt  = '\[{txtcyn}\]\$ \[{txtrst}\]'.format(**COLORS), 
            example = '{txtcyn}$ {txtrst}ls -l'.format(**COLORS)
            ),
        )

p的命令行参数该怎么实现

以python为例子。

由于参数比较简单,就没用用argparse等模块。直接使用的sys.argv

if len(sys.argv[1:]) == 0:
    help()
    return

for arg in sys.argv[1:]:
    if arg in ('-h', '--help'):
        help()
        return
    elif arg in ('-v', '--version'):
        version()
        return
    elif arg in ('--patch', ):
        patch_config()
        return
    else:
        select_prompt(arg)
        return

现在,可以实现p了

假如当前使用的是bash。下面通过某次对p的使用说明p的主要流程:

对每个bash提示符编号,设置当前选中的编号为0(也就是第一个提示符),然后进入全屏,显示所有的提示符号的样例。被选中的提示符的样例在显示时需要加一个特殊的标记,便于用户区分。(p中使用的是箭头“->”)。

当按下方向键时候,设置当前选中的编号为1,clear屏幕,重新显示所有的提示符样例。

接着又按了下方向键,设置当前选中的编号为2,clear屏幕,重新显示所有的提示符样例。

然后又按了上方向键,设置当前选中的编号为1,clear屏幕,重新显示所有的提示符样例。

最后按了回车,由于当前选中的编号是1,我们将1对应的提示符的信息写入~/.p,内容可能是:

export PS1="\[\e[1;32m\][\W]\$\[\e[0m\] "

现在,手动source ~/.p即可。

但是为了方便,我们在~/.bashrc中加入:

alias p="p && source $HOME/.p"

这样,开启新的终端后,运行p之后就不必手动source ~/.p了。

如果要使用原先的命令p,而非alias的p,可以:

$ \p

p的安装和使用

上面将的是p的主要实现,当前p的实现要更完善,所以实际使用中会略有不同。

1、下载源码

在**https://github.com/someus/p**下载p的源码包,解压。

2、安装

$ sudo make install

默认安装到/usr/local/bin/p

3、在bash、zsh配置文件中加入alias等信息

$ \p --patch

目前配置不会在当前的终端生效。在一个新启动的bash或者zsh中会生效。

4、运行p

在一个新启动的bash或者zsh中,运行命令p。

参考

n

Color Bash Prompt - Archlinux wiki

Zsh - Archlinux wiki

Zsh/Guide - gentoo wiki

How can I change the color of my prompt in zsh?

ANSI escape code - wikipedia
How do I get a single keypress at a time? - python docs

Terminal control/Preserve screen

Python method for reading keypress?

转载于:https://my.oschina.net/letiantian/blog/527775

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值