0%

Linux 命令行使用技巧

以前看过一部电影 《社交网络》,讲述的是天才程序员扎克伯格在哈佛创建 Facebook 的故事。电影中有一个场景:扎克伯格一边喝着啤酒,一边在自己笔记本电脑上通过一系列 hacking 操作,利用 wget、perl、emacs 等工具收集各个学院里女生的照片放在自己的网站上供大家浏览,这也是 Facebook 的雏形。完成所有工作后,男主吃着零食,淡定地坐在电脑前、监视网站的流量。当时被这个场景深深震撼到了,这才是一名 geeker 应该有的样子。这里是这段视频的链接。

作为一名主要工作在 Linux 系统上的程序员,如何更加高效地使用 Linux 命令行,更加 geek 地操作 Bash 一直是我学习的目标。其实网络上也有很多关于这方面的资源:

本篇文章主要基于 《Command Line Kong Fu》 一书,对其中每个实例都进行了测试,同时也提供更详细的解释。这篇文章也将会不停增长,用于积累学习到的其他技巧。

Let the hacking begin.

Shell 历史

使用 root 账户运行上一个命令

命令

1
2
3
$ sudo !!
或者
$ su -c "!!"

作用

当你运行一个命令失败,失败原因是由于需要 root 权限,此时可以通过以上方式快速使用 root 用户重新运行上一个命令。

例子

1
2
3
4
5
6
7
8
9
10
11
12
$ useradd test
-bash: /usr/sbin/useradd: Permission denied
$ sudo !!
sudo useradd test
$ id test
uid=1002(test) gid=1002(test) groups=1002(test)
$ userdel -r test
-bash: /usr/sbin/userdel: Permission denied
$ sudo !!
sudo userdel -r test
$ id test
id: test: no such user
1
2
3
4
5
6
$ useradd test2
-bash: /usr/sbin/useradd: Permission denied
$ su -c "useradd test2"
Password:
$ id test2
uid=1002(test2) gid=1002(test2) groups=1002(test2)

解释

这里所使用的 感叹号 语法被称为事件指示器。一个事件指示器可以引用你 shell 历史中的一个命令,连续两个感叹号 !! 可以重复最近的一个命令。

重用最近的以给定字符串开头的命令

命令

1
$ !<string>

作用

这是事件指示器的另一个例子,通过运行 !<string> 来运行最近一个以 <string> 字符串开头的命令。字符串的长度可以只有一个字符,也可以有多个字符,只要能够唯一地指定你想要运行的命令即可。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ who
root pts/0 2019-03-28 11:41 (192.168.204.1)
$ w
11:59:20 up 33 min, 1 user, load average: 0.15, 0.09, 0.06
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 192.168.204.1 11:41 0.00s 0.04s 0.01s w
$ !w
w
11:59:26 up 33 min, 1 user, load average: 0.13, 0.09, 0.06
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 192.168.204.1 11:41 6.00s 0.03s 0.00s w
$ !wh
who
root pts/0 2019-03-28 11:41 (192.168.204.1)
1
2
3
4
5
6
7
8
$ ps aux | grep dockerd
root 1569 0.3 7.1 652864 71632 ? Ssl 11:26 0:07 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
fuchenc+ 7932 0.0 0.0 112708 976 pts/0 R+ 12:03 0:00 grep --color=auto dockerd
$ sudo service docker stop
Redirecting to /bin/systemctl stop docker.service
$ !p
ps aux | grep dockerd
fuchenc+ 8147 0.0 0.0 112708 976 pts/0 S+ 12:04 0:00 grep --color=auto dockerd

解释

这也是事件指示器的一种用法。

重用上一个命令的第二个单词(第一个参数)

命令

1
$ !^

作用

如果需要重用上一个命令的第二个单词(也就是第一个参数),可以使用 !^ 这种单词指示器。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ host www.baidu.com 8.8.8.8
Using domain server:
Name: 8.8.8.8
Address: 8.8.8.8#53
Aliases:

www.baidu.com is an alias for www.a.shifen.com.
www.a.shifen.com is an alias for www.wshifen.com.
www.wshifen.com has address 103.235.46.39

$ ping -c 1 !^
ping -c 1 www.baidu.com
PING www.wshifen.com (103.235.46.39) 56(84) bytes of data.
64 bytes from 103.235.46.39 (103.235.46.39): icmp_seq=1 ttl=128 time=73.2 ms

--- www.wshifen.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 73.263/73.263/73.263/0.000 ms

解释

命令行中,无论在哪里使用 !^,它都将会被替换为上一个命令的第二个单词。

重用上一个命令的最后一个单词(最后一个参数)

命令

1
$ !$

作用

如果需要重用上一个命令的最后一个单词(也就是最后一个参数),可以使用 !$ 这种单词指示器。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ls -l /etc/networks
-rw-r--r--. 1 root root 58 Apr 11 2018 /etc/networks

$ cat !$
cat /etc/networks
default 0.0.0.0
loopback 127.0.0.0
link-local 169.254.0.0
total 0

$ du -sh !$
du -sh /home/fuchencong/
20K /home/fuchencong/

解释

命令行中,无论在哪里使用 !$,它都将会被替换为上一个命令的最后一个单词。

重用以前某个命令的第 N 个单词

命令

1
2
$ !!:N
$ <event_designator>:<number>

作用

!!:N 重用最近一个命令的第 N 个单词,N 是你想获取的单词计数。第一个单词计数为 0,第二个单词计数为 1,依次类推。所以可以认为 0 代表命令本身,1 代表命令的第一个参数,依此类推。

<event_designator>:<number> 重用以 <event_designator> 事件指示器所指定命令的第 N 个单词。

例子

1
2
3
4
5
6
7
8
$ ls -l file.1 file.2 file.3
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 28 14:25 file.1
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 28 14:25 file.2
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 28 14:25 file.3
$ mv !!:2 dir.1
mv file.1 dir.1
$ mv !ls:3 dir.2
mv file.2 dir.2

解释

可以认为,!!<event_designator> 都用于引用相应的命令,而 !<number> 用于引用该命令中第 number 个单词。

重复上一个命令,同时替换某个字符串

命令

1
$ ^<string1>^<string2>^

作用

该命令可以快速修正上一个命令中的拼写错误,从而快速更正上一个命令。如果省略 ^<string2>,则 <string1> 将会从命令中移除。默认,只有命令中的第一个 <string1> 会被替换,如果想替换该命令中的所有 <string1>,则在上述命令末尾加入 :&

另外,在不使用 :& 时,结尾的 ^ 也可以省略。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ grpe fuchencong /etc/passwd
-bash: grpe: command not found
$ ^pe^ep
grep fuchencong /etc/passwd
fuchencong:x:1001:1001::/home/fuchencong:/bin/bash

$ grep rooty /etc/passwd
$ ^y
grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
$ ls -l dir_1 dir_2
ls: cannot access dir_1: No such file or directory
ls: cannot access dir_2: No such file or directory

$ ^_^.^:&
ls -l dir.1 dir.2
dir.1:
total 0
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 28 14:25 file.1

dir.2:
total 0
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 28 14:25 file.2

解释

通过这种方式可以快速更正上一个命令的细微错误。

引用当前命令的某个单词

命令

1
$ !#:N

作用

上述方式引用当前命令的第 N 个单词。同样单词计数从 0 开始,第一个单词计数为 0,第二个单词计数为 1,依此类推。

例子

1
2
3
4
$ mv test.file new!#:1
mv test.file newtest.file
$ ls newtest.file
newtest.file

解释

可以认为 !# 事件指示器代表当前命令行,:N 单词指示器指定了第 N 个单词,所以该命令用于引用当前命令的第 N 个单词。

保存当前命令行会话的副本

命令

1
$ script

作用

如果想将当前命令行会话的所有内容保存为文档,可以使用 script 命令。

例子

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
$ script
Script started, file is typescript
$ ls -l
total 0
drwxrwxr-x. 2 fuchencong fuchencong 20 Mar 28 14:27 dir.1
drwxrwxr-x. 2 fuchencong fuchencong 20 Mar 28 14:29 dir.2
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 28 14:25 file.3
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 29 11:29 newtest.file
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 29 11:36 typescript
$ rm -rf file.3
$ echo "this is a test for script command"
this is a test for script command
$ exit
exit
Script done, file is typescript

$ cat typescript
Script started on Fri 29 Mar 2019 11:36:53 AM CST
$ ls -l
total 0
drwxrwxr-x. 2 fuchencong fuchencong 20 Mar 28 14:27 dir.1
drwxrwxr-x. 2 fuchencong fuchencong 20 Mar 28 14:29 dir.2
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 28 14:25 file.3
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 29 11:29 newtest.file
-rw-rw-r--. 1 fuchencong fuchencong 0 Mar 29 11:36 typescript
$ rm -rf file.3
$ echo "this is a test for script command"
this is a test for script command
$ exit
exit
Script done on Fri 29 Mar 2019 11:37:26 AM CST

解释

script 命令会捕获你终端中打印的一切内容,并将其保存为文档。可以提供一个文件名作为参数,否则默认的保存文件名为 typescript。

找出最常用的命令

命令

1
$ history | awk '{print $2}' | sort | uniq -c | sort -rn | head

作用

从 shell 命令历史中获取最常用的 10 个命令。

例子

1
2
3
4
5
6
7
8
9
10
11
$ history | awk '{print $2}' | sort | uniq -c | sort -rn | head
37 ls
12 sudo
9 history
8 cd
7 mv
6 ps
6 man
6 exit
5 mkdir
3 yum

解释

history 用于获取命令历史,通过 awk 截取执行的命令。然后通过 sort 进行命令字符串排序,排序完之后就可以通过 uniq 命令去重,同时输出每个命令出现的计数,之后使用 sort 根据计数值的大小进行排序,最后通过 head 命令输出前 10 个计数值最大的命令。

清空命令行历史

命令

1
$ history -c

作用

1
使用 history 命令的 -c 选项来清除命令历史。

例子

1
2
3
4
5
6
7
8
9
$ history | head -n 5
1 cd ~
2 ls
3 useradd test
4 sudo useradd test
5 LS
$ history -c
$ history
1 history

解释

使用 history -c 来清空当前 bash 命令历史。

文本处理和操作

去除注释和空白行

命令

1
$ grep -E -v "^#|^$" file

作用

该命令可以只显示 file 文件的非空白行和非注释行(这里的注释行指的是以 # 开头的行)。

例子

1
2
3
4
5
6
$ cat test.file
this is a test fille

# this is a comment line

enf of file.
1
2
3
$ grep -E -v "^#|^$" test.file
this is a test fille
enf of file.

解释

使用 grep 命令进行文本匹配,-E 选项支持使用正则表达式匹配,-v 进行反向匹配,即输出不符合匹配模式的文本行。正则表达式 ^#|^$ 匹配两种文本行:^# 匹配以 # 开头的文本行,^$ 匹配空白行。

使用 Vim 跨网络编辑文件

命令

1
2
$ vim scp://remote-host//path/to/file
$ vim scp://remote-user@remote-host//path/to/file

作用

该命令可以使用 vim 编辑远程主机 remote-host 上的指定文件,第二个命令除了指定远程主机 remote-host,还指定了登录用户为 remote-user,如果不指定用户名,则默认为当前执行该命令的用户。

例子

1
fchencong@contrail-ubm-srx-re3:~$ vim scp://cnrd-ngsrx-build08/~/test.file

解释

scp 指定了跨主机通信的协议。

以表格形式输出内容

命令

1
2
$ alias ct='column -t'
$ command | ct

作用

使用 column -t 对 command 命令的输出进行格式化,以表格的形式进行输出。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ echo -e "one two\nthree four"
one two
three four
$ echo -e "one two\nthree four" | ct
one two
three four
$ mount -t tmpfs
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel)
tmpfs on /run type tmpfs (rw,nosuid,nodev,seclabel,mode=755)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,seclabel,mode=755)
$ mount -t tmpfs | ct
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel)
tmpfs on /run type tmpfs (rw,nosuid,nodev,seclabel,mode=755)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,seclabel,mode=755)

解释

column 命令用于将输入格式化为多列,-t 选项根据输入内容的列数自动将输入内容格式化为表格形式进行输出。为了便于使用,使用 alias 创建一个命令别名,使用 ct 代替 columnn -t,之后直接使用 ct 对 command 命令的输出进行表格化。

获取每一行的最后一个单词

命令

1
2
$ awk '{print $NF}' file
$ cat file | awk '{print $NF}'

作用

以上方式可以获取 file 文件中每一行的最后一个单词。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ awk -F : '{print $NF}' /etc/passwd | sort -u
/bin/bash
/bin/sync
/sbin/halt
/sbin/nologin
$ awk -F : '{print $1,$NF}' /etc/passwd | sort | column -t
abrt /sbin/nologin
adm /sbin/nologin
bin /sbin/nologin
daemon /sbin/nologin
dbus /sbin/nologin
ftp /sbin/nologin
fuchencong /bin/bash
......

解释

使用 awk 命令的 print 来输出每一行的指定 filed,通过 $FIELD_NUMBER 的方式来指定 field。例如,通过 $1 的方式指定第一列单词,$2 指定第二列,以此类推,使用 $NF 来指定最后一列。默认 awk 使用空格符来划分 field,可以使用 -F 选项指定分隔符字段。

使用 less 命令时获得彩色输出

命令

1
2
$ ls --color=always | less -R
$ grep --color=always | less -R

作用

通过以上方式,即使在使用 less 查看 ls 或 grep 命令的输出时,也可以获得彩色输出形式。

例子

1
2
$ grep color color.txt | less
$ grep --color=always color color.txt | less -R

解释

一些 Linux 发行版本会为 ls 和 grep 命令创建命令别名,使其总是设置 —color=auto 选项,这导致只有在终端输出结果时才具有颜色高亮,当 ls 或 grep 的结果传递给管道时则省略掉颜色控制符。在我的 CentOS 7 上就是这样:

1
2
3
4
$ type ls
ls is aliased to `ls --color=auto'
$ type grep
grep is aliased to `grep --color=auto'

通过使用 —color=always 的方式强制总是输出颜色控制字符。最后通过 less 的 -R 选项根据原始的颜色控制符显示颜色。

通过管道使用 grep 时保留色彩

命令

1
$ ls -l --color=always | grep --color=nerver string

作用

通过上述方式,使用 grep 过滤 ls 命令的结果时,仍然能够保留 ls 命令的颜色高亮,而不是使用 grep 自己的颜色高亮机制。

例子

1
$ ls -l --color=always /etc | grep --color=never system

解释

在使用 ls 命令时,通过指定 —color=always 选项,可以将颜色控制符输出到管道中。而 grep 的默认颜色高亮机制会对匹配字符串进行高亮,通过使用 —color=never 机制忽略掉 grep 本色的颜色高亮机制,从而使用 ls 的颜色控制字符。

使用 sudo 用户权限向文件中添加内容

命令

1
$ echo text | sudo tee -a file

作用

通过上述方式,可以以 sudo 用户权限向 file 中添加内容。

例子

1
2
3
4
5
6
$ sudo echo "PRODUCTION Environment" >> /etc/motd
-bash: /etc/motd: Permission denied
$ echo "PRODUCTION Environment" | sudo tee -a /etc/motd
PRODUCTION Environment
$ tail -n 1 /etc/motd
PRODUCTION Environment

解释

如果想以其他用户的权限向 file 文件添加内容,sudo echo text >> file 的方式无法工作。因为该命令中,echo 命令将以 root 权限执行,而不是重定向符。所以通过 sudo tee 的方式将以 root 权限执行 tee 命令。tee 命令读取标准输入,并同时输出到标准输出和文件。-a 选项将以添加方式修改文件,而不是默认的覆写方式。

修改字符串的大小写

命令

1
$ tr [:lower:] [:upper]

作用

第一种方式可以将字符串中的大写字符替换为小写。第二种方式可以将字符串中的小写字符替换为大写。

例子

1
2
3
4
$ echo "this is a TEST String" | tr [:upper:] [:lower:]
this is a test string
$ echo "this is a TEST String" | tr [:lower:] [:upper:]
THIS IS A TEST STRING

解释

tr 命令用于替换字符串中的字符,[:upper:] 代表所有的大写字符, [:lower:] 代表所有的小写字符。除此之外。还可以用 A-Z 代表所有大写字符,a-z 代表所有小写字符。

以可读方式显示你的命令搜索路径(PATH 环境变量)

命令

1
$ echo $PATH | tr ':' '\n'

作用

通过上述方式,可以以更加可读的方式查看 PATH 环境变量的值。

例子

1
2
3
4
5
6
7
8
9
10
$ echo $PATH | tr ':' '\n'
/opt/rh/rh-ruby23/root/usr/local/bin
/opt/rh/rh-ruby23/root/usr/bin
/usr/local/bin
/bin
/usr/bin
/usr/local/sbin
/usr/sbin
/home/fuchencong/.local/bin
/home/fuchencong/bin

解释

PATH 环境变量中的路径字符串之间以 : 分割,这种方式不利于人类阅读。通过使用 tr 命令,将 : 替换为换行符,从而方便我们查看。

不使用编辑器创建一个文本文件

命令

1
2
3
$ cat > output.file
.......
<ctrl-d>

作用

通过上述方式,可以创建 output.file 文件,而文件内容则为你在标准输入中输入的内容,通过输入文件结束符 <ctrl-d> 结束输入。

例子

1
2
3
4
5
6
7
8
9
$ cat > shopping.list
milk
sugar
eggs
$
$ cat shopping.list
milk
sugar
eggs

解释

cat 命令是 concatenate 的简写,即 cat 命令可用于连接文件或标准输入的内容,并将其输出到标准输出。这里通过 cat 命令以及重定向操作符将标准输入的内容重定向到指定文件,从而把输入内容添加都新创建的文件中,而无需使用编辑器。

显示两个字符串之间的文本块

命令

1
$ awk '/start-pattern/, /stop-pattern/' file.txt

作用

该命令可以显示 file.txt 文件中 start-patternstop-pattern 之间的文本块。

例子

1
2
3
4
5
6
7
8
9
10
11
12
$ cat test.file
paragraph 1 start
this is contents of paragraph 1.
paragraph 1 end

paragraph 2 start
this is contents of paragraph 2.
paragraph 2 end

paragraph 3 start
this is contents of paragraph 3.
paragraph 3 end
1
2
3
4
$ awk '/paragraph 2 start/, /paragraph 2 end/' test.file
paragraph 2 start
this is contents of paragraph 2.
paragraph 2 end

解释

grep 命令适合单行文本上的过滤,如果想要获取一段文本块,可以使用 awk 的模式匹配进行实现,通过上述方式,可以显示 start-patternstop-pattern 之间的文本块。模式可以是简单的字符串,也可以是正则表达式。

删除两个字符串之间的文本

命令

1
sed '/start-pattern/, /stop-pattern/d' file

作用

通过上述命令,可以删除 file 文件中以 start-pattern 模式到 stop-pattern 模式之间的文本快。

例子

1
2
3
4
5
6
7
8
9
10
11
12
$ cat test.file
paragraph 1 start
this is contents of paragraph 1.
paragraph 1 end

paragraph 2 start
this is contents of paragraph 2.
paragraph 2 end

paragraph 3 start
this is contents of paragraph 3.
paragraph 3 end
1
2
3
4
5
$ sed '/paragraph 1 start/, /paragraph 2 end/d' test.file

paragraph 3 start
this is contents of paragraph 3.
paragraph 3 end

解释

该命令和上一个命令类似,都是对一段文本进行操作,这里通过 sed 的模式匹配查找以 start-patternstop-pattern 之间的文本块,并对其执行删除动作(d)。

通过 alias 解决常见拼写错误

命令

1
$ alias grpe="grep"

作用

如果在敲命令时通常犯同一个拼写错误,可以使用 alias 将拼写错误直接定义为正确命令的一个别名。这样下次即使拼写错误,也可以正常使用该命令。上面命令将 grpe 定义为 grep 的别名。

例子

1
2
3
4
5
6
$ grpe root /etc/passwd
-bash: grpe: command not found
$ alias grpe="grep"
$ grpe root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

解释

可以将该 alias 写入你的 bash 配置文件,这样该 alias 将会永久生效。只要你愿意,还可以为其他容易出现拼写错误的命令创建别名。

对输出的主体内容进行排序,同时保留第一行的表头

命令

1
2
3
4
5
body(){
IFS=read -r header
printf '%s\n' "$header"
"$@"
}

作用

有时我们需要对包含表头的一段内容进行排序,此时我们希望只对主体内容进行排序,而仍然保持表头(第一行内容)原样地输出,可以通过上述函数实现该功能。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ df -h | sort -k 5
devtmpfs 476M 0 476M 0% /dev
tmpfs 488M 0 488M 0% /dev/shm
tmpfs 98M 0 98M 0% /run/user/0
tmpfs 488M 0 488M 0% /sys/fs/cgroup
/dev/sda1 1014M 130M 885M 13% /boot
tmpfs 488M 7.7M 480M 2% /run
/dev/mapper/centos-root 17G 9.4G 7.7G 55% /
Filesystem Size Used Avail Use% Mounted on

$ df -h | body sort -k 5 -n
Filesystem Size Used Avail Use% Mounted on
devtmpfs 476M 0 476M 0% /dev
tmpfs 488M 0 488M 0% /dev/shm
tmpfs 488M 0 488M 0% /sys/fs/cgroup
tmpfs 98M 0 98M 0% /run/user/0
tmpfs 488M 7.7M 480M 2% /run
/dev/sda1 1014M 130M 885M 13% /boot
/dev/mapper/centos-root 17G 9.4G 7.7G 55% /
1
2
3
4
5
6
7
8
9
10
11
12
13
$ ps -eo pid,%cpu,cmd | sort -nrk 2 | head -n 5
788 0.1 /usr/bin/vmtoolsd
PID %CPU CMD
978 0.0 /usr/sbin/NetworkManager --no-daemon
9 0.0 [rcu_sched]
89 0.0 [kauditd]

$ ps -eo pid,%cpu,cmd | body sort -nrk 2 | head -n 5
PID %CPU CMD
788 0.1 /usr/bin/vmtoolsd
978 0.0 /usr/sbin/NetworkManager --no-daemon
9 0.0 [rcu_sched]
89 0.0 [kauditd]

解释

body 是定义的一个 bash 函数,在该函数中首先通过 read 命令读取输入的第一行(即表头),保存到 header 变量中,然后通过 printf 输出该行内容。完成表头的处理后,再执行原始的命令(通过 $@ 实现)对剩余的输入内容进行处理。

从字符串中移除某个或某些字符

命令

1
2
$ cat file | tr -d "X"
$ cat file | tr -d [set]

作用

通过以上方式,可以从输出的字符串中删除某个特定字符或者某些字符。

例子

1
2
3
4
5
6
7
8
9
10
11
12
$ cat test.file
"this is a test file."
"use tr command, you can delete characters."
"end of file."
$ cat test.file | tr -d '"'
this is a test file.
use tr command, you can delete characters.
end of file.
$ cat test.file | tr -d '",.'
this is a test file
use tr command you can delete characters
end of file

解释

1
tr 命令通常用于替换字符,但是通过 -d 选项,tr 命令也可以用于删除字符。

统计一个字符串出现的次数

命令

1
$ command | uniq -c

作用

uniq 命令可以去除重复的行,通过 -c 选项,可以输出重复行的计数。但是需要注意,uniq 命令并不会首先检查整个文件的内容,而是只对邻接的行进行去重,所以通常 uniq 命令会配合 sort 命令使用。

例子

1
2
3
4
5
6
$ cat /etc/passwd | awk -F : '{print $NF}' | sort | uniq -c | sort -nr
19 /sbin/nologin
4 /bin/bash
1 /sbin/shutdown
1 /sbin/halt
1 /bin/sync

解释

以上命令首先获取 /etc/passwd/ 所有的登录 shell,通过 sort 命令首先进行排序,之后才能用 uniq -c 统计所有登录 shell 出现的次数,最后调用 sort -nr 根据出现次数进行逆序输出。

网络和 SSH

通过 Web 接口共享当前目录下的文件

命令

1
2
$ python -m SimpleHTTPServer
$ python3 -m http.server

作用

通过以上命令,可以通过 Web 方式共享当前目录下的文件。也就是说,其他用户通过浏览器访问你主机 IP 的 8000 端口,就可以访问你当前目录下的文件。

例子

1
2
$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

解释

以上命令会启动 Python 内置的一个 Web 服务器,并默认在 8000 端口上进行监听(可以在命令的最后指定监听的端口)。如果当前目录下没有 index.html 文件,那么访问 http://HostIP:8000/ 将会列出当前目录的目录结构。所以其他用户可以通过访问该 URL 来浏览你目录下的文件。

通过 SSH 在你的主机上挂载远程服务器上的一个目录

命令

1
2
$ `sshfs remote-host:/directory mountpoint`
$ `fusermount -u mountpoint`

作用

第一个命令用于挂载远程服务器上的目录,第二个命令用于取消挂载。 通过以上方式,可以在你的主机上挂载远程服务器的一个目录,这样就可以像操作本地目录一样操作远程服务器上的文件。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ mkdir build_server
$ ls -l build_server/
total 0
$ sshfs fchencong@contrail-ubm-srx-re3.ccp.juniper.net://homes/fchencong build_server/
fuchencong@localhost ~]$ ls -l build_server/
total 584
drwxr-xr-x. 1 45444 935 4096 Mar 22 10:54 bin
drwxr-xr-x. 1 45444 935 4096 Oct 18 18:46 build
-rw-r--r--. 1 45444 935 572736 Nov 7 10:46 compare.tar.gz
-rwx------. 1 45060 707 1675 Nov 23 15:18 id_rsa
drwxr-xr-x. 1 45444 935 4096 Mar 22 10:53 pr
drwxr-xr-x. 1 45444 935 4096 Mar 20 10:39 private_image
drwxrwxrwx. 1 45444 935 4096 Feb 15 11:48 temp
$ fusermount -u build_server/
$ ls -l build_server/
total 0

解释

类似于 ssh 命令,可以使用 user@host 的形式指定登录远程服务器的用户名,如果不指定用户名,则默认为当前用户名。如果没有指定远程服务器的挂载目录,则默认为登录用户的 home 目录。

使用 curl 从命令行获取你的公有 IP

命令

1
$ curl ifconfig.me

作用

通常我们在局域网内使用私有 IP,在访问公网时通过 NAT 的方式获得一个公网 IP,使用该公网 IP 访问互连网的世界。可以通过以上方式,查取自己的公共 IP 地址。

例子

1
2
3
4
5
6
$ curl ifconfig.me
116.197.188.11
$ curl ifconfig.me/ip
116.197.188.11
$ curl ifconfig.me/host
unavailable

解释

ifconfig.me 其实是个网站,通过 curl 命令访问该网站获取自己的 IP 信息。

使用 SSH 免密码登录远端主机

命令

1
2
3
$ ssh-keygen
$ ssh-copy-id remote-host
$ ssh remote-host

作用

通过以上方式,可以免密码地使用 SSH 登录到远端主机。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/fuchencong/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/fuchencong/.ssh/id_rsa.
Your public key has been saved in /home/fuchencong/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:m1EQokepaYMU+C+EP9Hsei2uCwgFUAJ2w0lZdIgWhJg fuchencong@localhost.localdomain
The key's randomart image is:
+---[RSA 2048]----+
|O=BBBo+oo. |
|E+o*.+o. . |
| +o+.o. . |
|..= B. . |
|.o = . S |
|o + o + |
|o + . o |
| .. o . |
| o=.. |
+----[SHA256]-----+
1
2
3
4
5
6
7
8
9
10
$ ssh-copy-id fchencong@XXX
/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/fuchencong/.ssh/id_rsa.pub"
/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
fchencong@XXX's password:

Number of key(s) added: 1

Now try logging into the machine, with: "ssh 'fchencong@contrail-ubm-srx-re3.ccp.juniper.net'"
and check to make sure that only the key(s) you wanted were added.

解释

为了免密码登录到远端主机,需要生成一对 SSH 密码对,包含一个公钥和一个私钥。ssh-keygen 完成以上工作,生成秘钥时可以对秘钥设置密码,如果无需指定密码,直接输入回车键即可。

在远端主机上,需要在其 ~/.ssh/authorized_keys 包含你的公钥,通过 ssh-copy-id 可以实现这一工作。

显示打开的网络连接

命令

1
$ sudo lsof -Pni

作用

该命令可以显示所有打开的网络连接。

例子

1
2
3
4
5
6
7
8
9
$ sudo lsof -Pni
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 1555 root 3u IPv4 32748 0t0 TCP *:22 (LISTEN)
sshd 1555 root 4u IPv6 32771 0t0 TCP *:22 (LISTEN)
master 1719 root 13u IPv4 33537 0t0 TCP 127.0.0.1:25 (LISTEN)
master 1719 root 14u IPv6 33538 0t0 TCP [::1]:25 (LISTEN)
sshd 4650 root 3u IPv4 51061 0t0 TCP 192.168.204.100:22->192.168.204.1:51935 (ESTABLISHED)
sshd 11180 root 3u IPv4 905674 0t0 TCP 192.168.204.100:22->192.168.204.1:53496 (ESTABLISHED)
python 11668 fuchencong 3u IPv4 930470 0t0 TCP *:8000 (LISTEN)

解释

lsof 命令是 list open files 的简写,lsof 命令不仅可以用于显示打开的文件,还包括打开的端口以及网络连接。其中,-P 选项防止将端口号转化为服务名,-n 选项防止将 IP 地址转化为主机名,-i 选型告诉 lsof 列出所有的网络连接。

比较远端和本地文件的差异

命令

1
$ ssh remote-host cat /path/to/remotefile | diff /path/to/localfile -

作用

通过以上方式,可以比较远端主机上某个文件和本地文件系统中某个文件的差异。

例子

1
2
3
4
5
6
7
ssh cnrd-ngsrx-build08 cat /etc/passwd | diff /etc/passwd -
1c1
< root:x:0:0:vsrx_yocto_ubuntu12:/root:/bin/bash
---
> root:x:0:0:jbm_ubuntu12:/root:/bin/bash
17d16
......

解释

通过 ssh 连接到远端主机,并使用 cat filepath 的方式获取远端主机上某个文件的内容,然后通过管道传递给 diff 命令。diff 命令用于比较文件差异,在这个例子中,第一个参数是本地文件,第二个参数 - 用于表示标准输入,本例中也就是管道传入的内容。

从命令行中发送邮件

命令

1
2
$ mail recipient@domain.com
$ echo "message" | mail -s 'subject' recipient@domain.com

作用

通过 mail 命令可以从命令行给指定邮箱发送邮件。

例子

1
$ echo "this mail is from command line" | mail -s "Test mail" fchencong@juniper.net

解释

1
可以从标准输入键入你的邮件内容(通过 Ctrl-d 结束输入)或者通过管道指定邮件内容,-s 选项可以指定邮件主题。

从命令行发送邮件附件

命令

1
$ mail -a /path/to/attachment recipient@domain.com

作用

通过上述命令,可以在命令行中向指定邮箱发送附件。

例子

1
2
3
4
5
6
7
8
9
$ script howto.txt
Script started, file is howto.txt
$ ls
howto.txt
$ touch new.file
$ exit
exit
Script done, file is howto.txt
$ echo "FYI" | mail -a howto.txt -s "how to create an empty file on Linux" fchencong@juniper.net

解释

上面例子展示了一个我们经常会碰到的流程:如果我们需要告诉别人通过哪些命令完成某个任务,我们先在自己的 shell 上尝试,然后再将相应的命令发送给他人。这里首先使用 script 命令录制我们的尝试过程,并将其保存到 howto 这个文件中,最后通过 mail 命令 的 -a 选项发送该附件。

一气呵成,十分 geek。

创建 SSH 隧道访问远端资源

命令

1
$ ssh -N -L local-port:host:remote-port remote-host

作用

以上命令可以创建一个 SSH 隧道,-L 选项告诉 SSH 创建一个隧道,将会在本地主机打开第一个端口 local-port ,到该端口的所有连接都会通过隧道到达 remote-host,由 remote-host 将这些连接发送到 -L 选项中指定的 host:remote-port。-N 选项告诉 SSH 不要在远程主机上执行命令。

假设你有一个 Web 网站无法通过因特网访问,但是可以通过一个 server 访问该网站,同时你对该 server 有 SSH 权限。你可以创建一个隧道来访问该网站,就好像你在 Server 上访问该网站一样。

例子

1
2
$ ssh -N -L 8000:intranet.acme.com:80 jump-server &
$ curl http://localhost:8000

解释

通过以上方式,所以访问本地主机 8000 端口的连接都会通过隧道到达 jump-server,由 jumper-server 将这些连接发送给 intranet.acme.com 的 80 端口。所以访问本地主机的 8000 端口,其效果就等同于访问 intranet.acme.com 的 80 端口。

SSH 隧道的另一种用法是访问在服务器上运行的某种服务,而该服务器你有 SSH 权限。你可以在本地创建一个到该服务器的 SSH 隧道,之后通过访问本地的端口,就可以访问服务器上的相应服务。这个举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
fchencong@contrail-ubm-srx-re3:~$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

▶ ssh -N -L 8000:localhost:8000 contrail-ubm-srx-re3.ccp.juniper.net
▶ curl http://localhost:8000/
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for /</title>
<body>
<h2>Directory listing for /</h2>
<hr>
<ul>
<li><a href=".bash_history">.bash_history</a>
<li><a href=".bash_profile">.bash_profile</a>

在该例子中,首先在服务器上启动一个 HTTP sever,监听端口为 8000。为了在我的 Mac 客户端上访问该 HTTP 服务,在我的 MAC 客户端上通过 ssh -N -L 8000:localhost:8000 contrail-ubm-srx-re3.ccp.juniper.net 创建了一个 SSH 隧道,这个隧道的含义是:在 Mac 客户端访问 8000 端口的连接将会通过隧道到达 contrail-ubm-srx-re3.ccp.juniper.net,由其将连接发送给 localhost:8000,这里的 localhost 是相对于 contrail-ubm-srx-re3.ccp.juniper.net ,也就是服务器本身。

查找程序监听的端口

命令

1
$ netstat -nutlp

作用

通过以上方式,可以列出所有正在监听 tcp/udp 端口的程序,以及所对应的监听端口

例子

1
2
3
4
5
6
7
8
$ sudo netstat -nutlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 12519/python
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1555/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1719/master
tcp6 0 0 :::22 :::* LISTEN 1555/sshd
tcp6 0 0 ::1:25 :::* LISTEN 1719/master

解释

netstat 命令可以用于显示网络连接,在该命令中,-n 选项显示数字形式的地址,而不是符号形式,-u 选项用于包括 UDP 协议,-t 选项用于包括 TCP 协议,-l 选项用于列出正在监听的 socket,-p 选项打印出 pid 以及进程名。

对某一远程主机使用不同的 SSH key

命令

1
$ ssh -i ~/.ssh/id_rsa-remote-host

或者在 ~/.ssh/config 中添加如下配置:

1
2
Host remote-host
IdentityFile ~/.ssh/id_ras-remote-host

作用

如果针对不同的主机,你想使用不同的 SSH key,可以通过如上两种方式实现。第一种方式通过 ssh 的 -i 选项来显示指定 key。如果你不想每次都指定秘钥文件,可以在 ~/.ssh/config 中添加配置(即第二种方式)。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat ~/.ssh/config
Host db1.example.com
IdentityFile ~/.ssh/id_rsa-db1

$ cat ~/.ssh/config
Host db*
IdentityFile ~/.ssh/id_rsa-db1
Host *.work.net
IdentityFile ~/work-files/keys/id_rsa

$ cat ~/.ssh/config
Host *.example.com
IdentityFile ~/.ssh/id_rsa-%h

解释

1
在 `~/.ssh/config` 配置文件中,允许使用通配符。另外,%h 可以用于表示主机名,这样可以简化你的配置文件:不用每一个主机都添加一个配置项,%h 将自动扩展为相应 Host 中相应的主机名。

使用 SSH 建立连接时避免输入用户名

命令

在 ~/.ssh/config 中添加如下配置:

1
2
Host remote-host
User username

作用

如果在本地 Linux 主机上的用户名和远程服务器上的用户名不相同,在每次建立 SSH 连接时可以在主机名前通过 user@host 的方式显式指定登录用户名。但是为了避免每次都输入 username,可以在你的 ~/.ssh/config 文件中加入上述配置。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
whoami
fchencong
~
▶ ssh 192.168.204.100
fuchencong@192.168.204.100's password:
Last login: Mon Apr 1 14:22:37 2019 from 192.168.204.1
$ whoami
fuchencong
$ exit
logout
Connection to 192.168.204.100 closed.
▶ cat ~/.ssh/config
Host 192.168.204.100
User fuchencong

解释

通过在 ~/.ssh/config 指定 User,这样即使本地主机的用户名和远程登录的用户名不相同,也无需每次都显式指定用户名。

简化多跳 SSH 连接

命令

在 ~/.ssh/config 文件中添加如下配置:

1
2
3
4
Host jumphost.example.com
ProxyCommand none
Host *.example.com
ProxyCommand ssh -W %h:%p jumphost.example.com

作用

如果你想访问位于中转服务器之后的主机,可以通过以上方式简化你的 SSH 连接建立过程。以上配置将告诉 SSH 自动通过中转服务器连接到目的主机。在你的本地主机上,直接输入 ssh destination-host 即可连接到目的主机,而无需首先连接到中转服务器,再由中转服务器连接到目的主机。

例子

1
2
3
4
5
6
7
8
9
▶ ssh fchencong@cnrd-ngsrx-build07
^C
cat ~/.ssh/config
Host cnrd-ngsrx-build07
ProxyCommand ssh -W %h:%p contrail-ubm-srx-re3.ccp.juniper.net
▶ ssh fchencong@cnrd-ngsrx-build07
fchencong@contrail-ubm-srx-re3.ccp.juniper.net's password:
fchencong@cnrd-ngsrx-build07's password:
fchencong@cnrd-ngsrx-build07:~$

解释

在上面这个例子中,我的 Mac 客户端无法直接访问 cnrd-ngsrx-build07,之后配置 SSH 代理,通过 contrail-ubm-srx-re3.ccp.juniper.net 中转到目的地,之后就可以直接在 Mac 客户端连接到最终的目的主机了。

SSH 会话断开重连后,继续之前的工作

命令

1
2
3
4
5
6
$ ssh remote-host
$ sreen
ctrl-a,d
$ exit
$ ssh remote-host
$ sreen -r

作用

当你需要在远程主机上执行一个长时间任务,为了避免由于网络连接中断而导致该任务中断,可以启动给一个 sreen 会话,在该 screen 会话中执行该长时间任务,之后即使你 detach 该 screen 会话,断开 SSH 连接,该任务仍然将继续执行。当你重新建立 SSH 连接并恢复该 screen 会话,还可以看到中间的执行结果。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
▶ ssh 192.168.204.100
fuchencong@192.168.204.100's password:
Last login: Mon Apr 1 14:34:41 2019 from 192.168.204.1
$ screen

$ while : ; do let var=var+1; echo "var=$var"; sleep 5; done
var=1
var=2
[detached from 12820.pts-2.localhost]
$ exit
logout
Connection to 192.168.204.100 closed.
▶ ssh 192.168.204.100
fuchencong@192.168.204.100's password:
Last login: Mon Apr 1 17:08:00 2019 from 192.168.204.1
$ screen -r
$ while : ; do let var=var+1; echo "var=$var"; sleep 5; done
var=1
var=2
var=3
var=4
var=5

解释

在该例子中,通过 SSH 连接到主机后,使用 screen 命令创建一个 screen 会话,在该 screen 会话中执行一个 while 循环命令,然后通过 ctrl-a d detach 该 screen 会话,这样即使之后断开 SSH 连接并重新连上,通过 screen -r 恢复该 screen 会话,命令始终在运行,且能看到中间的结果。如果中间结果超过了一个屏幕,你可以通过 ctrl-a ESC 的方式查看之前的结果,输入该命令后使用 vi 移动命令在输出结果中移动,最后输入 ESC 可以退出查看结果的模式。输入 exit 可以退出 screen 会话。

screen 是最常用的屏幕复用器,还有其它类似的工具:tmux,dtach,以及 byobu。

配置 SSH 基于模式在主机名后添加域名

命令

在 ~/.ssh/config 文件中添加如下配置:

1
2
Host host-prefix* !*.domain.com
HostName %h.domain.com

作用

有时在 SSH 某个主机时,为了避免输入完整的域名,一种方式是配置域名解析配置文件 /etc/resolv.conf,但是这种方式有一些限制。另一种方式就是配置 SSH 进行解析,在该配置中创建一个规则,对于所有以某个模式为前缀的主机名,都添加指定的域名,另外,对于那些已经有该域名的主机名,则不需要再添加域名。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
▶ ssh contrail-ubm-srx-re3
ssh: Could not resolve hostname contrail-ubm-srx-re3: nodename nor servname provided, or not known

cat ~/.ssh/config
Host contrail* !*.ccp.juniper.net
HostName %h.ccp.juniper.net

▶ ssh contrail-ubm-srx-re3
fchencong@contrail-ubm-srx-re3.ccp.juniper.net's password:
fchencong@contrail-ubm-srx-re3:~$
fchencong@contrail-ubm-srx-re3:~$ hostname -f
contrail-ubm-srx-re3.svec.juniper.net
fchencong@contrail-ubm-srx-re3:~$ exit
logout
Connection to contrail-ubm-srx-re3.ccp.juniper.net closed.

解释

在这个例子中,直接输入不完全主机名,无法 SSH 成功,之后配置 ~/.ssh/config 文件,添加一个域名解析规则,只要是以 contrail 开头并且不以 ccp.juniper.net 结尾的主机名,都在其后面添加域名 ccp.juniper.net,之后使用 SSH 连接到该 FQDNs。

让任务在断开连接后仍然运行

命令

1
$ nohup command &

作用

正常情况下,如果你在 bash 中后台运行一个任务,当你退出当前 shell 会话后该任务也会被终止。如果你想在你退出 shell 会话后该任务仍然继续保持运行,可以使用 nohup 命令。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
▶ ssh 192.168.204.100
Last login: Mon Apr 1 17:10:51 2019 from 192.168.204.1
$ nohup find / -name nosuch.file &
[1] 13182
$ nohup: ignoring input and appending output to ‘nohup.out’

$ exit
logout
Connection to 192.168.204.100 closed.
▶ ssh 192.168.204.100
Last login: Mon Apr 1 19:53:45 2019 from 192.168.204.1
$ cat nohup.out
find: ‘/boot/grub2’: Permission denied
find: ‘/proc/tty/driver’: Permission denied
find: ‘/proc/1/task/1/fd’: Permission denied
find: ‘/proc/1/task/1/fdinfo’: Permission denied
find: ‘/proc/1/task/1/ns’: Permission denied
......

解释

nohup 代表 no hang up,执行该命令后默认的输出保存在当前目录的 nohup.out 文件中,可以检查该文件获取命令的输出。

通过 SSH SOCKS 代理加密你的 Web 浏览数据

命令

1
$ ssh -D PORT remote-host

作用

当你在使用开放的无线热点时,为了对你的 Web 浏览数据进行加密,可以通过 SSH 隧道将浏览流量转发到远程主机上,由远程主机再将流量转发到目的主机。

例子

1
2
$ ssh -ND 1080 ubuntu@test.computer-1.amazonaws.com
$ firefox http://www.mybank.com

解释

使用 ssh 的 -D 选项打开本地主机的的 PORT 端口,使用该端口进行代理连接。同时,由于只是使用端口转发,而并不需要真的登录到远程主机的 shell 执行命令,因此使用 -N 选项。然后配置你的 Web 浏览器,使用 SOCKS 5 proxy 协议,同时使用 localhost 作为 host,使用 PORT 作为端口。

从命令行下载 Web 网页、HTTP 数据或者使用 Web API

命令

1
2
$ curl -o file.html http://website/webpage
$ wget http://website/webpage

作用

curl 和 wget 命令都可以用于下载 Web 网页或者 Web 服务器上可下载的任何东西。也可以使用这些命令和 HTTP API 进行交互。

例子

1
2
3
4
5
6
$ curl -o baidu.index.html www.baidu.com
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2381 100 2381 0 0 6919 0 --:--:-- --:--:-- --:--:-- 6941
$ ls -l baidu.index.html
-rw-rw-r--. 1 fuchencong fuchencong 2381 Apr 2 11:44 baidu.index.html

解释

通过 curl 或 wget 命令就可以和 Web 服务器进行交互,而无需使用图形界面的浏览器。

Shell 脚本

命令行中创建 for 循环

命令

1
2
3
4
5
$ for VAR in LIST
> do
> # use $VAR
> done
$ for VAR in LIST; do use $VAR; done

作用

当你需要对一系列对象执行同样的操作,可以在 shell 中使用 for 循环实现这一点。在命令行中有两种输入循环方式,第一种由 bash 根据循环的语法自动提示你继续输入。第二种方式是直接在一行中输入整个 while 循环。

例子

1
2
3
4
5
6
7
8
9
$ for num in 1 2 3 4 5
> do
> touch file_$num
> done
$ ls
file_1 file_2 file_3 file_4 file_5
$ for num in 1 2 3 4 5; do rm file_$num; done
$ ls
$

解释

上面例子分别创建 5 个空文件,之后又删除这些文件。

命令替换

命令

1
2
$ VAR=`command`
$ VAR=$(command)

作用

以上两种方式都可以进行命令替换。第一种方式是更老的风格。命令替换的结果可以作为另一个命令的参数,或者赋值给变量等等。

例子

1
2
3
4
5
6
7
8
$ cp file file.`date +%F`
$ ls
file file.2019-04-02
$ for x in $(cut -d : -f 1 /etc/passwd); do groups $x; done
root : root docker
bin : bin
daemon : daemon
......

解释

以上例子展示了两种命令替换的用法。

将命令输出保存到变量中,方便之后使用

命令

1
2
3
4
5
$ for VAR in LIST
> VAR2=$(command)
> VAR3=$(command)
> echo "$VAR2 $VAR3"
> done

作用

这里其实就是命令替换的一种常见用法,将命令的输出结果保存到变量中,这样之后就可以多次重用该命令的输出结果了。

例子

1
2
3
4
5
6
7
8
$ for USER in $(cut -f 1 -d : /etc/passwd)
> do
> UID_MIN=$(grep ^UID_MIN /etc/login.defs | awk '{print $NF}')
> USER_ID=$(id -u $USER)
> [ $USER_ID -gt $UID_MIN ] && echo "user $USER, id $USER_ID"
> done
user fuchencong, id 1001
user test2, id 1002

解释

该示例脚本显示了所有用户 id 大于 1000 的用户。

从输入中每次读取一行

命令

1
2
3
4
5
6
7
8
$ while read LINE
> do
> # Do something with $LINE
> done < file.txt
$ command | while read LINE
> do
> # Do something with $LINE
> done

作用

如果你想从输入中每次读取一行,可以通过上述两种方式实现。第一种方式从文件中每次读取一行,第二种方式从 command 命令的输出中每次读取一行。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ df | grep [0-9]% | while read LINE
> do
> use=$(echo $LINE | awk '{print $5}' | tr -d '%')
> mountpoint=$(echo $LINE | awk '{print $6}')
> [ $use -gt 10 ] && echo "$mountpoint usage is over 10%"
> done
/ usage is over 10%
/boot usage is over 10%
$ df | grep [0-9]% | while read fs blocks used available use mountpoint
> do
> use=$(echo $use | tr -d %)
> [ $use -gt 10 ] && echo "$mountpoint usage is over 10%"
> done
/ usage is over 10%
/boot usage is over 10%

解释

如果你想迭代一系列单词,每次读取其中一个,直接使用循环即可。如果你想每次迭代一行,则需要通过循环以及 read 命令实现。

示例脚本读取 df 命令的输出,并进行解析,列出所有使用率超过 10% 的挂载点。第一种实现是在 while 循环内对每一行进行解析,第二种方式在读取每一行时直接将各个字段赋值给相应的变量。

接收用户输入并保存到变量中

命令

1
read

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ cat read.sh
#!/bin/basn

# Copyright (C) fuchencong.com

while true
do
read -p "Which server would you like to backup? " SERVER
echo "Backing up $SERVER"
read -p "Backup another server?(y/n)" -n 1 BACKUP_AGAIN
echo
[ "$BACKUP_AGAIN" = "y" ] || break
done

exit 0
$ sh read.sh
Which server would you like to backup? server1
Backing up server1
Backup another server?(y/n)y
Which server would you like to backup? server2
Backing up server2
Backup another server?(y/n)n

解释

上述脚本展示了 read 的用法,从用户输入中读取内容并保存到变量中。

计算一段文本中某列的数字之和

命令

1
2
$ awk '{sum += $1} END {print sum}' file
$ cat file | awk '{sum += $1} END {print sum}'

作用

通过上述方式,可以计算某一列的数字之和。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ seq 10 15
10
11
12
13
14
15
$ seq 10 15 | awk '{sum += $1} END {print sum}'
75
$ df -mt tmpfs
Filesystem 1M-blocks Used Available Use% Mounted on
tmpfs 488 0 488 0% /dev/shm
tmpfs 488 14 474 3% /run
tmpfs 488 0 488 0% /sys/fs/cgroup
tmpfs 98 0 98 0% /run/user/0
tmpfs 98 0 98 0% /run/user/1001
$ df -mt tmpfs | awk '{sum += $3} END {print sum}'
14

解释

这里通过 awk 工具实现这一点,awk 对每一行执行 sum += $1,而 $1 就是该行中的第一列的值,将其加到变量 sum 中(初始值为 0)。对所有行处理完毕后,最后执行 END 中的语句,即输出 sum 变量的值。

向交互式命令自动回答 yes

命令

1
2
$ yes | command
$ yes "string" | command

作用

如果你在执行某个命令的过程中,需要用户输入 yes,可以尝试使用 yes 命令。yes 命令默认行为是一直输出字符 y,直至终止。可以通过yes string 的方式一直输出字符串 string。

例子

1
2
3
4
5
6
7
8
$  yes | while true; do read  answer; echo "answer is $answer"; [ "$answer" = "y" ] || break; sleep 2; done
answer is y
answer is y
answer is y
answer is y
answer is y
answer is y
^C

解释

上述例子通过 yes 命令向 while 循环传递输入,可以看到 yes 命令默认输出的字符为 y,所以该 while 循环将会一直执行下去。

系统管理

以表格形式显示挂载的文件系统

命令

1
$ mount | column -t

作用

通过以上方式,可以以表格形式显示挂载的文件系统。

例子

1
2
3
4
5
6
7
8
9
10
11
12
$ mount -t tmpfs
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel)
tmpfs on /run type tmpfs (rw,nosuid,nodev,seclabel,mode=755)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,seclabel,mode=755)
tmpfs on /run/user/0 type tmpfs (rw,nosuid,nodev,relatime,seclabel,size=99796k,mode=700)
tmpfs on /run/user/1001 type tmpfs (rw,nosuid,nodev,relatime,seclabel,size=99796k,mode=700,uid=1001,gid=1001)
$ mount -t tmpfs | column -t
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel)
tmpfs on /run type tmpfs (rw,nosuid,nodev,seclabel,mode=755)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,seclabel,mode=755)
tmpfs on /run/user/0 type tmpfs (rw,nosuid,nodev,relatime,seclabel,size=99796k,mode=700)
tmpfs on /run/user/1001 type tmpfs (rw,nosuid,nodev,relatime,seclabel,size=99796k,mode=700,uid=1001,gid=1001)

解释

mount 命令的输出结果并不容易阅读,这里通过管道将 mount 命令的输出传递给 column -t 命令,以表格的形式输出结果。

结束给定用户或给定程序的所有进程

命令

1
2
$ pkill -9 command
$ pkill -9 user command

作用

如果你需要结束给定程序的多个进程,可以使用 pkill 命令。如果想结束给定用户的所有进程,可以使用 pkill 命令的 -u 选项。

例子

1
2
3
4
5
6
7
8
9
10
11
$ ps -ef | grep python | grep -v grep
root 846 1 0 Mar28 ? 00:00:01 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid
root 1557 1 0 Mar28 ? 00:01:08 /usr/bin/python -Es /usr/sbin/tuned -l -P
fuchenc+ 128392 11253 0 11:18 pts/1 00:00:00 python
fuchenc+ 128519 128491 0 11:19 pts/3 00:00:00 python

$ pkill -9 -u fuchencong python

$ ps -ef | grep python | grep -v grep
root 846 1 0 Mar28 ? 00:00:01 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid
root 1557 1 0 Mar28 ? 00:01:08 /usr/bin/python -Es /usr/sbin/tuned -l -P

解释

pkill 只会对那些与指定字段完全匹配的进程起作用。

重复一个命令,直至成功

命令

1
2
3
4
$ while true:
> do
> command && break
> done

作用

当 command 命令执行成功后,将会执行 break 退出循环,否则继续循环执行 command。通过以上方式,可以循环执行 comand 命令,直至该命令执行成功。

例子

1
2
3
4
5
$ while true
> do
> ping -c 1 -W 1 remotehost > /dev/null 2>&1 && break
> done; echo "remotehost is up at $(date)"
$

解释

上述例子通过 ping 命令判断 remotehost 是否可达,如果不可达则持续等待,否则退出循环并打印 remotehost 变成可达状态的时间。

查找谁占用的磁盘空间最多

命令

1
$ sudo du -s /home/* | sort -rn

作用

通过以上命令可以找出 /home/ 目录下谁占用的磁盘空间最多。

例子

1
2
3
4
$ sudo du -s /home/* | sort -rn
508448 /home/ovs
144 /home/fuchencong
12 /home/test2

解释

还有一个图形界面工具 ncdu,可以更加直观地显示磁盘使用率。

查找占用磁盘空间最多的文件

命令

1
$ find / -type -f -exec wc -c {} \; | sort -rn

作用

通过以上方式,可以从根目录开始查找占用磁盘空间最多的文件。

例子

1
2
3
4
5
6
7
8
9
$ sudo find ~ -type f -exec wc -c {} \; | sort -rn
46382 /home/fuchencong/nohup.out
26398 /home/fuchencong/.bash_history
2381 /home/fuchencong/LearnProgramming/Linux/Command_KungFu/Chapter_03/baidu.index.html
1679 /home/fuchencong/.ssh/id_rsa
1011 /home/fuchencong/LearnProgramming/Linux/Command_KungFu/Chapter_01/typescript
512 /home/fuchencong/LearnProgramming/Linux/Command_KungFu/Chapter_03/howto.txt
414 /home/fuchencong/.ssh/id_rsa.pub
......

解释

使用 find 命令从指定目录开始查找,通过 -type 选项设置查找的目标为文件,对每个找到的文件执行 wc -c 命令,即统计该文件的字节数。然后通过 sort 命令根据数字进行逆序排序,从而找到占用磁盘空间最多的文件。

根据内存使用率排列进程

命令

1
$ ps aux | sort -rnk 4

作用

通过以上方式,可以根据内存使用率对所有进程进行排列。

例子

1
2
3
4
5
$ ps aux  | sort -rnk 4 | more
root 1567 0.0 3.2 477252 32372 ? Ssl Mar28 3:21 /usr/bin/containerd
root 846 0.0 2.9 357988 28964 ? Ssl Mar28 0:01 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid
root 1557 0.0 1.7 573856 17132 ? Ssl Mar28 1:10 /usr/bin/python -Es /usr/sbin/tuned -l -P
......

解释

使用 ps aux 列出各个进程的详细信息,然后使用 sort 命令根据第 4 列的数字值进行逆序排列,从而列出内存占用最高的进程。

根据 CPU 使用率列出进程

命令

1
$ ps aux | sort -rnk 3

作用

通过以上方式,可以根据 CPU 使用率对所有进程进行排列。

例子

1
2
3
4
$ ps aux  | sort -rnk 3 | more
root 788 0.1 0.6 298728 6116 ? Ssl Mar28 9:05 /usr/bin/vmtoolsd
root 978 0.0 1.2 628596 11988 ? Ssl Mar28 0:10 /usr/sbin/NetworkManager --no-daemon
......

解释

使用 ps aux 列出各个进程的详细信息,然后使用 sort 命令根据第 3 列的数字值进行逆序排列,从而列出 CPU 使用率最高的进程。

获取系统是 32 位还是 64 位

命令

1
$ getconf LONG_BIT

作用

通过上述命令,可以快速检查自己的系统是 32 位 还是 64 位。

例子

1
2
$ getconf LONG_BIT
64

解释

通过检查 LONG 整型所占用的位数,快速获得自己系统是 32 位还是 64 位。

创建一个随机密码

命令

1
2
$ openssl rand -base64 48 | cut -c1-PASSWORD_LENGTH
$ gpw() {openssl rand -base64 48 | cut -c1-${1};}

作用

可以使用上述方式创建一个随机密码。第一个命令随机产生指定 PASSWORD_LENGTH 的密码,第二个命令创建了一个函数,可以通过参数指定密码的长度。

例子

1
2
3
4
5
6
$ echo 'gpw() { openssl rand -base64 48 | cut -c1-${1}; }' >> ~/.bash_profile
$ source ~/.bash_profile
$ gpw 6
2O/VvU
$ gpw 12
3fkqdzXcYCB3

解释

该例子将创建随机密码的函数写入 bash 配置文件,并通过 source 命令使其立即生效,之后就可以使用 gpw 创建指定长度的随机密码了。

文件和目录

快速创建文件的备份

命令

1
$ cp file{,.bak}

作用

可以使用大括号表达式快速创建一个文件的备份。大括号表达式允许你使用单个命令行参数创建出多个参数。例如 file{,.bak} 将会被扩展为两个参数 file 以及 file.bak ,file_{one,two,three} 将会被扩展为 file_one、file_two、file_three。大括号中的值可以是以逗号分隔的字符串,也可以是序列表达式,例如 {1..5} 或者 {a..z}。

例子

1
2
3
4
5
6
7
8
$ cp test.file{,.bak}
$ ls test.file*
test.file test.file.bak
$ touch file-{1,2,3}
$ ls
file-1 file-2 file-3
$ echo 10.0.0.{0..7}
10.0.0.0 10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4 10.0.0.5 10.0.0.6 10.0.0.7

解释

这里通过 Bash 中大括号表达式,快速创建文件的一个备份。

快速修改文件扩展名

命令

1
2
$ mv file{.old,.new}
$ mv file.{old,new}

作用

这里继续使用大括号语法,快速修改文件的扩展名,将其文件名从 file.old 修改为 file.new。

例子

1
2
3
4
5
6
7
8
9
10
11
12
$ touch file.txt
$ ls
file.txt
$ mv file{.txt,.doc}
$ ls
file.doc
$ mv file.{doc,ppt}
$ ls
file.ppt
$ mv file{.ppt,}
$ ls
file

解释

这里通过 Bash 中大括号表达式,快速修改文件扩展名。

根据日期创建文件的备份

命令

1
2
$ alias d='date +%F'
$ cp file file.`d`

作用

通过上述方式,可以创建文件 file 的备份,备份文件的后缀名为当前日期。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ alias d="date +%F"
$ d
2019-04-04
$ cp file file.`d`
$ ls
file file.2019-04-04
$ rm -rf file.2019-04-04
$ cp file file.$(d)
$ ls
file file.2019-04-04
$ rm -rf file.2019-04-04
$ cp file{,$(d)}
$ ls
file file2019-04-04

解释

首先通过 alias 创建一个命令别名,然后创建文件备份时通过命令替换获得当前日期,并将结果字符串作为备份文件的后缀名。

覆盖文件的内容

命令

1
$ command > file

作用

将 command 命令的输出结果写入到 file 文件中。如果 file 文件不存在,将创建一个文件,否则将覆写该文件的内容。

例子

1
2
3
$ echo "this is a test file" > file
$ cat file
this is a test file

解释

通过重定向符 > 将命令的输出写入指定文件。

清空文件的内容

命令

1
2
$ > file
$ cat /dev/null > file

作用

通过以上两种方式,都可以快速清空文件 file 的内容。

例子

1
2
3
4
5
$ cat file
this is a test file
$ > file
$ cat file
$

解释

这里解释一下清空一个文件的内容与删除并重新创建该文件的区别。如果删除文件时,其他进程仍然保持了打开该文件的句柄,该文件所占用的磁盘空间并不会释放,只有当所有进程都释放了该文件对应的句柄,磁盘空间才会被释放。而清空一个文件则是直接删除该文件的内容,因此磁盘空间立即被释放。

在文件末尾添加字符串

命令

1
$ command >> file

作用

通过以上命令,可以将 command 命令的输出添加到 file 文件的末尾。如果文件不存在,将创建一个文件。

例子

1
2
3
4
5
6
$ cat file
this is line 1
$ echo "this is line 2" >> file
$ cat file
this is line 1
this is line 2

解释

通过重定向 >> 操作符,可以在 file 文件的末尾添加字符串。

查看动态增长的文件

命令

1
$ tail -f file

作用

通过以上命令,可以动态实时地查看一个文件更新的内容。

例子

1
2
3
4
5
6
7
$ tail -f tmp.log
current value is 30
current value is 31
current value is 32
current value is 33
current value is 34
current value is 35

解释

通过 tail -f 可以查看实时查看文件末尾添加的内容。

同时监视多个 log 文件

命令

1
2
$ multitail file1 fileN
$ multitail file1 -I fileN

作用

通过使用 multitail 命令,可以同时监视多个动态变化的文件。而且 multitail 命令还支持高亮、过滤、合并以及其他功能。

例子

1
$ multitail tmp1.log tmp2.log

解释

multitle 的 -I 选项允许将多个文件合并到一个窗口中进行监视,这种方式使得多个文件的输出将混合显示。这在 troubleshooting 时有时非常有用。

在使用 multitail 命令经常使用的一些命令:

  • F1:帮助
  • a:添加文件到视图中
  • d:从视图中删除文件
  • /:搜索
  • ctrl-g:从命令、菜单或动作中退出
  • q:退出

删除空目录

命令

1
$ find . -type d -empty -delete

作用

通过上述方式,可以删除当前目录下的空目录。

例子

1
2
3
4
5
$ mkdir empty_dir{1..5}
$ ls
empty_dir1 empty_dir2 empty_dir3 empty_dir4 empty_dir5
$ find . -type d -empty -delete
$ ls

解释

通过 find 命令的 -type 选项查找目录,-empty 查找空目录,通过 -delete 选项对查找到的空目录执行删除动作。

打印包含给定字符串的文件列表

命令

1
$ grep -rl string .

作用

在当前目录下查找所有包含指定字符串的文件,并将其列出。

例子

1
2
3
4
5
6
$ grep -rl test .
./file.1
./file.2
./file.3
./file.4
./file.5

解释

通过 grep 命令进行模式查找,-r 选项用于递归查找,-l 选项列出匹配的文件名。

以可读方式递归列出目录结构

命令

1
$ find . -type f -ls

作用

通过以上方式,可以以更加可读的方式递归列出目录下的文件。

例子

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
$ ls -lR
.:
total 20
drwxrwxr-x. 2 fuchencong fuchencong 76 Apr 4 17:22 dir
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:21 file.1
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:21 file.2
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:21 file.3
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:21 file.4
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:21 file.5

./dir:
total 20
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:22 file.1
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:22 file.2
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:22 file.3
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:22 file.4
-rw-rw-r--. 1 fuchencong fuchencong 20 Apr 4 17:22 file.5

$ find . -type f -ls
34025643 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:21 ./file.1
34025644 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:21 ./file.2
34025645 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:21 ./file.3
34025646 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:21 ./file.4
34025647 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:21 ./file.5
34025649 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:22 ./dir/file.1
34025650 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:22 ./dir/file.2
34025651 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:22 ./dir/file.3
34025652 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:22 ./dir/file.4
34025653 4 -rw-rw-r-- 1 fuchencong fuchencong 20 Apr 4 17:22 ./dir/file.5

解释

尽管 ls 命令的 -R 选项也可以递归列出目录下的文件,但是其输出形式可读性不强。而且 find 命令同时会列出文件的路径。通过 find 命令的 的 -type 选项查找类型为 file 的文件,并使用 -ls 将其列出。

以树形结构列出文件和目录

命令

1
2
3
$ tree
$ tree -d
$ tree -L number

作用

通过以上方式,可以列出目录结构。

例子

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
$ tree
.
├── dir
│ ├── file.1
│ ├── file.2
│ ├── file.3
│ ├── file.4
│ └── file.5
├── file.1
├── file.2
├── file.3
├── file.4
└── file.5

1 directory, 10 files
$ tree -d
.
└── dir

1 directory
$ tree -L 1
.
├── dir
├── file.1
├── file.2
├── file.3
├── file.4
└── file.5

1 directory, 5 files

解释

tree 命令可以以树形格式列出文件和目录。如果只想查看目录结构,可以列出 -d 选项。为了限制树的深度,可以使用 -L 选项并指定树的深度。

在多个文件中替换某个字符串

命令

1
$ find /path -type f -exec sed -i.bak 's/string/replacement/g' {} \;

作用

通过以上方式,可以在指定目录下对多个文件实现字符串替换,将这些文件中的 string 替换为 replacement。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ grep this -R *
file.1:this is a test line
file.2:this is a test line
file.3:this is a test line

$ find . -type f -exec sed -i.bak 's/this/that/g' {} \;

$ grep this -R *
file.1.bak:this is a test line
file.2.bak:this is a test line
file.3.bak:this is a test line
$ grep that -R *
file.1:that is a test line
file.2:that is a test line
file.3:that is a test line

解释

通过 find 命令在指定 /path 下查找所有类型为 file 的文件,并对每一个找到的文件执行一个 sed 命令。该 sed 命令中的 -i.bak 用来指定直接在文件中进行替换,同时在替换之前创建当前文件的一个备份,备份文件的后缀为 .bak,s/string/replacement/g 用于将文件中的所有 string 替换为 replacement。

从文件中获取指定行

命令

1
$ awk 'NR==N'

作用

通过上述命令,可以打印文件中指定的第 N 行。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat test.file
this is line 1
this is line 2
this is line 3
this is line 4
this is line 5
this is line 6
this is line 7
this is line 8
this is line 9
this is line 10
$ awk 'NR==6' test.file
this is line 6

解释

通过 awk 实现打印文件中的第 N 行。

将文本文件在 Windows 格式 和 Linux 格式之间转换

命令

1
2
$ dos2unix
$ unix2dos

作用

Windows 格式的文本文件以 CR(carriage return)和 LF(line feed)作为行结束符,而 Linux 格式的文本文件以 LF(line feed)作为行结束符。有时候需要在这两种格式之间进行转换。dos2unix 命令可以将 Windows 格式的文本文件转换为 Linux 格式。unix2dos 命令可以将 Linux 格式的文本文件转换为 Windows 格式。

通过 cat -A 命令或者 file 命令检查行结束符。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ cat -A test.file
this is line 1$
this is line 2$
this is line 3$
$ file test.file
test.file: ASCII text
$ unix2dos test.file
unix2dos: converting file test.file to DOS format ...
$ cat -A test.file
this is line 1^M$
this is line 2^M$
this is line 3^M$
$ file test.file
test.file: ASCII text, with CRLF line terminators
$ dos2unix test.file
dos2unix: converting file test.file to Unix format ...
$ cat -A test.file
this is line 1$
this is line 2$
this is line 3$
$ file test.file
test.file: ASCII text

解释

有时候在 Linux 上创建的文本文件,在 Windows 上用记事本打开时可能所有内容都在一行上,所以需要在 Linux 上使用 unix2dos 命令将其先转换为 Windows 格式。

杂项

切换到上一个工作目录

命令

1
$ cd -

作用

通过以上方式,可以快速回到上一个工作目录。

例子

1
2
3
4
5
6
7
8
9
$ pwd
/home/fuchencong/LearnProgramming/Linux/Command_KungFu/Chapter_05
$ cd ../Chapter_04
$ pwd
/home/fuchencong/LearnProgramming/Linux/Command_KungFu/Chapter_04
$ cd -
/home/fuchencong/LearnProgramming/Linux/Command_KungFu/Chapter_05
$ pwd
/home/fuchencong/LearnProgramming/Linux/Command_KungFu/Chapter_05

解释

OLDPWD 环境变量保存着你最近的一个工作目录的路径,使用 cd - 等同于 cd $OLDPWD

重置你的终端仿真器的显示

命令

1
$ reset

作用

通过以上方式,可以重置你的终端仿真器的显示。

例子

1
2
3
4
$ cat /bin/ls
ü¶­a¨ã¨¯a¨è°añ3a3@ ¶a¶
......
$reset

解释

如果一不小心在终端中显示了一个二进制文件,这会导致你的终端变得不可用。为了恢复你的终端,可以使用 reset 命令。

从命令行搜索维基百科

命令

1
2
$ dig +short txt <string>.wp.dg.cx
$ host -t txt <string>.wp.dg.cx

作用

如果有时候如果你想快速查找关于某个主题的信息,可以使用 DNS 来搜索维基百科。域名服务器会将维基百科的文章内容作为 TXT 格式返回。

例子

1
2
$ dig +short txt unix.wp.dg.cx
;; connection timed out; no servers could be reached

解释

以上方式目前已经无法工作了,感觉原因是维基百科不再为每个词条创建相应的域名了。一种替代方式是使用 lynx 在命令行上浏览网页。

使非交互式会话和交互式会话行为一致

命令

将所有的自定义配置放入 ~/.bashrc,然后 ~/.bash_profile 的内容如下:

1
2
3
if [ -f ~/.bashrc ]; then
source ~/.bashrc
fi

作用

如果你交互式方式登录到 shell 并执行一个命令,其行为可能和仅仅使用 ssh 连接到主机执行一个命令不一致。.profile 和 .bash_profile 中的配置内容只有在交互式会话启动时才会执行。所以最好将所有的自定义配置放入 .bashrc 文件中,然后在 ~/.bash_profile 中仅仅引用 .bashrc 中的内容即可。

例子

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
$ cat ~/.bash_profile
ll() { ls -l; }
$ ll
total 4
-rw-rw-r--. 1 fuchencong fuchencong 45 Apr 6 14:26 test.file
$ ssh localhost ll
bash: ll: command not found
$ cat ~/.bash_profile
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

# User specific environment and startup programs
$ cat ~/.bashrc
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi

ll() { ls -l; }
$ ssh localhost ll
fuchencong@localhost's password:
total 48
drwxrwxr-x. 2 fuchencong fuchencong 6 Mar 31 16:03 build_server
drwxrwxr-x. 3 fuchencong fuchencong 19 Mar 28 14:24 LearnProgramming
-rw-------. 1 fuchencong fuchencong 46382 Apr 1 19:57 nohup.out

解释

最初,将 ll 命令定义在 ~/.bash_profile 中,可以在交互式 shell 中使用,但是 ssh 直接调用该命令则无法生效。之后将该命令定义移动到 ~/.bashrc,则两种场景都可以使用。

让你的电脑和你说话

命令

1
2
$ espeak -f file
$ echo text | espeak

作用

espeak 工具可以将文本转换为语音。可以为 espeak 工具提供一个文件,或者通过管道提供文本,espeak 将会将其转化为语音。

例子

1
$ for i in {1..10}; do sleep 1; done; espeak "our job is done"

解释

可以在完成一个长时间的任务后,调用 espeak 工具输出一段语音提示。

显示不同时区下的当前时间

命令

1
$ TZ=<TIMEZONE> date

作用

通过以上方式可以获得指定时区下的当前时间。

例子

1
2
3
4
$ TZ=MST date
Sat Apr 6 04:57:38 MST 2019
$ TZ=Beijing date
Sat Apr 6 11:57:44 Beijing 2019

解释

通过 TZ 环境变量设置当前时区,通过 date 命令显示在该时区下的当前时间。

在命令行中显示日历

命令

1
2
3
$ cal
$ cal MM YYYYY
$ cal YYYY

作用

通过以上方式,可以在命令行中显示日历信息。第二种方式 cal MM YYYY 可以显示指定年指定月的日历信息。第三种方式 cal YYYY 可以显示指定年的日历信息。

例子

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
$ cal
April 2019
Su Mo Tu We Th Fr Sa
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
$ cal 03 2019
March 2019
Su Mo Tu We Th Fr Sa
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
$ cal 2019
2019

January February March
Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
1 2 3 4 5 1 2 1 2
6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9
13 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16
20 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23
27 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30
31
......

解释

通过 cal 命令可以在命令行显示日历信息。

解压压缩文件到指定目录

命令

1
$ tar tarfile.tar -C /path/to/extraction/directory

作用

通过使用 tar 命令的 -C 选项,可以将压缩文件解压到指定目录中。

例子

1
2
3
4
5
6
7
$ tar -xv -f test.tar -C dest_dir/
test.file
$ ls
dest_dir test.tar
$ ls dest_dir/ -l
total 4
-rw-rw-r--. 1 fuchencong fuchencong 45 Apr 6 14:26 test.file

解释

不用首先切换到指定目录,直接使用 tar 命令的 -C 选项就可以将压缩文件解压到指定目录中。

解压压缩文件时改变目录结构

命令

1
$ tar xf tarfile.tar --strip-components=NUMBER

作用

如果你想从压缩文件中的某个子目录开始解压,可以使用 —strip-components 选项。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ curl -sLO https://github.com/twbs/bootstrap/archive/v3.4.1.tar.gz
$ ls
v3.4.1.tar.gz
$ tar -ztv -f v3.4.1.tar.gz | head -n 1
drwxrwxr-x root/root 0 2019-02-13 23:55 bootstrap-3.4.1/
$ tar -zxv -f v3.4.1.tar.gz --strip-components=1 -C ./bootstrap/
bootstrap-3.4.1/.editorconfig
bootstrap-3.4.1/.gitattributes
......
$ ls -l bootstrap/
total 400
-rw-rw-r--. 1 fuchencong fuchencong 643 Feb 13 23:55 bower.json
-rw-rw-r--. 1 fuchencong fuchencong 434 Feb 13 23:55 CHANGELOG.md
-rw-rw-r--. 1 fuchencong fuchencong 17 Feb 13 23:55 CNAME
......

解释

例子中下载一个指定版本的压缩文件,该压缩文件的顶级目录是带版本号的目录,如果我们不想要该目录项,可以使用 —strip-components=1 去除该顶级目录,然后通过 -C 选项将该目录下的所有文件解压到指定目录。

在命令行使用电子表格

命令

1
$ sc

作用

可以在命令行中使用电子表格程序 sc,sc 中的按键规则类似于 vi。

例子

1
2
3
4
5
6
7
root@92c4e480f92b:/# sc
Do you want a chance to save the data?

A B C D E F G H
0 10.00
1 20.00
2

解释

这里我启用了一个 Docker Ubuntu 容器,然后通过 apt-get install sc 的方式安装了 sc 程序,对于其他发行版本,可能需要通过源码安装的方式进行安装。

简易版的命令行计时器

命令

1
$ time read

作用

通过以上方式可以创建一个简易版的命令行计时器,从输入命令后开始计时,再次输入回车键后结束计时,此时会打印出当前经过的时间。

例子

1
2
3
4
5
$ time read

real 0m4.863s
user 0m0.000s
sys 0m0.000s

解释

这是通过 time 命令实现的一个小技巧,time 命令用于计算一个程序运行的时间,这里 time read 用来计算 read 命令所消耗的时间,而 read 命令在读取到一行后就停止执行,time 命令也就会打印出这中间所经过的时间。

周期执行一个命令,并观察它的输出

命令

1
$ watch command

作用

通过以上方式,可以周期执行 command 命令,并输出其执行结果。默认的执行间隔为 2 s。

例子

1
2
3
4
5
6
$ watch free
Every 2.0s: free Sun Apr 7 14:31:30 2019

total used free shared buff/cache available
Mem: 997956 326368 109976 7956 561612 408596
Swap: 2097148 3868 2093280

解释

通过以上方式,就可以周期执行 free 命令,并根据其输出动态监视系统内存的变化情况。

在给定时间执行一个命令

命令

1
2
$ echo "command" | at time
$ at -f file time

作用

通过以上方式,可以在指定时间执行指定的命令。第一种方式直接指定命令,第二种方式将要执行的命令写入文件 file 中。

例子

1
2
3
4
$ echo "echo hello world" | at now +1minutes
job 3 at Sun Apr 7 14:44:00 2019
$ atq
3 Sun Apr 7 14:44:00 2019 a fuchencong

解释

可以通过 atq 命令列出当前所设置的的 at 任务。通过 atrm 删除某个 at 任务。

和另一个用户共享你的 sceen 会话

命令

1
$ screen -x user/session

作用

通过以上方式,可以共享 user/session 这个 screen 会话。当一个 screen 会话被多方共享时,他们不仅可以看到同一个屏幕,还可以同时进行输入。

例子

1
2
3
4
5
6
7
8
9
10
11
$ sudo chmod u+s /usr/bin/screen
$ sudo chmod 0755 /var/run/screen/
$ screen -S for_test
ctrl-a :multiuser on
ctrl-a :acladd fuchencong

$ screen -ls
There are screens on:
110156.for_test (Attached)
1 Socket in /var/run/screen/S-fuchencong.
$ screen -x for_test

解释

为了让 screen 工具支持多用户,screen 可执行程序必须设置 SUID 权限。然后当某人通过 screen -S 创建了一个 screen 会话,为了允许用户共享这个 screen 会话,输出 Ctrl-a 后,再输入 :multiuser on。为了允许某人连接到你的 session 会话,可以在输入 ctrl-a 后再输入 :acladd username。为了断开某人的会话连接,可以使用 :acldel username。

在允许他人连接到你的 screen 会话后,其他人就可以通过 screeen -x 的方式连接到指定的会话,会话可以由 PID 或者 name 指定。

对于一个命令别名执行其原始版本

命令

1
$ \aliased-command

作用

通过转义字符,可以使用该命令别名的原始版本,而不是对应的 alias 命令。

例子

1
2
3
4
5
6
7
$ type ls

ls is aliased to `ls -F'
$ ls
bootstrap/ sc/ v3.4.1.tar.gz
$ \ls
bootstrap sc v3.4.1.tar.gz

解释

可以看到,这里 ls 命令是 ls -F 的命令别名,这样就可以显示文件类型的后缀提示,直接输入 ls 是调用的命令别名版本,\ls 则调用原始命令。

将命令的结果保存为图像

命令

1
$ command | convert label:@-image.png

作用

通过以上方式,可以将命令的输出保存为图像格式。label 之后的 @ 符号告诉 convert 命令从后面指定的文件中读取内容,- 表示文件为标准输入,然后闯将名为 image.png 的图片。

例子

1
2
3
4
$ echo "hello world" | convert label:@- test1.png
$ convert label:"hello world" test2.png
$ ls
test1.png test2.png

解释

convert 工具是 ImageMagick 软件套件的一部分,所以首先需要安装该软件:sudo yum install ImageMagick。而且为了让上述例子生效,需要修改如下配置,将 /etc/ImageMagick/policy.xml 中的如下行注释:

1
<policy domain="coder" rights="none" pattern="LABEL" />