Bash 行操作

Bash 行操作

简介

Bash 内置了 Readline 库,具有这个库提供的很多“行操作”功能,比如命令的自动补全,可以大大加快操作速度。Bash 默认开启这个库,但是允许关闭。

这个库默认采用 Emacs 快捷键,也可以改成 Vi 快捷键。本章介绍的快捷键都属于 Emacs 模式。

Readline 的配置文件为 ~/.inputrc。我们可以在这里配置 Readline。

光标移动

Readline 提供快速移动光标的快捷键。

上面快捷键的 Alt 键,也可以用 ESC 键代替。

vim 中的快捷键是

Ctrl + f 屏幕『向下』移动一页

Ctrl + b 屏幕『向上』移动一页

清除屏幕

Ctrl + l 快捷键可以清除屏幕,即将当前行移到屏幕的第一行,与 clear 命令作用相同。

编辑操作

下面的快捷键可以编辑命令行内容。

使用 Ctrl + d 的时候,如果当前行没有任何字符,会导致退出当前 Shell,所以要小心。

剪切和粘贴快捷键如下。

同样地,Alt 键可以用 Esc 键代替。

自动补全

命令输入到一半的时候,可以按一下 Tab 键,Readline 会自动补全命令或路径。比如,输入 cle,再按下 Tab 键,Bash 会自动将这个命令补全为 clear

如果符合条件的命令或路径有多个,就需要连续按两次 Tab 键,Bash 会提示所有符合条件的命令或路径。

除了命令或路径,Tab 还可以补全其他值。如果一个值以 $ 开头,则按下 Tab 键会补全变量;如果以 ~ 开头,则补全用户名;如果以 @ 开头,则补全主机名(hostname),主机名以列在 /etc/hosts 文件里面的主机为准。

自动补全相关的快捷键如下。

上面的 Alt 键也可以用 ESC 键代替。

用的最多的还是 Tab,以 Alt 开头的快捷键用的很少。

操作历史

基本用法

Bash 会保留用户的操作历史,即用户输入的每一条命令都会记录。有了操作历史以后,就可以使用方向键的 ,快速浏览上一条和下一条命令。

退出当前 Shell 的时候,Bash 会将用户在当前 Shell 的操作历史写入 ~/.bash_history 文件,该文件默认储存 500 个操作。这个文件保存在用户的家目录下,也就是不同的用户的操作历史是分割开的。

环境变量 HISTFILE 总是指向这个文件。

当前登录用户的名字为 admin

$ echo $HISTFILE
/home/admin/.bash_history

history 命令会输出这个文件的全部内容。用户可以看到最近执行过的所有命令,每条命令之前都有行号。越近的命令,排在越后面。

$ history
...
498 echo Goodbye
499 ls ~
500 cd

在输入命令之前,按下 Ctrl + r 快捷键,就可以搜索操作历史,选择以前执行过的命令。这时键入命令的开头部分,Shell 就会自动在历史文件中按首部匹配,查询并显示最近一条匹配的结果,这时按下回车键,就会执行那条命令。不按回车,使用方向键的 可以切换这条历史命令的上一条命令和下一条命令。

!e 表示找出操作历史之中,最近的那一条以 e 开头的命令并执行。Bash 会先输出那一条命令,然后直接执行。

$ !echo
echo $HISTFILE
/home/admin/.bash_history

注意,!string 语法只会匹配命令,不会匹配参数。所以 !echo H 不会执行 echo Hello World,而是会执行 echo Goodbye,并把参数 H 附加在这条命令之后。同理,!echo H G 也是等同于 echo Goodbye 命令之后附加 H G

由于 !string 语法会扩展成以前执行过的命令,所以含有 ! 的字符串放在双引号里面,必须非常小心,如果它后面有非空格的字符,就很有可能报错。因此在双引号中 ! 后面最好只跟空格。

$ echo "hello !echo"
# 会查找以 echo" 开头的历史命令,肯定找不到
-bash: !echo": event not found
$ echo "hello ! echo"
hello ! echo
# 在 ! 前加上反斜杠即可,但是这样 \ 也会输出
$ echo "hello \!echo"
hello \!echo

history 命令

前面说过,history 命令能显示操作历史,即 .bash_history 文件的内容。使用该命令,而不是直接读取 .bash_history 文件的好处是,它会在所有的操作前加上行号,最近的操作在最后面,行号最大。而且通过定制环境变量 HISTTIMEFORMAT,可以显示每个操作的时间。比如可以这样定制各式 HISTTIMEFORMAT='%F %T ',其中 %F 相当于 %Y - %m - %d%T 相当于 %H : %M : %S

$ tail ~/.bash_history
czt $HISTFILE
cat $HISTFILE
echo $((val <<= 2,val *=2))
echo $HISTFILE
su admin
history
history
echo $HISTFILE
echo "hello ! echo"
echo "hello \!echo"
$ history | tail
  988  history
  989  HISTTIMEFORMAT='%F %T  '
  990  history
  991  tail ~/.bash_history
  992* HIST
  993  unset HISTTIMEFORMAT='
  994  unset HISTTIMEFORMAT
  995   HISTTIMEFORMAT
  996  tail ~/.bash_history
  997  history | tail
$ HISTTIMEFORMAT='%F %T  '
$ history | tail
  990  2023-08-26 08:31:07  history
  991  2023-08-26 08:31:39  tail ~/.bash_history
  992* 2023-08-26 08:31:59  HIST
  993  2023-08-26 08:32:48  unset HISTTIMEFORMAT='
  994  2023-08-26 08:32:52  unset HISTTIMEFORMAT
  995  2023-08-26 08:32:57   HISTTIMEFORMAT
  996  2023-08-26 08:33:06  tail ~/.bash_history
  997  2023-08-26 08:33:22  history | tail
  998  2023-08-26 08:33:38  HISTTIMEFORMAT='%F %T  '
  999  2023-08-26 08:33:40  history | tail

操作历史的每一条记录都有编号。知道了命令的编号以后,可以用 感叹号 + 编号 执行该命令。如果想要执行 .bash_history 里面的第 8 条命令,可以像下面这样操作。

$ !8

history 命令的 -c 参数可以清除操作历史。

$ history -c

环境变量 HISTSIZE 设置保存历史操作的数量。

$ export HISTSIZE=10000

上面命令设置保存过去 10000 条操作历史。

如果不希望保存本次操作的历史,可以设置 HISTSIZE 等于 0。

export HISTSIZE=0

如果 HISTSIZE=0 写入用户主目录的 ~/.bashrc 文件,那么就不会保留该用户的操作历史。如果写入 /etc/profile,整个系统都不会保留操作历史。

环境变量 HISTIGNORE 可以设置哪些命令不写入操作历史。

export HISTIGNORE='pwd:ls:exit'

上面示例设置,pwdlsexit 这三个命令不写入操作历史。多个命令之间用 : 分割

相关快捷键

下面是一些与操作历史相关的快捷键。

感叹号 ! 的快捷键 / 内置变量如下。

感叹号相关的变量可以用在脚本中,用于历史命令导航切换非常方便。

如果希望确定是什么命令,然后再执行,可以打开 histverify 选项。这样的话,使用 ! 快捷键所产生的命令,会先打印出来,等到用户按下回车键后再执行。

$ shopt -s histverify

其他快捷键

上面的 Alt + . 快捷键,对于很长的文件路径,有时会非常方便。因为 Unix 命令的最后一个参数通常是文件路径。

$ mkdir foo_bar
$ cd #按下 Alt + .

上面例子中,在 cd 命令后按下 Alt + .,就会自动插入 foo_bar