[Tips] 终端10X工作法

此篇文章转载legendtkl的博客终端10X工作法,我一直很喜欢terminal使用中带来的一些小tricks,这篇文章刚好列举了一些代表性的,之后我会在这篇文章的基础上,将我比较常用的记录并放到顶部,也算是方便查找。

另外也非常感谢legendtkl的文章合抱之木,生于毫末,也算是我开始写博客的原因之一。

以下为正文内容。

在 github 上面有一个 700 多人 star 的 repo 叫做 Bash-Oneliner,介绍了很多实用并且可以有效提高工作效率的命令,我们来了解一下。原文直达:Bash-Oneliner 。注:去除了部分看上去没啥用的命令,可以原文查看所有内容。

1. Terminal

注:下面介绍的是在 Linux 下的 terminal 的行为,在 Mac 下面会略有差异,尽量补全

Ctrl 相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Ctrl + n : 类似向下方向键
Ctrl + p : 类似向上方向键
Ctrl + r : 反向搜索 terminal 的历史命令
Ctrl + s : 停止该 terminal
Ctrl + q : 在 Ctrl + s 后面重新恢复该 terminal
Ctrl + a : 移动光标到行的开始处。(这个很有用)
Ctrl + e : 移动光标到行的结尾处。(同上)
Ctrl + d : 如果当前的 terminal 的命令行有输入,那么 Ctrl + d 会删除光标处的数字。否则会退出当前的 terminal
Ctrl + k : 删除从当前光标开始到结尾的所有的字符
Ctrl + x + backspace: 删除从当前光标到行开始的所有的字符
Ctrl + t : 交换当前光标下的字符和其前面的字符的位置。Esc + t 交换光标前面的两个单词。(这个很有意思)
Ctrl + w : 剪切光标之前的单词。Ctrl + y 粘贴该单词
Ctrl + u : 剪切光标之前的所有字符。Ctrl + y 粘贴刚刚剪切的字符
Ctrl + _ : 撤销前面的操作。(可以连续操作多次)
Ctrl + l : 类似 clear。(类似 Mac 终端下的 Command + k)
Ctrl + x + Ctrl + e : 唤起 $EDITOR 环境变量设置的编辑器程序,在需要输入多行的情况下比较有用。(试验了一下如果之前没有设置,$EDITOR 的默认值是 emacs。你可以将其设置为 vim,即 export EDITOR=vim。当然也可以将这个环境变量设置为其他的应用程序去实现一些有趣的功能,这个测试过了)

改变字符大小写

1
2
3
Esc + u : 将当前光标开始到单词结尾的字符都转换成大写。(这里的单词指的是光标所在的位置前后以空格分隔形成的单词)
Esc + l : 将当前光标开始到单词结尾的字符都转换成小写
Esc + c : 将光标所在位置的字符转换成大写

执行历史命令

1
2
!53 : 执行 history 中的 53 号命令
!! : 执行上一条命令

替换上一条命令,并替换一些参数

1
2
3
4
5
6
7
8
# 上一条命令:echo 'aaa'
^aaa^bbb
# 此时将上一条命令替换为 echo 'bbb',输出 bbb

# 需要注意的是,这样只会替换第一次 aaa,如果要替换所有的 aaa,需要像下面这样使用
^aaa^bbb^:&
# 或者
!!:gs/aaa/bbb/

前缀匹配执行历史命令

1
2
3
4
!cat
# 或者
!c
# 执行历史命令中最近一条满足前缀是 c 或者 cat 的命令

文件名使用正则

1
2
3
4
5
6
7
8
9
10
11
# ? 表示一个单独的任意字符
/b?n/?at # 匹配上 /bin/cat

# * 表示多个任意字符
/etc/pa*wd # 匹配上 /etc/passwd

# '[]' 表示一个字符范围
ls -al [a-z]* # 列出所有以字母开头的文件

# '{}' 文件名匹配多种模式
ls {*.sh,*.py} # 列出所有的 .sh 和 .py 文件

环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$0, $1, $2, $3, ... : 在执行 shell 的时候传参使用,$0 表示 shell 名字,$1,$2,$3依次表示后面的参数
$# : 参数的个数
$? : 最近的一个终端 foreground 命令的退出状态
$- : 当前 shell 设置的选项,可以通过 echo $- 查看
$$ : 当前 shell 进程的 pid
$! : 最近的一个终端后台命令的 pid

$DESKTOP_SESSION : 当前的展示管理器。(可能说的是 xWindow)
$EDITOR : 编辑器,可以通过上面提到的快捷键唤醒
$LANG : 语言设置
$PATH : 这个不用说了
$PWD : 当前目录
$SHELL : 当前的 shell
$USER : 当前的 username
$HOSTNAME : 当前的 hostname

2. Grep

grep 的种类

1
2
3
4
5
grep = grep -G  # 支持基本的正则表达式
fgrep = grep -F # 查找文件里符合条件的字符串
egrep = grep -E # 支持扩展的正则表达式
pgrep = grep -P # 兼容 Perl 的正则表达式语法
rgrep = grep -r # 递归 grep

统计空行个数

1
grep -c "^$"

grep 并且只返回数字

1
2
3
grep -o '[0-9]*'
#或者
grep -oP '\d'

grep 含有特定数字的数字

1
2
3
4
5
grep ‘[0-9]\{3\}’
# or
grep -E ‘[0-9]{3}’
# or
grep -P ‘\d{3}’

查找 IP 地址

1
2
3
grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
# or
grep -Po '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'

查找单词

1
2
3
4
5
6
7
8
# return also 3 lines after match
grep -A 3 'bbo'

# return also 3 lines before match
grep -B 3 'bbo'

# return also 3 lines before and after match
grep -C 3 'bbo'

查找特定字符开头的单词

1
grep -o 'S.*'

提取两个特定单词之间的文本

1
grep -o -P '(?<=w1).*(?=w2)'

查找不包含某个单词的文件内容

1
grep -v bbo filename

查找不是以特定字符开头的文件内容

1
grep -v '^#' file.txt

查找含有空格的内容

1
2
bbo="some strings"
grep "$boo" filename

查找第一个 match 文件内容

1
grep -m 1 bbo filename

查找并返回满足条件的文件内容条数

1
grep -c bbo filename

统计单词在文件中出现的次数

1
grep -o bbo filename |wc -l

大小写敏感的查找

1
grep -i "bbo" filename

匹配结果着色

1
grep --color bbo filename

查找目录下的所有文件

1
2
3
grep -R bbo /path/to/directory
# or
grep -r bbo /path/to/directory

查找目录下的所有文件,不输出文件内容

1
grep -rh bbo /path/to/directory

查找目录下的所有文件,只输出匹配的文件名

1
grep -rl bbo /path/to/directory

OR 查找

1
grep 'A\|B\|C\|D'

AND 查找(比如 A and B)

1
grep 'A.*B'

正则查找(比如 ACB 或者 AEB)

1
grep 'A.B'

查找特定字符(比如 color 或者 colour)

1
grep ‘colou?r’

查找多个文件的所有内容

1
grep -f fileA fileB

查找 tab

1
grep $'\t'

从变量中查找

1
2
3
4
$echo "$long_str"|grep -q "$short_str"
if [ $? -eq 0 ]; then echo 'found'; fi
#grep -q will output 0 if match found
#remember to add space between []!

查找括号中间的字符串

1
grep -oP '\(\K[^\)]+'

略过目录查找

1
grep -d skip 'bbo' /path/to/files/*

3. Sed

移除文件第一行

1
sed 1d filename

移除文件的前 100 行

1
sed 1,100d filename

移除包含特定字符串的文件行

注:这种方式并不会修改原文件,可以将输出重定向到新的文件保存

1
2
3
sed "/bbo/d" filename
# case insensitive:
sed "/bbo/Id" filename

移除文件不满足第 n 个字符串不等于某个值的行

1
2
3
4
# 第 5 个字符不等于 2
sed -E '/^.{5}[^2]/d'
#aaaa2aaa (you can stay)
#aaaa1aaa (delete!)

修改原文件

1
2
# 删除包含 bbo 的行并直接保存文件
sed -i "/bbo/d" filename

使用变量的时候使用双引号

1
2
3
4
# e.g. add >$i to the first line (to make a bioinformatics FASTA file)
sed "1i >$i"
# notice the double quotes! in other examples, you can use a single quote, but here, no way!
# '1i' means insert to first line

删除空行

1
2
3
4
5
sed '/^\s*$/d'

# or

sed '/^$/d'

删除最后一行

1
sed '$d'

删除文件的最后一个字符

1
sed -i '$ s/.$//' filename

向文件开头插入字符串(比如 “[“)

1
sed -i '1s/^/[/' file

向文件中特定行中插入字符串

1
sed -e '1isomething' -e '3isomething'

向文件结尾插入字符(比如 “]”)

1
sed '$s/$/]/' filename

向文件结尾插入新行

1
sed '$a\'

向文件的每一行插入数据

1
sed -e 's/^/bbo/' file

向文件的每一行结尾插入数据

1
sed -e 's/$/\}\]/' filename

每 n 个字符插入换行符(比如每 4 个字符)

1
sed 's/.\{4\}/&\n/g'

连接多个文件

1
sed -s '$a,' *.json > all.json

内容替换

1
sed 's/A/B/g' filename

基于正则的文件内容替换

1
sed "s/aaa=.*/aaa=\/my\/new\/path/g"

筛选文件中以特定字符串开始行

1
sed -n '/^@S/p'

####打印文件中的多行

1
sed -n 500,5000p filename

打印文件中特定行

1
2
3
sed -n '0~3p' filename

# 打印 3 的倍数行

打印奇数行

1
sed -n '1~2p' filename

删除文件开头的空格和 tab

1
2
sed -e 's/^[ \t]*//'
# Notice a whitespace before '\t'!!

只删除空格

1
2
3
sed 's/ *//'

# notice a whitespace before '*'!!

移除文件结尾的逗号

1
sed 's/,$//g'

文件结尾添加一列(以 tab 分隔)

1
2
3
4
5
sed "s/$/\t$i/"
# $i is the valuable you want to add

# To add the filename to every last column of the file
for i in $(ls);do sed -i "s/$/\t$i/" $i;done

打印特定的行

1
sed -n -e '123p'

删除文件的最后一个字符

1
sed '$ s/.$//'

指定位置插入字符

1
sed -r -e 's/^.{3}/&#/' file

4. Awk

设置 tab 为分隔符

1
awk -F $'\t'

设置 tab 为输出内容的分隔符

1
awk -v OFS='\t'

传递参数

1
2
a=bbo;b=obb;
awk -v a="$a" -v b="$b" "$1==a && $10=b" filename

输出文件行号以及每行的字符个数

1
awk '{print NR,length($0);}' filename

输出 column/field 的个数

1
awk '{print NF}'

判断是不是有逗号

1
awk '$1~/,/ {print}'

输出字符串出现 n 次之前的所有行

1
awk -v N=7 '{print}/bbo/&& --N<=0 {exit}'

输出文件名和其最后一行

1
ls|xargs -n1 -I file awk '{s=$0};END{print FILENAME,s}' file

向指定的 column 插入字符串

1
awk 'BEGIN{OFS="\t"}$3="chr"$3'

移除包含特定字符串的行

1
awk '!/bbo/' file

移除最后一个 column

1
awk 'NF{NF-=1};1' file

理解 NR 和 FNR 的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# For example there are two files:
# fileA:
# a
# b
# c
# fileB:
# d
# e
awk 'print FILENAME, NR,FNR,$0}' fileA fileB
# fileA 1 1 a
# fileA 2 2 b
# fileA 3 3 c
# fileB 4 1 d
# fileB 5 2 e

5. Xargs

设置 tab 为分隔符

1
xargs -d\t

每行显示三个元素

1
2
3
echo 1 2 3 4 5 6| xargs -n 3
# 1 2 3
# 4 5 6

执行之前先询问

1
2
3
$ echo a b c |xargs -p -n 3
$ echo a b c ?... #输入 y
$ a b c

find 文件并删除

1
find . -name "*.html"|xargs rm

删除文件名中含有空格的文件

1
find . -name "*.c" -print0|xargs -0 rm -rf

显示 limits

1
xargs --show-limits

移动文件

1
2
3
4
5
6
find . -name "*.bak" -print 0|xargs -0 -I {} mv {} ~/old

# or
find . -name "*.bak" -print 0|xargs -0 -I file mv file ~/old

ls |head -100|xargs -I {} mv {} d1

并发执行

1
2
3
4
time echo {1..5} |xargs -n 1 -P 5 sleep

# a lot faster than:
time echo {1..5} |xargs -n1 sleep

基于条件的文件拷贝

1
2
3
4
find /dir/to/A -type f -name "*.py" -print 0| xargs -0 -r -I file cp -v -p file --target-directory=/path/to/B

# v: verbose|
# p: keep detail (e.g. owner)

和 sed 配合使用

1
ls |xargs -n1 -I file sed -i '/^Pos/d' filename

将文件名加入到文件的第一行

1
ls |sed 's/.txt//g'|xargs -n1 -I file sed -i -e '1 i\>file\' file.txt

统计

1
ls |xargs -n1 wc -l

将输出整合到一行

1
ls -l| xargs

统计文件夹下面各个文件的行数和总的行数

1
ls|xargs wc -l

和 grep 联合使用

1
cat grep_list |xargs -I{} grep {} filename

和 sed 联合使用

1
grep -rl '192.168.1.111' /etc | xargs sed -i 's/192.168.1.111/192.168.2.111/g'

6. Find

递归列出所有的子目录和文件

1
find .

列出当前目录下的所有文件

1
find . -type f

列出当前目录下的所有子目录

1
find . -type d

修改当前目录下的所有文件(将 ‘www’ 替换成 ‘w’)

1
2
3
4
5
find . -name '*.php' -exec sed -i 's/www/w/g' {} \;

# if there are no subdirectory
replace "www" "w" -- *
# a space before *

删除大小小于 74 byte 的文件

1
2
3
find . -name "*.mso" -size -74c -delete

# M for MB, etc