技巧
以下技巧均适用于 bash ,其他类型的 shell 可能不一定能全部适用。
快捷键模式
默认情况下, shell 的快捷键为 emacs 模式,如: Ctrl-A
/ Ctrl-E
将光标移动到行 头 / 尾, Ctrl-F
/ Ctrl-B
将光标向 前 / 后 移动一个字符, Ctrl-P
/ Ctrl-N
选择 上 / 下 一条历史命令等。
为了提升命令编辑效率,我们可以把快捷键设置为 vi 模式,之后我们就能使用 vi 的快捷键来编辑命令, shell 将像 vi 一样,可以在 normal 模式和 insert 模式之间切换。
下面的设置将 shell 的快捷键设置为 vi 模式,并且还附带了一些辅助设置:
set -o vi # 将 shell 快捷键设置为 vi 模式
stty werase undef # 取消 Ctrl-W 绑定(向后删除一个单词,空格为单词分隔符)
bind '\C-w:backward-kill-word' # 重新绑定 Ctrl-W (向后删除一个单词,非字母 / 数字字符为单词分隔符)
bind '\C-l:clear-screen' # 绑定 Ctrl-L 到清屏功能
bind '\C-f:forward-search-history' # 绑定 Ctrl-F 到向前查找历史命令功能(Ctrl-S 已绑定到暂停 shell 输出功能)
定制命令行提示
很多 Linux 的 Server 发行版的命令行提示默认比较简单,也没有颜色高亮,白(黄 / 绿)花花一片,我们输入的命令、命令的输出同样也是白(黄 / 绿)花花一片。当命令的输出很多时,我们用眼睛去识别哪部分是命令行提示、输入的命令或命令的输出会很费劲。
下面的设置将命令行提示符设置为:绿色显示用户名和主机名、蓝色显示当前的绝对路径、紫色显示当前时间
PS1='\[\033[01;32m\]\u@\h\[\033[00m\] \[\033[01;34m\]\w\[\033[00m\] \[\033[01;35m\]\t\[\033[00m\] \[\033[01;34m\]\$\[\033[00m\] '
历史查找
仅使用 上 / 下 方向键
(通用)、k
/ j
( vi 模式)、Ctrl-P
/ Ctrl-N
( emacs 模式)来翻找历史命令是很低效的,我们可以对历史命令进行增量搜索。
按Ctrl-R
之后,输入要搜索的历史命令(命令的任何一部分都行,如命令名、参数等),这时将开始历史命令的向 后 增量搜索过程。此时,如果连续按 Ctrl-R
,那么将不断显示上一条匹配当前输入的历史命令。如果有根据 快捷键模式 部分的推荐配置绑定了 Ctrl-F
的话,那么按 Ctrl-F
将转为历史命令的向 前 增量搜索过程。
找到自己想要的历史命令后,按 ESC
(vi 模式) / Ctrl-G
(emacs 模式)退出搜索过程,继续编辑命令;也可以直接按 Enter
执行当前命令。
别名
使用命令行难免需要经常重复输入一些比较繁琐的命令,给这些繁琐的命令定义简单的别名,可以节省大量的时间。别名的另一个用途是重新定义命令的行为,比如给一些命令增加上默认的参数等。很多 Linux 发行版都定义了一些简单的别名,如下:
alias cp='cp -i'
alias rm='rm -i'
alias mv='mv -i'
这些别名可以防止我们不小心错误地删除或覆盖文件。另外,如果我们想绕过别名机制,直接使用原始命令,那么在命令前加上反斜杠 \
即可,如下:
\rm test.txt
函数
有时候我们可能想使用一条命令执行一系列操作,如果我们把这一系列操作定义为别名,会导致定义过于复杂,可读性比较差;也可能我们需要传一些参数给若干命令,使用定义别名的方式无法满足这一需求。这时候我们可以定义 shell 函数。下面是几个简单的例子,其中$1
代表函数的第一个参数:
mcd() { mkdir -p "$1" && cd "$1"; }
cls() { cd "$1" && ls; }
backup() { cp "$1"{,.bak}; }
shell 函数的调用与执行普通命令的方式一样,如下:
mcd test
命令补全
我们都知道输入命令时,可以按 Tab
补全命令、文件名等,但是可能不是所有人都知道, shell 的补全机制其实是设计为可扩展的。以 debian 系的发行版为例(其他发行版可能有出入),我们能在 /etc
目录下找到 bash_completion
文件 和 / 或 bash_completion.d
目录。它们是对命令补全的扩展配置,例如对 git 命令的子命令补全进行配置,这样我们就能用 Tab
补全 git 的子命令了。在 debian 系发行版中,安装 bash-completion
包,我们就能获得很多常用命令的扩展补全配置。
历史展开
shell 的历史展开涉及很多内容,这里只对历史参数展开方面的部分常用内容进行叙述,想做详细了解的朋友可以参考History Expansion。
在 shell 命令中,我们可以使用一些特殊的标识来表示 上一条 执行过的命令中的某一 个 / 些 参数,例如:
-
!*
:所有参数 -
!^
:第一个参数 -
!$
:最后一个参数 -
!:n
:第n个参数 -
!:a-b
:第a个到第b个参数
下面是一个实际例子,我们执行以下命令:
ls t1 t2 t3 t4 t5
ls !:2-4
实际上执行的是:
ls t1 t2 t3 t4 t5
ls t2 t3 t4
子进程
shell 命令的执行默认是在当前 shell 进程中执行的,命令的执行能影响到当前的 shell 环境。如果我们不希望当前 shell 环境被影响,那么我们可以显式地指定命令在子进程中执行,方法就是用 小括号
将整条命令括起来,例如:
(cd build && cmake ..)
命令执行完成后, shell 的当前路径并不会发生变化。
子进程的创建还有另外一种应用场景,即把一条命令的输出作为另外一条命令的 参数 。这里与管道不一样,管道是将一条命令的输出作为另外一条命令的 输入 。例如,我们可以执行以下命令来递归统计当前目录下,所有Go源文件的代码行总数:
cat $(find -name "*.go") | wc -l
或:
cat `find -name "*.go"` | wc -l
工具
tmux
tmux 是终端复用工具,简单地说,就是我们能用它在终端上创建多个 窗口,窗口之间可以通过快捷键快速切换。同时,每个窗口又可以任意分割成多个 窗格,光标可以通过快捷键在窗格之间快速跳转。
tmux 的基本使用方法在网上有很多资料可以参考,这里就不赘述了。这里只叙述它的一些常规功能之外的优点,如下:
- CS (Client-Server) 设计架构,我们可以与 Server 建立起多个会话,每个会话可以独立管理窗口。我们可以随时断开会话,然后关闭终端窗口,只要 Server 进程还在,我们就可以随时重建会话,会话中的窗口和其中运行的进程完全不会受影响。
- 得益于 CS 架构,多个 Client 可以通过网络,与同一个 Server 的同一个会话建立连接, Server 的会话状态将实时推送到所有参与会话的 Client,也就是说,这是在做 直播,某一个 Client 的操作将对其他 Client 实时可见。
- 还是得益于 CS 架构,再加上 tmux 对子命令的支持,我们可以通过 tmux 子命令与 Server 交互。这样,我们就可以把常用的 tmux 操作写成 shell 脚本,执行自动化操作。
下面是一段 tmux 配置,可以加入到 ~/.tmux.conf
文件中,作用是将当前窗口分割成 田 字形的四个窗格,绑定的快捷键为 前缀键(默认Ctrl-B) Shift-W
:
bind-key W split-window -h \; split-window \; select-pane -t 0 \;\
split-window \; select-pane -t 0
下面是一段 shell 脚本,作用是创建一个 tmux 会话,然后在该会话中创建编号为 0 - 9 的十个窗口,最后连接上该会话。脚本接受一个可选参数,如果该参数不为空,那么将作为会话的名字,否则,会话名字默认为 “0”。脚本执行时,需要确保当前没有连接 tmux 会话:
#!/bin/bash
if [ -z $1 ]; then
SESSION_NAME="0"
else
SESSION_NAME=$1
fi
if $(tmux has-session -t "${SESSION_NAME}" > /dev/null 2>&1); then
tmux attach-session -t "${SESSION_NAME}"
exit 0
fi
tmux new-session -d -s "${SESSION_NAME}"
for i in {1..9}
do
tmux new-window
done
tmux select-window -t "${SESSION_NAME}:0"
tmux attach-session -t "${SESSION_NAME}"
网友评论