[TOC]

0x00 基础简述

描述:本章主要学习并记录了Linux中命令行补全以及参数补全的实现方法,还Linux中执行过的命令进行查看,让您想Hacker一样操作终端;

目录摘要:
补全:

  • 何谓补全: 即在我们使用shell命令行时候按tab键进行自动补全命令或者参数
  • 补全触发按键(Tab)
  • 文件名、路径名补全
  • 程序名、命令名补全
  • 用户名(~) 、 主机名(@) 、 变量名补全($)
  • 可编程补全

多种Shell测试版本:

1
2
3
4
5
# bash --version
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

# zsh --version
zsh 5.0.2 (x86_64-redhat-linux-gnu)

两者之间的对比:

1
2
bash : 除了sh就是它是最常用的shell,使用广泛多数发行版系统默认是bash
zsh: 用户的交互功能比bash更强大;


0x01 补全实践

文件名补全

ALT+?: 显示补齐参数列表

1
2
3
4
[[email protected] opt]$
gitlab/ LNMP/ sec/ student.sql

[[email protected] opt]$ gitlab/

FIGNORE=".ini": 排除指定后缀文件,在tab补全时候将不显示排除的文件;

1
2
3
4
5
6
[[email protected] frp]# ls
frpc frpc.ini frps_full.ini LICENSE
frpc_full.ini frps frps.ini systemd
[[email protected] frp]# FIGNORE=".ini"
[[email protected] frp]# cat frp
frpc frps


特殊补全

用户名(~)

示例1.快速进入系统用户家目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[[email protected] frp]# ~
~adm/ ~halt/ ~polkitd/
~bin/ ~lp/ ~postfix/
~chrony/ ~mail/ ~root/
~ctcss/ ~mysql ~shutdown/
~daemon/ ~nginx ~sshd/
~dbus/ ~nobody/ ~sync/
~ftp ~operator/ ~systemd-network/
~games/ ~php ~www
[[email protected] frp]# ~www
-bash: /home/www: No such file or directory
[[email protected] frp]# cd ~sshd
[[email protected] sshd]# ls -alg
total 0
drwx--x--x. 2 root 6 Aug 9 2019 .
drwxr-xr-x. 3 root 18 Mar 1 2019 ..
[[email protected] sshd]# pwd
/var/empty/sshd

主机名(@)
主机名来源的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat /etc/hosts
# 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
# ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
# 113.62.177.210 my.weiyigeek.top weiyi

cat > ~/.ssh/config<<END
Host *
controlmaster auto
controlpath /tmp/ssh_mux_%h_%p_%r
serveraliveinterval 90
serveralivecountmax 5
strictHostKeyChecking no

Host study.weiyigeek.top
HostName 127.0.0.1
User root
Port 22
END

基础示例:

1
2
3
4
5
6
7
8
9
10
11
12
#显示主机名
[[email protected] ~]# @
@::1 @localhost6.localdomain6
@localhost @localhost.localdomain
@localhost4 @my.weiyigeek.top
@localhost4.localdomain4 @weiyi
@localhost6


#ssh利用别名登陆
[[email protected] ~]$ ssh study.weiyigeek.top
Welcome to Ubuntu 14.04.6 LTS (GNU/Linux 3.19.0-25-generic x86_64)

变量名补全($)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[[email protected] ~]# $  [Tab 补齐]
$_ $COMP_WORDBREAKS $LINENO $PS4
$BASH $DIRSTACK $LINES $PWD
$BASH_ALIASES $EUID $LOGNAME $RANDOM
$BASH_ARGC $GROUPS $LS_COLORS $SECONDS
$BASH_ARGV $HISTCMD $MACHTYPE $SHELL
$BASH_CMDS $HISTCONTROL $MAIL $SHELLOPTS
$BASH_COMMAND $HISTFILE $MAILCHECK $SHLVL
$BASH_LINENO $HISTFILESIZE $OPTERR $SSH_CLIENT
$BASHOPTS $HISTSIZE $OPTIND $SSH_CONNECTION
$BASHPID $HOME $OSTYPE $SSH_TTY
$BASH_SOURCE $HOSTNAME $PATH $TERM
$BASH_SUBSHELL $HOSTTYPE $PIPESTATUS $UID
$BASH_VERSINFO $ID $PPID $USER
$BASH_VERSION $IFS $PROMPT_COMMAND $XDG_RUNTIME_DIR
$colors $LANG $PS1 $XDG_SESSION_ID
$COLUMNS $LESSOPEN $PS2

[[email protected] ~]# echo $SHELL
/bin/bash
[[email protected] ~]# echo $RANDOM
8857


命令行参数补齐

描述:针对于Bash以及Zsh命令行参数补齐我们可以分别采用以下扩展bash-completionzsh-completion

bash-completion

安装与配置:

1
2
3
4
5
6
#Linux
yum install -y bash-completion
yum install -y bash-completion-extras

#Mac
sudo pacman -s bash-completion

配置:
1
2
3
4
$vim ~/.bashrc
[ -r /usr/share/bash-completion/bash_completion ] && . /usr/share/bash-completion/bash_completion

source !$

基础实例:

1
2
3
4
5
6
[[email protected] ~]# ls --al
--all --almost-all

[[email protected] ~]# find -
-amin -ignore_readdir_race -path
-anewer -ilname -perm


zsh-autosuggestions

描述:显示命令参数以及历史信息默认都已经支持参数与子命令显示,而无需安装额外的扩展但是对bash-completion更为强大可以显示命令参数说明;

安装与配置:
配置:

1
2
3
4
5
6
7
8
cat > ~/.zshrc <<END
# Completion
autoload -U compinit
compinit -i
END

#配置生效
source ~/.zshrc

补充:zsh命令自动建议插件zsh-autosuggestions [2020年5月5日 23:36:55]
1
2
3
4
5
#下载
git clone git://github.com/zsh-users/zsh-autosuggestions ~/.zsh/plugins/zsh-autosuggestions

#编辑.zshrc文件
plugins=(git zsh-autosuggestions)

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#示例1
[[email protected]]~$ find -ignore_readdir_race
-ignore_readdir_race -inum -iwholename
-ilname -ipath
-iname -iregex

#示例2
[[email protected]]~# git init
index-pack -- build pack index file for an existing packed archive
init -- create empty git repository or re-initialize an existing one
instaweb -- instantly browse your working repository in gitweb

#示例3.命令建议示例
[[email protected]]/tmp# ls -lash #此时你打出-l便会从命令历史中进行显示最近一次输入的参数,然后按右键进行补齐选择
[[email protected]]/tmp# cd /tmp

可编程补全

描述: 主要用于我们shell编程脚本中进行命令参数进行提示,依赖于bash-completion插件;

bash示例可编程补全:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat > /etc/bash_completion.d/Testcmd<<'end'
# Completion
_Testcmd(){
local cur opts
#1.数组变量(包含当前命令行中每个单独的子),其中COMP-CWORD 表示当前光标位置在${COMP_WORDS} 中的索引;
cur="${COMP_WORDS[COMP-CWORD]}"
#2.命令选项
opts="--help --version"

if [[ ${cur} == -* ]];then
#3.生成可供补全的选项列表
COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
fi
}

#4.补全函数绑定到具体的shell脚本中
complete -F _Testcmd Testcmd
end


zsh示例可编程补全:接下来定义的函数与变量跟 bash 示例相似

  • words 相当于 bash 中的 COMP_WORDS
  • CURRENT 与 bash 中的 COMP_CWORD 类似
  • COMPREPLY 则和 compadd 这个内置的 zsh 命令相同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 步骤1
cat > ~/.zsh/_mycmd<<END
_mycmd(){
local cur opts
cur='${words[CURRENT]}'
opts=(--help --version)
if [[ ${cur} == -* ]];then
compadd -- ${opts}
fi
}
_mycmd "$0"
END

#步骤2.修改~/.zshrc 添加如下
# Completion:载入_mycmd中的补全代码
fpath=($HOME/.zsh $fpath)

实践 mycmd 在 zsh 中的补全效果,只需先执行一下 source ~/.zshrc,值得一提的是,zsh 本身还提供了一些辅助函数以用于补全,比如 _arguments、
_describe、_message 等等

1
2
$ mycmd --
--help --version


0x02 历史命令

在编程领域有一个十分重要的原则,那就是如何想办法来重复利用代码。
比如,通过把具有相同逻辑的代码抽象成函数,从而能够加以反复调用。

在Linux中的Shell终端里我们也可以将前面的命令进行复用,下面我们将先从设置历史变量以及如何查看、搜索、以及前后移动历史说起;

设置历史记录

描述:无论是 bash 还是 zsh,都能够将我们已经执行过的命令存储到一个文件中,这样便于我们以后对其加以重复使用;

Step1.查看 bash 或 zsh 的历史文件位置

1
2
3
4
5
[[email protected] ~]$ echo $HISTFILE
/root/.bash_history

[[email protected] ~]$ echo $HISTFILE
为 ~/.zsh_history

Step2.针对于bash进行历史变量记录设置(注意默认记录数为1000)

1
2
3
4
5
6
7
8
9
$ vim  ~/.bashrc
#历史文件报保存位置
HISTFILE=~/.bash_history
#HISTFILE文件所能保存的最大行数
HISTFILESIZE=1000
#Shell 中记忆的最大历史命令数
HISTSIZE=1000
#剔除掉那些重复的命令、开头包含空格的命令、以及常用的简单命令等等
HISTCONTROL='erasedups:ignorespace'

Step3.针对于zsh进行历史变量记录设置

1
2
3
4
5
6
7
8
$ vim ~/.zshrc
HISTFILE=~/.zsh_history
SAVEHIST=1000
HISTSIZE=1000
# 去掉重复的命令
setopt HIST_IGNORE_ALL_DUPS
#去掉开头具有空格的命令
setopt HIST_IGNORE_SPACE

补充说明:

  • 通常将这两个变量(HISTFILESIZE/HISTSIZE)设置的值保持一致,否则在 $HISTFILE 中保存的内容可能会被截断。
    • 比如:在$HISTSIZE 设为 1000 的情况下,而 $HISTFILESIZE 却为 500。
    • 因为:历史命令数大于文件的行数,所以有部分历史命令不能保存到历史文件中。
查看历史命令

描述:Shell 本身提供了 history 这个内置命令来让我们随时查看所记录的历史命令;

bash查看历史命令

1
2
3
4
5
6
7
8
9
#示例1.数字后面带 * 号的行则说明已经被修改过
~$ history | less
# 3 cat .bashrc
# 4 cat .bash_profile
# 5* cat .bash_history


#示例2.查看倒数的 5 个历史命令
history 5

zsh查看历史命令

1
2
3
4
5
6
7
8
9
10
#1.查看倒数几个命令
history -5

#2.查看一段范围内的历史命令
history -10 -5

#3.们提供更多的历史命令细节,包括命令执行的日期和时间,以及每个命令持续运行的时间
history -i -D
# 20 2020-05-08 16:52 0:00 history 5
# 21 2020-05-08 16:52 0:00 history -5

fc 命令
描述:除了 history 之外,另一个用来查看历史命令列表的是 fc;

1
2
3
4
5
6
7
8
9
10
11
12
#示例0.默认修改最有一条命令
$ fc

#示例1.-l选项使用列举命令历史
$ fc -l # 列出最后 16 条命令
$ fc -l -5 # 列出倒数 5 条命令
$ fc -l 20 30 # 列出编号 20 到 30 的命令
$ fc -l 100 # 列出编号为 100 后的所有命令
$ fc -l cat # 列出 cat 后的所有命令

#示例2.-e 选项还能够编辑历史命令列表
$ fc -e vi 5 10

搜索历史命令

描述:过滤出需要的命令的几种方式

1
2
3
4
5
6
7
8
#方式1.是将 history 与 grep 联用
history | grep 'xxx'

#方式2.按 Ctrl + r 组合键逆向搜索历史命令
#若是匹配成功,则显出结果。
#若是匹配失败,我们还可以按退格键删除字符,然后重新输入来继续搜索。
(reverse-i-search) ech: echo > ~/.bash_history #按下->右键
[[email protected] tmp]$ echo > ~/.bash_history

前后移动历史命令

描述:我经常使用的另外两组快捷键是 Ctrl + p (移到前一条命令) 和 Ctrl + n (移到后一条命令)或者键盘上的上下键,bash与zsh都能很好的支持;

快速执行历史命令

描述:既然我们把已经执行过的命令存储到 Shell 的历史文件中,那么自然想有一天能够再次用到它

  • 1.重复执行上一条命令采用!!,它被称为 bang bang;

    1
    2
    3
    4
    5
    6
    7
    #方式1.我们无需重新输入 htop 命令,只需按两下 !! 并敲回车即可
    $ htop
    $ !!

    #方式2.!! 经常与 sudo 联用,用来解决缺少权限的问题。例如:
    $ sudo !!
    $ sudo pacman -S figlet
  • 2.执行以某些字符打头的命令,Shell 将以逆序的方式搜索历史命令列表,一旦与给定的开头字符匹配到,便予以执行该条命令;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #方式1.例如利用 !foo 这种表示法允许我们执行以 foo 这三个字符打头的命令
    $ !he
    $ help

    #方法2.如果不能确定所找到命令的完整内容,那么可以在其后追加 :p(修饰符),这样Shell 将打印出该命令并不会执行。
    $ !grep:p
    grep locat /var/log/dmesg

    #方法3.快速执行历史命令中的第几条
    $ history | grep "htop" # 52 的一条
    $ !52
    #如果是负数就会执行命令中的倒数第二条,示例如下
    $ vim first.c
    $ gcc -o first first.c
    $ !-2 # 再编辑
    $ !-2 # 再编译
修改历史执行命令

描述:平常在使用命令行时,我经常会遇到的情况是,要么不小心,要么手太快,总之命令没有输入正确就执行了;
此时我们可以采用Shell提供的简便方法进行快速更正与执行;

例如,我在使用 grep 过滤日志时,不幸多输入了一个 o(原本是 loocat):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#原命令
$ grep loocat /var/log/dmesg

#执行 ˆo 即可将多余的 o 字符删除
[[email protected] tmp]# grep loocat /var/log/dmesg
[[email protected] tmp]# ^o #执行 ˆo 将上一条命令中找到的第一个 o 字符删除,从而纠正了输错的命令
grep locat /var/log/dmesg #Shell 在回显出正确的命令后立即执行了它
[ 0.000000] NODE_DATA(0) allocated [mem 0x13ffd7000-0x13fffdfff]
[ 0.000000] allocated 16777216 bytes of page_cgroup
[ 0.748714] ftrace: allocating 29560 entries in 16 pages
[ 1.702371] HugeTLB registered 1 GB page size, pre-allocated 0 pages
[ 1.705874] HugeTLB registered 2 MB page size, pre-allocated 0 pages
[ 2.601401] [TTM] Initializing pool allocator
[ 2.601404] [TTM] Initializing DMA pool allocator

例如:我在查看 file1 这个文件的内容时错输成了 flie1:

1
2
3
4
5
$ cat flie1

#以用 ˆliˆil 来将输错的 li 替换为 il。
$ ˆliˆil
$ cat file1

即便在没有输错的情况下,ˆoldˆnew 也是很实用的,假如我在查看 file1 后接着想查看 file4,那么只要执行 ˆ1ˆ4;

全局替换

描述:有时候我们想不只替换一处,而是把上一条命令中的每处内容都替换掉,此时我们可以使用!:gs/old/new, : (冒号) 后边的 gs 意为全局(g)
替换(s)/old/new 则与 ˆoldˆnew 相似;

基础实例:

1
2
3
4
5
6
7
8
9
$ansible nginx -a 'which nginx'

#bash方式
$ !:gs/nginx/haproxy
$ ansible haproxy -a 'which haproxy'

#zsh方式
$ ˆnginxˆhaproxyˆ:G
$ ansible haproxy -a 'which haproxy'

快速引用命令的参数 (重点)

描述:我们即将执行的命令与之前的命令具有相同的参数,比如同样的文件名、路径名等等,此时我们可以采用下面的方法进行重复利用上次执行的参数值;

基础示例:

  • 1.引用最后一位参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #方式1.最常用的是 !$,
    mkdir /tmp/web/
    cd !$ && pwd #/tmp/web/

    #方式2.按 Alt + .快捷键来达到同样的目的,可以获取历史中的前几个命令的参数
    [[email protected] tmp]$ pwd #ALT+.

    #方式3.采用$_的方式
    mkdir /tmp/web1/
    cd $_ && pwd #/tmp/web1
  • 2.引用最开头的参数

    1
    2
    3
    4
    5
    6
    #方式1.!ˆ 能够让我们引用上一条命令中最开头的参数
    $ ls /usr/share/doc /usr/share/man
    $ cd !^ && pwd #/usr/share/doc

    #方式2.组合键 Ctrl + Alt + y 可以实现同样的效果
    [[email protected] web1]$ /usr/share/doc
  • 3.引用所有参数

    1
    2
    3
    4
    #方式1.以使用 !*实现上一条命令的所有参数
    $ ls src code
    #cp 命令中的 !* 跟 src code 同样,它表示两个参数都要引用。
    $ cp -r !*
  • 4.引用第 n 个参数
    描述:对于引用上一条命令中的参数,我们甚至可以要求 Shell 精确到具体的第几个。

    1
    2
    3
    4
    5
    6
    #方式1.参数数组的index是从0开始的
    touch foo.txt bar.txt baz.txt
    0 1 2 3
    #vim 命令中的 !:2 就相当于上一条命令中的 bar.txt 文本文件。
    $ touch foo.txt bar.txt baz.txt
    $ vim !:2
  • 5.引用从 m 到 n 的参数
    描述:还有一种情况可能会遇到,即同时引用上一条命令的好几个参数,此时我们可以使用!:m-n 表示法,m 为开始端,n 为结束端;

    1
    2
    3
    #1.引用 touch 命令中的前两个参数。
    $ touch foo.txt bar.txt baz.txt
    $ nvim !:1-2
  • 6.引用从 n 到最后的参数
    描述:通过 !:n* 这种表示让我们能够从上一条命令中引用从第 n 个到最后的参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #示例1.下面允许我将 hosts 和 hostname 同时打开进行编辑。
    $ cat /etc/resolv.conf /etc/hosts /etc/hostname
    $ nvim !:2*


    #示例2.我们也可以引用历史列表中其它命令的参数
    #引用以 hi 打头的命令的第 2 个参数
    $ !hi:2
    #将引用第 10 条命令的 2、3 两个参数。
    $ !10:2-3


快速引用参数的部分 (重点)

描述:Shell比我们想象更为强大,利用 Shell 提供的历史展开模式修饰符,使我们得以快速引用参数中的部分内容。

  • 1.引用路径开头,助记技巧将 :h 想成 head 的开头字符

    1
    2
    3
    4
    5
    #1.在 !$(最后一位参数)的基础上添加了 :h
    #此处的 :h 为修饰符,意味着截取路径的开头部分,正如 dirname 的效果一样
    #引用该路径的开头部分 /usr/share//
    $ ls /usr/share//truetype
    $ cd !$:h && pwd
  • 2.引用路径结尾(值得学习),助记技巧将 :t 想成 tail 的开头字符

    1
    2
    3
    #通过 :t 修饰符,我们可以引用路径的结尾部分,其效果跟basename 类似。
    $ wget http://nginx.org/download/nginx-1.15.8.tar.gz
    $ ls -alh !$:t #nginx-1.15.8.tar.gz
  • 3.引用文件名,利用:r 修饰符来只引用文件名部分(这
    将排除掉扩展名)。

    1
    2
    ~$ unzip hello.zip 
    ~$ cd !$:r # 将 hello.zip 去掉扩展名,只保留 hello 部分
  • 4.将引用部分更改为大写或者小写(zsh特有bash是没有办法的)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #1) 通过 :u 修饰符我们能够将所引用的部分更改为大写字母
    [[email protected]]~# echo histchars
    histchars
    [[email protected]]~# echo !$:u
    echo HISTCHARS
    HISTCHARS

    #2) 通过:l 则使我们能够将所引用的参数全部更改为小写字母。
    [[email protected]]~# echo SAVEHIST
    SAVEHIST
    [[email protected]]~# echo !$:l
    echo savehist
    savehist

    PS:
    - 助记技巧,将 :u 想成 uppercase 的开头字符
    - 助记技巧,将 :l 想成 lowercase 的开头字符
  • 5.Shell 还支持将多个修饰符进行联用,在它们之间只需使用: (冒号) 分隔即可。
    1
    2
    3
    4
    5
    #1.我们先用 :t 引用了路径的结尾部分,然后又使用 :u 将其更改为了大写
    字母。
    ~$ ls /usr/share/fonts/truetype
    ~$ echo !$:t:u
    ~$ echo TRUETYPE

命令修饰符总结

描述:总结一下历史命令展开的模式,史展开模式包括以下三个部分:

1
2
3
1. !! !foo !n:用来调用历史列表中的命令
2. $ ˆ * n m-n n*:引用命令参数的各个部分
3. h t r [`前三个重点`] u l:修饰符,对所引用的内容进行修改

  1. 模式的每个部分之间都用 :(冒号)进行分隔,即太慢可以进行组合使用:

    1
    2
    #引用 ec 打头命令的最后一位参数,并只保留路径尾部
    ~$ !ec:$:t
  2. 通过脱字符 ^ 来替换上一条命令中的 debian 为 ubuntu 然后执行相同的命令

    1
    2
    3
    4
    5
    [email protected]:~/docke/alpine# docker run --rm -it -e TZ=Asia/Shanghai debian date
    Thu Jan 2 11:38:56 CST 2020
    [email protected]:~/docke/alpine# ^debian^ubuntu
    docker run --rm -it -e TZ=Asia/Shanghai ubuntu date
    Thu Jan 2 03:44:13 Asia 2020

0x03 命令行编辑

我们在 Vim、Emacs、Sublime、VS Code 等熟悉的编辑器中编辑文本时,通常会有一种十分舒服的感觉;

Shell 命令行也能像文本编辑器一样编辑命令使我们的行编辑效率大大提升,下面我们将要学习Emacs(Linux默认方式)与vi两种编辑模式,注意这里只是小试牛刀,由于本人喜欢使用vi所以专门做了一篇文章进行记录;

设置编辑模式

描述:bash 与 zsh 都提供了 Emacs 和 vi 两种编辑模式,我们可以利用下面的命令在两种编辑模式下进行选择;

  • bash 方式:

    1
    2
    3
    #临时生效
    ~$ set -o vi
    ~$ set -o emacs
  • zsh 方式:

    1
    2
    3
    #临时生效
    ~$ bindkey -e
    ~$ bindkey -v

为了永久保存设置,我们需要将 bash 的设置选项添加到~/.bashrc 配置文件。而 zsh 的设置选项则需添加到~/.zshrc 配置文件;

Emacs 编辑模式

描述:该模式下主要是按字移动和删除以及替换 、按 “词” 移动和删除、按行移动和删除;

Emacs 编辑模式的内容编辑范围主要包括下列 3种:

    1. “词”
  • 字移动和删除以及替换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [[email protected] ~]$ echo WeiyiG`e`ek  , 假如此时的光标在e的地方;

    #1.命令行中的光标移动方法
    - 按左方向键(←)和右方向键(→)来向左或往右移动一个字符
    - Ctrl + b 左移动一个字符 和 Ctrl + f 右移动一个字符

    #2.命令行中删除字符的方法
    Backspace 退格键
    Ctrl + d 来删除光标下的字符

    #3.交换左边的两个字符顺序
    Ctrl + t #将光标左边的两个字符交换顺序
    [[email protected] ~]$ echo sl
    sl
    [[email protected] ~]$ echo ls

    注意事项:在空的命令行按 Ctrl + d 将退出 Shell。

  • 2 按 “词” 移动和删除

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #(1)命令行默认分词符号查看
    #通过变量 WORDCHARS 我们可以看到这类字符到底有哪些
    [[email protected] ~]$ echo $WORDCHARS #bash 空行

    # zsh 中对 “词” 的界定跟 bash 有所不同
    ~$ echo $WORDCHARS
    *?_-.[]~=/&;!#$%ˆ(){}<>

    #(2)命令行默认分词符号设置
    :~$ WORDCHARS= #将zsh判定词的行为与bash一致

    #(3) 按 “词” 移动和删除的操作例子:
    $ grep 'figlet' /var/log/pacman.log

    Alt + b 向左移动一个 “词”
    Alt + f 往右移动一个 “词”
    Alt + 退格键 删除光标左边的 “词”
    Ctrl + w 同上
    Alt + d 删除光标右边的 “词”
    Ctrl + y 获取上次删除的内容
    Alt + t 交换光标左边两个 “词” 的顺序
    #zsh特有
    Esc + c 将光标右边的 “词” 的开头字母变成大写
    Esc + u 将光标右边的 “词” 全部更改为大写字母
    Esc + l 将光标右边的 “词” 全部更改为小写字母
  • 3.按行移动和删除

    1
    2
    3
    4
    Ctrl + a 将光标移到行首(最左边)
    Ctrl + e 将光标移到行尾(最右边)
    Ctrl + k 从光标处往右删除至行尾
    Ctrl + u 从光标处向左删除至行首
  • 4.Emacs 编辑模式总结

    WeiyiGeek.

    WeiyiGeek.


vi 编辑模式

描述:与 Emacs 编辑模式相比,vi 编辑模式为我们提供了更多的控制命令;

  • 移动命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #(1)vi 编辑模式中的移动命令
    echo hello, this is a command
    #首先,按 Esc 键进入命令模式,此时光标位于 command 结尾的 d 上。
    h 向左移动一个字符
    l 往右移动一个字符
    b 向左移动一个单词
    w 往右移动一个单词
    e 移到单词结尾
    B、W、E 与 b、w、e 类似,按不同的单词定义进行移动
    0 移到行首
    ˆ 移到行首,但第一个字符为非空白字符
    $ 移到行尾
  • 重复命令:每个移动命令之前可以跟一个数字,用来将该命令重复执行多次。
    例如,3b 表示向左移动 3 个单词,5l 则表示往右移动 5 个字符。

  • 添加文本:编辑可以从vi命令模式回到插入模式

    1
    2
    3
    4
    i 在光标左边插入新的文本内容
    a 在光标右边追加新的文本内容
    I 在行开头插入新的文本内容
    A 在行结尾追加新的文本内容
  • 删除文本:跟移动命令一样,在上述删除命令之前也可以带一个数字,以便多次执行该命令。
    例如:5x 将删除 5 个字符,而 3dw 将删除 3 个单词,这里 3 的顺序并不重要,d3w 仍然同样有效。

    1
    2
    3
    4
    5
    x 删除光标下的字符
    X 删除光标左边的字符
    dm m 为某个移动指令,如 db 删除光标左边的单词 ,dl则是删除光标右边的单词(值得学习)
    D 从光标处删除到行尾
    dd 删除整行内容

    注意:通过删除命令删除的内容,Shell 并没有丢弃,而是将其保留在了删除缓冲器中,我们可以通过执行u命令来恢复这些删除的内容(可以多次使用)

  • 替换文本:在删除该内容后再进入插入模式重新输入外,也可以使用 vi 编辑模式所提供的文本替换命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #先进入命令模式,按 cb 将 kode 删除后进入了插入模式,我们输入新的内容 code。再次按 Esc,接着按 4b 左移到 show,按 r 将s 替换成 S
    $ echo talk is cheap. show me the kode.

    cm m 为某个移动命令,如 cw 将光标右边的单词删掉后进入插入模式
    C 从光标处删除到行尾,并进入插入模式
    cc 删除整行,并进入插入模式
    r 替换光标下的字符
    R 进入替换文本模式,直到按 Esc 结束
    s 利用输入的字符来替换光标下的字符,直到按 Esc 结束
  • 搜索字符:令用于搜索命令行中的字符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #当跟 d 和 c 命令组合使用,还能够删除或更改从光标处到该字符的这一段文本。
    #进入命令模式后,按 fp 光标移到了 p 上,按 th 移到了 h 左边的w。按 Fm 光标左移到 m 上。
    :~$ echo A program which handles the interface

    fc 移动光标到 c 的下一处
    Fc 与 f 相反方向搜索,移动光标到 c 的上一处
    tc 移动光标到 c 左边的字符
    Tc 移动光标到 c 右边的字符
    ; 重复上次的 f 或 F 命令
    , 以相反的方向重复上次的 f 或 F 命令

vi 编辑模式总结:

WeiyiGeek.

WeiyiGeek.


0x04 必备锦囊

快速导航

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#1.回到用户主目录和指定用户家目录
cd ~
cd ~weiyigeek #来转到别的用户的主目

#2.回到上次工作的目录(可以重复执行)
cd -
cd "$OLDPWD" && pwd

#3.访问常用目录
#bash 和 zsh 两个都为我们提供了 $CDPATH 变量,它由 : (冒号) 分隔的路径列表组成(类似于$PATH)。
~$ CDPATH=:~:~/src:~/tmp/WeiyiGeek #$CDPATH 路径列表中的目录为待导航的目标目录
的父目录
#假如我们打算转到 ~/tmp/web/iscsi 目录下的话,那么只要执行下列命令即可
[[email protected] ~]$ mkdir /tmp/web/iscsi
[[email protected] ~]$ CDPATH=:~:~/src:/tmp/web
[[email protected] ~]$ cd iscsi && pwd
/tmp/web/iscsi
/tmp/web/iscsi

#4.自动纠正错误:帮助我们自动纠正拼写错误,并导航到正确的目录
#开启 bash 的控制选项:shopt -s cdspell
[[email protected] ~]$ shopt -s cdspell
[[email protected] ~]$ cd /ect #原本是想要导航到 /etc 目录,但是我们却错输成了 /ect , 但是任然可以正确的进入etc目录中;
/etc #如果cd 有两个参数则以第二个字符串替换它;


#5.自动导航
#启用 autocd 选项:shopt -s autocd | setopt autocd
#现在假设我们想导航到 ~/prj 目录,省略 cd 命令代替执行:
~$ prj
~/prj$ pwd
/home/weiyigeek/prj


#6.使用目录栈:两个最基本的目录栈命令是 pushd 和 popd。
* pushd 命令将一个目录添加到目录栈中(入栈)
* popd 命令则从目录栈中移除上次添加的目录(弹栈)
[[email protected] tmp]$ ls
a b
[[email protected] tmp]$ pushd ./a/
/tmp/a /tmp
[[email protected] a]$ pushd /etc/ssh/
/etc/ssh /tmp/a /tmp
[[email protected] ssh]$ popd
/tmp/a /tmp
[[email protected] a]$ pwd
/tmp/a
#如果你在执行多次入栈与出栈后忘了目录栈中还有哪些条目的话,那么可以执行 dirs -v 命令来查看;
dirs -v
0 /tmp/a
1 /tmp

使用别名

描述:我们使用alias命令来定义别名,比如一些发行版本中我们可以使用ll别名来显示ls -lah --color=auto所达到的效果;

基础示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#1.alias别名此处是临时生效,如果想永久生效需要在profile 或 bashrc中加入;
alias ls='ls --color=auto'
alias sl='ls --color=auto'


#2.alias 命令还能用 -s 选项来定义后缀别名。
alias -s pdf=zathura
#通过执行 cheat_sheet_ssh_v4.pdf 来代替执行 zathuracheat_sheet_ssh_v4.pdf 命令
cheat_sheet_ssh_v4.pdf

#3.查看别名
alias sd
alias sd='shutdown -h now'

#4.取消别名(永久和临时)
unalias sl
\sl

#4.PS:别名中无法参数化
#你应当考虑使用的是函数。另一方面,别名可能覆盖真实的命令,从而误导你原本想要执行命令的意图。
type -a ll
ll is aliased to `ls -l --color=auto'


{} 构造参数

描述:针对多个参数条目执行操作的使用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#示例0.(Shell 也支持通过 .. (两点) 来指定一个区间)与连用嵌套
echo {1..9..2} #步进值
1 3 5 7 9
echo {1..9..-2} #倒序罗列数字
9 7 5 3 1
echo {9..1..2} #效果同上
echo {{A..Z},{a..z},{0..9}} # {} (花括号) 结构不仅可以连用,而且能够嵌套

#示例1.bash 和 zsh 都提供了逗号分隔的花括号列表
echo {one,two,three} #生成以下 3 个参数条目
one two three

#示例2.批量创建文件或者目录
touch {1..9}.txt #创建1-9个txt文件;
mkdir {a..z} ##创建a-z名称的目录;
mkdir -p 2019/{01..12}/{baby,photo}

#示例3.备份文件与来创建存档
cp file{,.bak} #参数 file{,.bak} 展开后将变成 file ->> file.bak
tar cf docs{.tar,} #将 docs 目录存档为 docs.tar。


#实例4.通过生成的序列,将其与路径组合,在下载多个文件时尤其有用
~$ wget https://linuxtoy.org/img/{1..5}.png
补充说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#示例1.要求 Shell 在执行 grep 命令后直接将文件名传递给文本编辑器 (如 nvim):
vim `grep -l error *.py`
vim $(grep -l error *.py)

#在嵌套时,$() 看起来一目了然,而 `` (反引号) 则需要转义,其可读性较差。
~$ vim $(grep -l failed $(date +'%Y%m%d').log)
~$ vim `grep -l failed \`date +'%Y%m%d'\`.log`

#示例2.查询后进行打开
grep -l ssh /etc/ssh/* | awk '{cmd="cat " $1;system(cmd)}'


#示例3.重复执行命令,利用 Shell 提供的 for 循环;
~$ cd /usr/share/figlet
~$ for font in *.tlf
> do
> echo "Font: $font"
> figlet -f $(basename $font .tlf) Linux
> done

#单行形式
for font in *.tlf; do figlet -f $(basename $font .tlf) Linux; done

0x05 补充命令

cmatrix 命令

描述:这是在黑客帝国中显示代码雨效果的命令;

1
2
3
4
5
6
7
8
9
10
yum -y install ncurses* gcc gcc-c++
tar xf cmatrix-1.2a.tar.gz
./configure
make && make install
cmatrix

#特殊按键不同的效果
a B b n
1~9
! @ # $

参数参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
-a:异步滚动
-B:启用粗体字符
-b:所有粗体字符(覆盖-b)
-f:强制启用linux$term类型
-l:linux模式(使用矩阵控制台字体)
-o:使用旧式滚动
-h:打印使用和退出
-n:无粗体字符(覆盖-b和-b,默认)
-s:“屏幕保护程序”模式,在第一次按键时退出
-x:窗口模式,如果您的xterm使用mtx.pcf,则使用
-v:打印版本信息并退出
-u:延迟(0-10,默认4):屏幕更新延迟
-C[颜色]:将此颜色用于矩阵(默认为绿色)

WeiyiGeek.

WeiyiGeek.


shopt 命令

描述:用于显示和设置shell中的行为选项,通过这些选项以增强shell易用性。
PS:若不带任何参数选项,则可以显示所有可以设置的shell操作选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

$shopt
#shell操作选项
autocd off #对于目录可以忽略cd从而只输入目录名即可进入;
cdable_vars off #如果给cd内置命令的参数不是一个目录就假设它是一个变量名,变量的值是将要转换到的目录
cdspell off #目录名的较小拼写错误,检查的错误包括颠倒顺序的字符,遗漏的字符以及重复的字符,它会自动修改为正确路径
checkhash off #bash在试图执行一个命令前,先在哈希表中寻找,以确定命令是否存在.如果命令不存在,就执行正常的路径搜索
checkjobs off
checkwinsize off #bash在每个命令后检查窗口大小,如果有必要,就更新LINES和COLUMNS的值
cmdhist on #bash试图将一个多行命令的所有行保存在同一个历史项中.这是的多行命令的重新编辑更方便(缺省on)
compat31 off
compat32 off
compat40 off
compat41 off
direxpand off
dirspell off
dotglob off #Bash在文件名扩展的结果中包括以点(.)开头的文件名
execfail off #如果一个非交互式shell不能执行指定给exec内置命令作为参数的文件它不会退出,如果exec失败一个交互式shell不会退出
expand_aliases on #别名被扩展(缺省on)
extdebug off
extglob on #打开扩展的模式匹配特性(正常的表达式元字符来自Korn shell的文件名扩展)(缺省on)
extquote on
failglob off
force_fignore on
globstar off
gnu_errfmt off
histappend on #如果readline正被使用,用户有机会重新编辑一个失败的历史替换
histreedit off
histverify off #如果设置,且readline正被使用,历史替换的结果不会立即传递给shell解释器.而是将结果行装入readline编辑缓冲区中,允许进一步修改
hostcomplete off # 如果设置,且readline正被使用,当正在完成一个包含@的词时bash将试图执行主机名补全
huponexit off
interactive_comments on #在一个交互式shell中.允许以#开头的词以及同一行中其他的字符被忽略.缺省为打开
lastpipe off
lithist off #如果打开,且cmdhist选项也打开,多行命令讲用嵌入的换行符保存到历史中,而无需在可能的地方用分号来分隔
login_shell on
mailwarn off #如果设置,且bash用来检查邮件的文件自从上次检查后已经被访问,将显示消息”The mail in mailfile has been read”
no_empty_cmd_completion off
nocaseglob off #如果设置,当执行文件名扩展时,bash在不区分大小写的方式下匹配文件名
nocasematch off
nullglob off #如果设置,bash允许没有匹配任何文件的文件名模式扩展成一个空串,而不是他们本身
progcomp on
promptvars on #如果设置,提示串在被扩展后再进行变量和参量扩展.缺省为打开
restricted_shell off #如果shell在受限模式下启动就设置这个选项.该值不能被改变.当执行启动文件时不能复位该选项,允许启动文件发现shell是否受限
shift_verbose off #如果该选项设置,当移动计数超出位置参量个数时,shift内置命令将打印一个错误消息
sourcepath on #如果设置source内置命令使用PATH的值来寻找作为参数提供的文件的目录.缺省为打开
syslog_history off
xpg_echo off

#参数
-s 开启指定扩展命令
-u 关闭指定扩展命令
-p 列出所有可设置的选项.

基础示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#示例1.启动关闭扩展 extglob 
shopt -s extglob #启动命令
shopt -u extglob #关闭命令
#开启之后,以下5个模式匹配操作符将被识别(正则表达式):
* ?(pattern-list) - 所给模式匹配0次或1次;
* *(pattern-list) - 所给模式匹配0次以上包括0次;
* +(pattern-list) - 所给模式匹配1次以上包括1次;
* @(pattern-list) - 所给模式仅仅匹配1次;
* !(pattern-list) - 不匹配括号内的所给模式。

rm -rf !(*jpg) #删除文件名不以jpg结尾的文件:
rm -rf *@(jpg|png) #删除文件名以jpg或png结尾的文件,此处@表示限定:
rm -rf file[1-3] #删除当前目录下所有file开头的文件或目录

#示例2.zsh 也支持 - (减号) 这种区间表示,不过需要启用 braceccl 选项。
~$ setopt braceccl
~$ echo {A-Za-z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z


pushd 命令
popd 命令

描述:pushd和popd命令是操作目录栈,改变栈顶元素会切换目录;

  • pushd命令:将元素加入到栈顶
  • popd命令:将栈顶元素删除

什么是目录栈的概念?

存放一个或多个目录,栈中至少要有一个元素。栈顶元素永远是当前目录:使用cd命令切换目录会改变目录栈的栈顶元素,

基础语法:

1
2
3
4
usage: pushd [-n] [+N | -N | dir]  
pushd +N # 将栈内元素循环左移,直到将(从左边数)第N个元素移动到栈顶,由0开始计。
pushd -N # 将栈内元素循环左移,直到将(从右边数)第N个元素移动到栈顶,由0开始计。
pushd -n dir # 将目录入栈,但不改变当前元素,即将目录插入栈中作为第二个元素。 注意-n参数要在目录之前。

实际示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#示例1.将路径或者元素加入栈中
pushd /var/log/journal/
/var/log/journal /usr/bin /etc/apt /etc/ssh /

#示例2.不加参数时,交换栈顶前两个元素
pushd
/usr/bin /var/log/journal /etc/apt /etc/ssh /

#示例2.pushd 命令中的 + (加号) 用于从上往下计数,使用 - (减号) 来从下往上计数;
pushd +1
/var/log/journal /etc/apt /etc/ssh / /usr/bin #将栈顶得元素移动到栈底并进入第二参数目录中
/var/log/journal
pushd +1
/etc/apt /etc/ssh / /usr/bin /var/log/journal
/etc/apt

基础语法:

1
2
3
popd +N  # 删除栈中(从左边数)第N个元素,由0开始计。
popd -N # 删除栈中(从右边数)第N个元素,由0开始计。
pop -n # 不改变当前目录(也就是不改变栈顶元素,操作除了栈顶外栈内其他元素)

实际示例:

1
2
3
4
5
6
7
8
9
10
11
#示例1.执行 popd -1 命令后从目录栈中移除了倒数第二个条目 
popd -1
/etc/ssh /etc/apt /var/log/journal
popd -1
/etc/ssh /var/log/journal #/etc/apt 右边正数第二个参数
/etc/ssh

#示例2.弹栈不切换目录
/etc/ssh /var/log/journal
popd -n
/etc/ssh


dirs 命令

描述:查看现在目录栈元素;

基础示例:

1
2
3
4
5
6
7
8
#示例1.显示当前目录栈元素
dirs
/home /

#示例2.按索引列出当前目录栈元素
dirs -v
0 /home
1 /