必会系统命令

grep

grep 命令很容易学习,它主要有两种使用方式,一种是单独使用,比如搜索某个文件中的内容:

grep 'content' file.txt

或者从标准输入中搜索内容:

echo 'something' | grep 'some'

要想掌握好 grep,重点在于了解它的各种参数。下面是一些常用的参数,如果不记得,后续可以用 man grep 命令来查阅。

grep 在搜索时,默认是大小写敏感的,但如果要搜索 mysql,它可能写做 mysql 也可能写做 MySQL,这就可能存在搜索不到的问题,此时可以用 -i 参数:

echo 'MySQL' | grep -i 'mysql'

如果使用 -n 参数可以打印匹配行的行号,使用 -H 参数可以打印匹配文件的文件名。

默认情况下,如果某个二进制文件中含有搜索的关键词,会显示 Binary file ... matches,使用 -I 选项可以忽略二进制文件,使用 -a选项可以把二进制文件当做文本文件来处理,从而输出匹配的部分。

默认情况下 grep 会展示匹配的那一行,如果想查看上下文,可以使用 -A-B-C 这三个参数:

  1. -A 3:展示匹配行以及后面的 3 行
  2. -B 3:展示匹配行以及前面的 3 行
  3. -C 3:展示匹配行以及前后的 3 行,等价于 -A 3 -B 3

另外一些常用的选项包括 -v,表示只显示那些不匹配的行,-o 表示只显示匹配的部分,-q 表示不输出内容,通常与 if 连用。

xargs

在前面的章节中我们介绍过,可以通过管道将多个命令串联起来,前提是管道后面的命令要支持从标准输入中读取数据,比如前文的 grep 命令。

然而有些命令并不支持从标准输入中读取,比如这样写是无效的:

echo 'file_name' | rm

此时我们可以借助 xargs 命令:

echo "a" | xargs rm

这条命令的原理是,xargs 会把换行符、空格、制表符、EOF等符号做为分隔符,把输入的内容切分为一个数组,并把数组中每一个元素作为参数,放到后面的命令中执行,用伪代码来写就是:

for arg in read_input; do
    rm arg
done

很常见的一个坑就是,如果文件名带有空格,比如 hello world 就会被 xargs 截断为两个参数,显然不符合预期。不过一般对内容或者文件进行过滤时,我们都会使用 grepfind,这两个命令都有办法配合 xargs

ls | grep 'a' | tr "\n" "\0" | xargs -0 rm

grep 的话会繁琐一些,需要用 tr 命令把换行符转换成特殊字符 \0,再利用 xargs-0 参数,根据文档所述,这个参数会把分隔符指定为 -0,从而避免了文件名中含有空格的影响。

find 也是类似的原理:

find . -print0 | xargs -0 rm

只不过它自带了 -print0选项,写法更简单。

sed

sed 诞生于 1977 年,已经 41 岁了,这么一位叔叔级别的命令至今还活跃在各种 Shell 脚本中,由此可见它是多么重要。

Mac 自带的时 BSD 版本的 sed,因为功能较弱,我不推荐使用,建议使用 gsed,如无特殊说明,下文的介绍都是针对 gsed的。

brew install coreutils
which gsed
# /usr/local/bin/gsed

sedgrep 的用法类似,都是 sed pattern file 或者 echo 'xx' | sed pattern,也就是说第二个参数可以是文件,也可以从标准输入流中读取。

最标准的用法是进行文本替换(也可以用 tr 命令实现):

echo "a b\nc d"
# a b
# c d
echo "a b\nc d" | gsed 's/a/aa/g'
# aa b
# c d

有时候我们可能不止使用一次 sed,此时可以用 -e 参数把多个命令串联起来:

echo "a b\nc d" | gsed -e 's/a/aa/g' -e 's/b/bb/g'

gsed 中,还可以使用 Shell 里定义的变量:

old=a
new=aa
echo "a b\nc d" | gsed "s/$old/$new/g"

我推荐用 gsed 是因为它有一个 -i 选项,可以对文件进行原地修改:

gsed -i 's/a/aa/g' file

sed 最核心的部分在于这里的 s/a/aa/g,它由若干个斜杠组成(其实也不一定要用斜杠,只要保持一致就行)。这里的 s 表示替换,a 表示待匹配的内容,支持正则,aa 表示替换后的内容,g 表示全部替换,更多的用法有:

  1. 1,3s/a/aa/g:只替换第一到三行中的内容
  2. s/a/aa/1:只替换每行第一个 a
  3. s/a/aa/2g:每行前两个 a 不替换,从第三个开始替换
  4. 2i sss:在第二行前面追加一行,内容为 sss
  5. 1a sss:在第一行后面追加一行,内容为 sss,等价于 2i sss
  6. /a/a sss:遇到有字母 a 的行,就在后面追加一行 sss
  7. 1c sss:把第一行替换为 sss
  8. 2d:删除第二行

这些用法虽然看起来复杂,但是和 vim 一样,每个部分就几种写法,然后自行排列组合即可。

gsed 在默认情况下,会把输入的每一行都输出一遍,它有一个常用的选项是 -n,表示不输出任何一行。通常与 p 命令合用,这个命令可以打印匹配的行,类似于 grep 的效果。

awk

awk 是和 sed 同时代的命令,并称为文本处理两大神器。个人认为 sed 的强大之处在于文本匹配后的处理,而 awk 则更适合文本的结构化处理。

这里以获取 ip 地址的命令来介绍下:

ifconfig | sed -n -e '/127.0.0.1/d' -e '/inet /p' | awk '{print $2}'

这里 awk 的用法其实很简单,就是打印第二列。awk 的核心在于内建的变量:

$0 当前记录(这个变量中存放着整个行的内容)
$1~$n 当前记录的第n个字段,字段间由FS分隔
FS 输入字段分隔符 默认是空格或Tab
NF 当前记录中的字段个数,就是有多少列
NR 已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中。
FNR 当前记录数,与NR不同的是,这个值会是各个文件自己的行号
RS 输入的记录分隔符, 默认为换行符

awk 一个很常见的用法是 -f 参数,可以指定输入字段的分隔符:

echo "a;b;c" | awk -F';' '{print $2}'

其实理论上来说,awksed 还要强大,因为它是一个图灵完备的语言,支持 for 循环等等编程思想。建议感兴趣的读者阅读 AWK 简明教程 了解更多 awk 的使用技巧

results matching ""

    No results matching ""