终端使用技巧

Bash

在文本编辑器中编辑当前命令

bash默认的Ctrl+x+Ctrl+e可以在文本编辑器中编辑当前命令,但是在退出编辑后会自动运行。如果只是想要在编辑器中编辑,但是退出编辑器后依然可编辑不运行,可以借助自定义函数来完成:

# in `bashrc`
edit_command() {
	local tmpf="/tmp/command_$(date +%m%d_%H%M%S).sh"
	printf '%s' "$READLINE_LINE" > $tmpf
	${EDITOR:-vi} $tmpf
	READLINE_LINE="$(< $tmpf)"
	READLINE_POINT="${#READLINE_LINE}"
	rm -f "$tmpf"
}
bind -x '"\C-e":edit_command'

这样就可以用Ctrl+e(由"\C-e"定义)编辑当前命令了。Ctrl+e默认的功能是跳转到行末,具有同样功能的还有End键)
如果想要修改使用的编辑器,可以选择定义EDITOR或者修改${EDITOR:-vi}为想要使用的编辑器。

可回退的多行命令

通常情况下,多行命令(如if-then-fi语句)在键入回车后,之前的内容即无法修改。可以通过修改bash默认的快捷键来解决这一问题:

# in `bashrc`
bind '"\C-j": self-insert'

之后在命令行中输入Ctrl+Enter时即可换行而不执行。

原理

bash接收的按键都经过转义,如按下Enter键实际接收到的是^M,也就是Ctrl+m;而按下Ctrl+Enter键实际接收到的是^J,也就是Ctrl+j

这两个键在bash中的默认作用是提交命令。将"\C-j"修改为self-insert后按下Ctrl+j就是插入换行符(而不执行命令)了,Ctrl+Enter实际发送给bash的就是^J,因此也是插入换行符。

如果要查看一个按键发送到bash后实际收到的是什么,可以通过Ctrl+q+<key>看到,例如Ctrl+q+Ctrl+Backspace就可以在命令行中得到一个^H,表明bash收到的是一个^H,用Ctrl+h也能打出一样的效果。

如果要查看一个快捷键在bash中的作用,可以通过bind -p看到。
其中\C-表示Ctrl键,而\e表示Alt键,与^[同义。

临时保存未完成的命令输入

命令写到一半想要运行某个命令(例如ls当前目录),不想放弃写到一半的命令,又不想影响剪贴板,可以通过快捷键Ctrl+u Ctrl+k把当前命令存入bash的剪贴缓冲区,然后运行想要运行的命令,运行完毕后通过Ctrl+y把之前存入的命令恢复回来继续编辑。

原理

bash的键盘交互是通过readline库实现的,readline提供了一个剪贴缓冲区,可以通过快捷键Ctrl+uunix-line-discard,删除光标前内容)和Ctrl+kkill-line,删除光标后内容)把当前命令删除并存入剪贴缓冲区,然后通过Ctrl+y(粘贴)把剪贴缓冲区的内容恢复回来。

也可以将kill-whole-line绑定到某个快捷键,一次性保存整行命令:

# in `bashrc`
bind '"\C-k":kill-whole-line'

之后按下Ctrl+k即可将整行命令存入剪贴缓冲区,不论当前光标在什么位置。

粘贴多行命令而不运行

向bash粘贴中粘贴多行命令时,粘贴内容中的换行符会被识别为提交命令。可以通过配置readline设置来修改这一表现:

# in `bashrc`
bind 'set enable-bracketed-paste on'

之后粘贴到bash的内容会保留(可回退的)换行符。

该功能在bash 5.1版本之后默认启用。

输出写入剪贴板

# in `bashrc`
osc52 () {
    printf '\e]52;c;%s\a' $(base64 -w 0 $1)
}

用法:

osc52 file
# or
command | osc52

需要终端客户端支持。

原理

\e]52;c;...\a是一种终端控制序列,当此格式的序列被打印到终端时,受支持的终端会将...中的内容经base64解码后写入剪贴板。

运行命令时修改终端标题

# in `bashrc`
PS0='\e]0;(busy)...\a'$PS0
PS1='\e]0;...\a'$PS1

效果:在bash命令执行时终端标题会变为(busy)...,执行完毕后会变为...。(...的内容可以自定义,例如\W@\H

原理

\e]0;...\a是一种终端控制序列,当此格式的序列被打印到终端时,受支持的终端会将标题修改为...

PS0PS1是bash的命令提示符,其内容分别会在命令执行前和命令执行后打印到终端上。

计算命令执行时间

# in `bashrc`
PS0='${PS1:$((ps_time=$SECONDS,0)):0}'$PS0
PS1='$(($SECONDS - ${ps_time-0}))s '$PS1

效果:命令运行完毕后会在提示符前显示命令执行时间(秒)。

原理

$SECONDS是一个bash内置变量,表示自shell启动以来的秒数。

PS0PS1是bash的提示符。PS1是等待输入提示符,内容一般为用户名和当前工作路径;PS0则是在提交命令后显示的提示符,通常是空的。

bash会在提交命令后等待输入前分别扩展PS0PS1的值,并将结果打印到终端上。

$(())是bash的算术扩展,其中的赋值语句会在扩展时生效。按上述方式设置PS0PS1后,bash会在命令提交后(执行前)记录当前时间到ps_time,并在命令执行后(执行后)计算当前时间与ps_time的差值,从而得到命令执行时间。

注意,如果提交的命令为空,bash不会扩展PS0,此时显示的是当前时间到上一条命令提交前时间的差值。如果要在此种情况下不显示秒数,可以再加入一个开关变量,或者在PROMPT_COMMAND中设置更加复杂的逻辑。

转义打印

彩色文字

printf '
\\e[0m\e[0mNormal
\\e[1m\e[1mBold\e[0m
\\e[2m\e[2mFaint\e[22m\\e[22mOff
\\e[3m\e[3mItalic\e[23m\\e[23mOff
\\e[21m\e[21mDouble \e[0m\\e[4m\e[4mUnderline\e[24m\\e[24mOff
\\e[5m\e[5mSlow Blink\e[25m\\e[25mOff
\\e[6m\e[6mRapid Blink\e[25m\\e[25mOff
\\e[7m\e[7mReverse\e[27m\\e[27mOff
\\e[8m\e[8mHidden\e[28m\\e[28mOff
\\e[9m\e[9mStrikeout\e[29m\\e[29mOff
\\e[51m\e[51mFramed\e[54m\\e[54mOff
\\e[52m\e[52mEncircled\e[54m\\e[54mOff
\\e[53m\e[53mOverlined\e[55m\\e[55mOff
\\e[30m\e[30mBlack         \e[0m \\e[40m\e[40mBackground\e[0m
\\e[31m\e[31mRed           \e[0m \\e[41m\e[41mBackground\e[0m
\\e[32m\e[32mGreen         \e[0m \\e[42m\e[42mBackground\e[0m
\\e[33m\e[33mYellow        \e[0m \\e[43m\e[43mBackground\e[0m
\\e[34m\e[34mBlue          \e[0m \\e[44m\e[44mBackground\e[0m
\\e[35m\e[35mMagenta       \e[0m \\e[45m\e[45mBackground\e[0m
\\e[36m\e[36mCyan          \e[0m \\e[46m\e[46mBackground\e[0m
\\e[37m\e[37mWhite         \e[0m \\e[47m\e[47mBackground\e[0m
\\e[38m\e[38mDefault       \e[0m
\\e[90m\e[90mBright Black  \e[0m\\e[100m\e[100mBackground\e[0m
\\e[91m\e[91mBright Red    \e[0m\\e[101m\e[101mBackground\e[0m
\\e[92m\e[92mBright Green  \e[0m\\e[102m\e[102mBackground\e[0m
\\e[93m\e[93mBright Yellow \e[0m\\e[103m\e[103mBackground\e[0m
\\e[94m\e[94mBright Blue   \e[0m\\e[104m\e[104mBackground\e[0m
\\e[95m\e[95mBright Magenta\e[0m\\e[105m\e[105mBackground\e[0m
\\e[96m\e[96mBright Cyan   \e[0m\\e[106m\e[106mBackground\e[0m
\\e[97m\e[97mBright White  \e[0m\\e[107m\e[107mBackground\e[0m
'

# 8 bit : color=5;$((0..15)) (standard) | color=5;$((16+36*$r+6*$g+$b)) (216 color) | color=5;$((232..255)) (gray scale)
# 24 bit : color=2;$R;$G;$B
echo -e '\e[0m\\e[38;'"${color}"'m\e[38;'"${color}"'mForeground\e[0m\\e[48;'"${color}"'m\e[48;'"${color}"'mBackground\e[0m'

效果:

\e[0mNormal
\e[1mBold
\e[2mFaint\e[22mOff
\e[3mItalic\e[23mOff
\e[21mDouble \e[4mUnderline\e[24mOff
\e[5mSlow Blink\e[25mOff
\e[6mRapid Blink\e[25mOff
\e[7mReverse\e[27mOff
\e[8mHidden\e[28mOff
\e[9mStrikeout\e[29mOff
\e[51mFramed\e[54mOff
\e[52mEncircled\e[54mOff
\e[53mOverlined\e[55mOff
\e[30mBlack          \e[40mBackground
\e[31mRed            \e[41mBackground
\e[32mGreen          \e[42mBackground
\e[33mYellow         \e[43mBackground
\e[34mBlue           \e[44mBackground
\e[35mMagenta        \e[45mBackground
\e[36mCyan           \e[46mBackground
\e[37mWhite          \e[47mBackground
\e[38mDefault
\e[90mBright Black  \e[100mBackground
\e[91mBright Red    \e[101mBackground
\e[92mBright Green  \e[102mBackground
\e[93mBright Yellow \e[103mBackground
\e[94mBright Blue   \e[104mBackground
\e[95mBright Magenta\e[105mBackground
\e[96mBright Cyan   \e[106mBackground
\e[97mBright White  \e[107mBackground

控制序列

printf "\e]2;%s\a" "terminal title" # 设置标题
printf "\e]52;c;%s\a" $(base64 <<< "clipboard text") # 写入剪贴板
printf "\e[?1003;1006h" # 鼠标追踪
printf "\e[?1049h" # 启用新的屏幕上下文
printf "\e[?1049l" # 退出屏幕上下文

向其他终端窗口发送消息

printf "message" > $target_tty # 向目标终端发送消息
read -r height width <<< $(stty size -F $target_tty) # 获取目标终端窗口大小

其中$target_tty是目标终端的tty设备(例如/dev/tty/3/dev/pts/2等),可以通过在对应终端内执行tty查看。

发送的消息不限于文本,也可以是控制序列,包括sixel图像。

Vim

返回退出时视图

# in `~/.vimrc`
autocmd BufReadPost *
    \ if line("'\"") > 0 && line("'\"") <= line("$") |
    \   exe "normal! g`\"" |
    \ endif
posted @ 2026-01-29 01:10  Fan-iX  阅读(0)  评论(0)    收藏  举报