1. grep命令
# 在stdin中搜索匹配特定模式的文本行:
echo "hello world" | grep "hel"
# output: hello world
# 在文件中搜索匹配特定模式的文本行
grep 1/1/11 1.txt
# output: 1 1/1/11 default inactive
# 在多个文件中搜索匹配特定模式的文本行:
grep "hello" 1.txt 2.txt
# 使用正则表达式
grep -E '^[^0-9]+$' 1.txt
# 选项-o可以只输出匹配到的文本
grep -Eo '1/1/11' 1.txt
# output: 1/1/11
# 选项-v可以打印出不匹配的所有行
grep -Ev '1/1/11' 1.txt
# 选项-c能够统计出匹配模式的文本行数
grep -Ec '1/1/11' 1.txt
# 选项-n可以打印出匹配字符串所在行的行号
cat 1.txt | grep 1/1/11 -n
# 选项-b可以打印出匹配出现在行中的偏移
grep -b '1/1/11' 1.txt
# 选项-l可以列出匹配模式所在的文件(和-l效果相反的选项是-L,它会返回一个不匹配的文件列表)
grep -l 1/1/11 ./*
# output: ./1.txt
# 递归搜索多个文件
grep "text" . -R -n #等价于find . -type f | xargs grep "text"
# 忽略模式中的大小写
grep -i 'test' 1.txt
# 使用grep匹配多个模式
grep -e 1/1/11 -e 1/1/12 1.txt
grep -E '1/1/11|1/1/12' 1.txt
# 在grep搜索中指定或排除文件(grep可以在搜索过程中使用通配符指定(include)或排除(exclude)某些文件)
grep "hello" . -r --include=*.{txt,log}
grep "1/1/111" . -r --include=*.{txt,log} --exclude-dir=test
# 使用0值字节后缀的xargs与grep
grep test file* -lZ | xargs -0 rm
# grep的静默输出(并不打算查看匹配的字符串,而只是想知道是否能够成功匹配。这可以通过设置grep的静默选项(-q)来实现)
grep test file* -qlZ
# 打印出匹配文本之前或之后的行
grep 1/1/11 1.txt -B 2 -A 2
grep 1/1/11 -C 3 1.txt
2. cut命令
# 选项-f可以指定要提取的字段;选项-d能够设置分隔符,默认是制表符(Tab);也可以用 --complement选项显示出没有被-f指定的那些字段
cut -f 2,3 -d ";" 3.txt
# 查看cut参数的含义
cut --help
2.1 指定字段的字符或字节范围
N- | 从第N个字节、字符或字段开始到行尾 |
---|---|
N-M | 从第N个字节、字符或字段开始到第M个(包括第M个在内)字节、字符或字段 |
-M | 从第1个字节、字符或字段开始到第M个(包括第M个在内)字节、字符或字段 |
结合下列选项将字段指定为某个范围内的字节、字符或字段:
- -b 表示字节
- -c 表示字符
- -f 用于定义字段
cat 4.txt
# output: qweqdklfcvxcejrweljks
# 打印第2个到第5个字符
cut -c 2-5 4.txt
# output: weqd
# 打印前5个字符:
cut -c -5 4.txt
# output: qweqd
# 打印不连续的字符,并用--output-delimiter参数指定输出分隔符
cut -c 1-3,6-10 --output-delimiter=, 4.txt
# output: qwe,klfcv
3. sed命令
sed是stream editor(流编辑器)的缩写。它最常见的用法是进行文本替换。
# sed可以使用另一个字符串来替换匹配模式。模式可以是简单的字符串或正则表达式:
# 原型:sed 's/pattern/replace_string/' file
cat 4.txt
# output: hello world!
sed 's/world/xxxxx/' 4.txt
# output: hello xxxxx!
# sed从stdin中读取输入:
cat 4.txt | sed 's/xxxxx/world/'
# output: hello world!
cat 4.txt
# output: a:h:e:l:l:o
cat 4.txt | cut -f 1,2 -d ":" | sed 's/:/-/'
# output: a-h
# 选项-i会使得sed用修改后的数据替换原始文件:
sed -i 's/:/-/' 4.txt
# 之前的例子只替换了每行中模式首次匹配的内容。g标记可以使sed执行全局替换:
cat 4.txt
# output: a:h:e:l:l:o
sed 's/:/-/' 4.txt
# output: a-h:e:l:l:o
sed 's/:/-/g' 4.txt
# output: a-h-e-l-l-o
# #g标记可以使sed替换第N次出现的匹配:
sed 's/:/-/3g' 4.txt
# output: a:h:e-l-l-o
# sed命令会将s之后的字符视为命令分隔符。这允许我们更改默认的分隔符/:
sed 's:text:replace:g'
sed 's|text|replace|g'
# 移除空行(空行可以用正则表达式 ^$ 进行匹配;/d告诉sed不执行替换操作,而是直接删除匹配到的空行)
sed '/^$/d' 4.txt
cat 4.txt
# output: 11 abc 111 this 9 file contains 111 11 88 numbers 0000
# \b 是正则表达式规定的一个特殊代码(metacharacter),代表着单词边界
sed 's/\b[0-9]\{3\}\b/NUMBER/g' 4.txt
# output: 11 abc NUMBER this 9 file contains NUMBER 11 88 numbers 0000
# 直接在文件中替换且备份(这时的sed不仅替换文件内容,还会创建一个名为file.bak的文件,其中包含着原始文件内容的副本。)
sed -i.bak 's/abc/def/' file
# 已匹配字符串标记(&)
echo this is 1 an example | sed 's/\w\+/[&]/g'
# output: [this] [is] [1] [an] [example]
# 子串匹配标记(\1)(\#来指代出现在括号中的部分正则表达式;对于匹配到的第一个子串,其对应的标记是\1,匹配到的第二个子串是\2,往后以此类推;\(pattern\)用于匹配子串)
echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
# output: this is 7 in a number
echo seven EIGHT | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
# output: EIGHT seven
# 组合多个表达式(多个模式之间可以用分号分隔,或是使用选项-e PATTERN)
sed 'expression' | sed 'expression'
等同于
sed 'expression; expression'
等同于
sed -e 'expression' -e 'expression'
echo abc | sed 's/a/A/' | sed 's/c/C/'
# output: AbC
echo abc | sed 's/a/A/;s/c/C/'
echo abc | sed -e 's/a/A/' -e 's/c/C/'
# 引用
$ text=hello
$ echo hello world | sed "s/$text/HELLO/"
# output: HELLO world
4. awk命令
awk命令可以处理数据流。它支持关联数组、递归函数、条件语句等功能。
awk脚本的结构如下:
awk ‘BEGIN{ print “start” } pattern { commands } END{ print “end” }’ file
awk命令也可以从stdin中读取输入。
awk脚本通常由3部分组成:BEGIN、END和带模式匹配选项的公共语句块(common statement block)。这3个部分都是可选的,可以不用出现在脚本中。
awk以逐行的形式处理文件。BEGIN之后的命令会先于公共语句块执行。对于匹配PATTERN的行,awk会对其执行PATTERN之后的命令。最后,在处理完整个文件之后,awk会执行END之后的命令。
awk ‘BEGIN { statements } { statements } END { end statements }’
或者
awk “BEGIN { statements } { statements } END { end statements }”
# 下面的命令会输出文件行数
cat 4.txt
# output: 11 abc 111 this 9 file contains 111 11 88 numbers 0000
awk 'BEGIN {i=0} {i++} END {print i}' 4.txt
# output: 1
awk命令的工作方式如下。
-
首先执行BEGIN { commands } 语句块中的语句
-
接着从文件或stdin中读取一行,如果能够匹配pattern,则执行随后的commands语句块。重复这个过程,直到文件全部被读取完毕。
-
当读至输入流末尾时,执行END { commands } 语句块。
echo -e "line1\nline2" | awk 'BEGIN { print "Start" } { print } END { print "End" } ' # output: Start # output: line1 # output: line2 # output: End # print能够接受参数。这些参数以逗号分隔,在打印参数时则以空格作为参数之间的分隔符。在awk的print语句中,双引号被当作拼接操作符(concatenation operator)使用。 echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1,var2,var3 ; }' # output: v1 v2 v3 # 拼接的使用方法 echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1 "-" var2 "-" var3 ; }' # output: v1-v2-v3
4.1 特殊变量
以下是awk可以使用的一些特殊变量:
- NR:表示记录编号,当awk将行作为记录时,该变量相当于当前行号。
- NF:表示字段数量,在处理当前记录时,相当于字段数量。默认的字段分隔符是空格。
- $0:该变量包含当前记录的文本内容。
- $1:该变量包含第一个字段的文本内容。
- $2:该变量包含第二个字段的文本内容。
echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | \
awk '{ print "Line no:"NR",No of fields:"NF, "$0="$0, "$1="$1,"$2="$2,"$3="$3 }'
# output: Line no:1,No of fields:3 $0=line1 f2 f3 $1=line1 $2=f2 $3=f3
# output: Line no:2,No of fields:3 $0=line2 f4 f5 $1=line2 $2=f4 $3=f5
# output: Line no:3,No of fields:3 $0=line3 f6 f7 $1=line3 $2=f6 $3=f7
# 打印出每一行的第二和第三个字段:
echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | awk '{ print $3, $2 }'
# output: f3 f2
# output: f5 f4
# output: f7 f6
# 使用NR统计文件的行数
echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | awk 'END{ print NR }'
# output: 3
# 将每一行的第一个字段累加
seq 5 | awk 'BEGIN{sum=0} {print $1"+";sum+=$1} END{print sum}'
4.2 将外部变量值传递给awk
# 借助选项-v,我们可以将外部值(并非来自stdin)传递给awk
VAR=10000
echo | awk -v VARIABLE=$VAR '{ print VARIABLE }'
# output: 10000
# 将多个外部变量传递给awk
var1="Variable1" ; var2="Variable2"
echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2
# 当输入来自于文件而非标准输入时,使用下列命令
awk '{ print v1,v2 }' v1=$var1 v2=$var2 filename
4.3 用getline读取行
awk默认读取文件中的所有行。如果只想读取某一行,可以使用getline函数。它可以用于在BEGIN语句块中读取文件的头部信息,然后在主语句块中处理余下的实际数据。
seq 5 | awk 'BEGIN { getline; print "Read ahead first line", $0 } { print $0 }'
4.4 使用过滤模式对awk处理的行进行过滤
# 我们可以为需要处理的行指定一些条件:
cat 3.txt
#output:No Name Mark Percent
#output:1 Sarath 45 90
#output:2 Alex 49 98
#output:3; Anu; 45; 90
# 行号小于5的行
awk 'NR < 2' 3.txt
#output:No Name Mark Percent
# 行号在1到5之间的行
awk 'NR==1,NR==3' 3.txt
#output:No Name Mark Percent
#output:1 Sarath 45 90
#output:2 Alex 49 98
# 包含模式为linux的行(可以用正则表达式来指定模式)
awk '/Alex/' 3.txt
#output: 2 Alex 49 98
# 不包含模式为linux的行
awk '!/Alex/' 3.txt
#output: No Name Mark Percent
#output: 1 Sarath 45 90
#output: 3; Anu; 45; 90
4.5 设置字段分隔符
# 默认的字段分隔符是空格。我们也可以用选项-F指定不同的分隔符:
awk -F: '{ print $NF }' /etc/passwd
awk 'BEGIN { FS=":" } { print $NF }' /etc/passwd
#在BEGIN语句块中可以用OFS="delimiter"设置输出字段分隔符
4.6 在awk中使用循环
# awk还支持列表形式的for循环,也可以显示出数组的内容:
# for(i in array) { print array[i]; }
awk 'BEGIN {FS=":"} {nam[$1]=$5} END {for (i in nam) {print i,nam[i]}}' /etc/passwd
#output: freerad
#output: gnats Gnats Bug-Reporting System (admin)
#output: sshd
#output: tcpdump
4.7 awk内建的字符串处理函数
awk有很多内建的字符串处理函数:
- length(string):返回字符串string的长度。
- index(string, search_string):返回search_string在字符串string中出现的位置。
- split(string, array, delimiter):以delimiter作为分隔符,分割字符串string,将生成的字符串存入数组array。
- substr(string, start-position, end-position) :返回字符串 string 中 以start-position和end-position作为起止位置的子串。
- sub(regex, replacement_str, string):将正则表达式regex匹配到的第一处内容替换成replacment_str。
- gsub(regex, replacement_str, string):和sub()类似。不过该函数会替换正则表达式regex匹配到的所有内容。
- match(regex, string):检查正则表达式regex是否能够在字符串string中找到匹配。如果能够找到,返回非0值;否则,返回0。match()有两个相关的特殊变量,分别是RSTART和RLENGTH。变量RSTART包含了匹配内容的起始位置,而变量RLENGTH包含了匹配内容的长度。