JohnShen's Blog.

朝花夕拾 - Linux Shell 文本处理

字数统计: 2.1k阅读时长: 9 min
2021/07/19 Share

朝花夕拾 - Linux Shell 文本处理

A. grep

文本搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. --color可以在输出行中着重标记出匹配到的模式
grep --color=auto word filename
# 2. 选项-E可以使grep使用扩展正则表达式。也可以使用默认启用扩展正则表达式的egrep命令
grep -E "[a-z]+" filename
egrep "[a-z]+" filename
# 3. 选项-o可以只输出匹配到的文本
echo this is a line. | egrep -o "[a-z]+\." # line.
# 4. 选项-v可以打印出不匹配match_pattern的所有行
grep -v match_pattern file
# 5. 选项-n可以打印出匹配字符串所在行的行号
# 6. 选项-i可以在匹配模式时不考虑字符的大小写
# 7. 匹配多个模式
grep -e "pattern1" -e "pattern2"
# 8. 在静默模式中,grep命令不会输出任何内容。它仅是运行命令,然后根据命令执行成功与否返回退出状态。0表示匹配成功,非0表示匹配失败。
grep -q "$match_text" $filename
if [ $? -eq 0 ]; then
echo "The text exists in the file"
else
echo "Text does not exist in the file"
fi
# 9. 匹配之前或之后的行. -A可以打印匹配结果之后的行, -B可以打印匹配结果之前的行, -C可以分别打印出匹配结果之前及之后的n行

B. cut

cut命令可以按列来切分文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 制表符是字段默认的分隔符
# FIELD_LIST是需要显示的列。它由列号组成,彼此之间用逗号分隔
cut -f 2,3 filename
# 选项-d能够设置分隔符
cut -f 2 -d ":"
cut -f 2- -d @
# N- 从第N个字节、字符或字段开始到行尾
# N-M 从第N个字节、字符或字段开始到第M个(包括第M个在内)字节、字符或字段
# -M 从第1个字节、字符或字段开始到第M个(包括第M个在内)字节、字符或字段
# -b 表示字节 -c 表示字符 -f 用于定义字段
#cat range_fields.txt
#abcdefghijklmnopqrstuvwxyz
#abcdefghijklmnopqrstuvwxyz
cut -c2-5 range_fields.txt
#bcde
#bcde
cut -c -2 range_fields.txt
#ab
#ab
# --output-delimiter可以指定输出分隔符
cut range_fields.txt -c1-3,6-9 --output-delimiter ","
#abc,fghi
#abc,fghi

C. sed

sed是 stream editor(流编辑器) 的缩写。它最常见的用法是进行文本替换。

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
sed 's/pattern/replace_string/' file
# 选项-i会使得sed用修改后的数据替换原始文件
sed -i 's/text/replace/' file
#以上只替换了每行中模式首次匹配的内容。g标记可以使sed执行全局替换
sed -i 's/pattern/replace_string/g' file
#sed命令会将s之后的字符视为命令分隔符。这允许我们更改默认的分隔符
sed 's:text:replace:g'
sed 's|text|replace|g'
# 删除行
sed -i '/server/d' /etc/nginx/nginx.conf
sed -i '/^$/d' file # 移除空行, 空行可以用正则表达式 ^$ 进行匹配
# &可指代模式所匹配到的字符串,能够在替换字符串时使用已匹配的内容
echo this is an example | sed 's/\w\+/[&]/g' # 正则表达式\w\+匹配每一个单词
# [this] [is] [an] [example]
# 子串匹配标记(\1)
echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/' # \(pattern\)用于匹配子串. 对于匹配到的第n个子串,其对应的标记是\n
# this is 7 in a number
echo seven EIGHT | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
# EIGHT seven
#利用管道组合多表达式. 以下三句含义相同:
sed 'expression' | sed 'expression'
sed 'expression; expression'
sed -e 'expression' -e 'expression'
echo abc | sed 's/a/A/' | sed 's/c/C/' # AbC
echo abc | sed 's/a/A/;s/c/C/' # AbC
echo abc | sed -e 's/a/A/' -e 's/c/C/' # AbC

D. awk

awk命令用于高级文本处理,可以处理数据流。它支持关联数组、递归函数、条件语句等功能。

awk脚本通常由3部分组成:BEGIN、END和带模式匹配选项的公共语句块。这3个部分都是可选的,可以不用出现在脚本中。

1
awk 'BEGIN{ print "start" } pattern { commands } END{ print "end" }' file

awk以逐行的形式处理文件。BEGIN之后的命令会先于公共语句块执行。对于匹配PATTERN的行,awk会对其执行PATTERN之后的命令。最后,在处理完整个文件之后,awk会执行END之后的命令。

和pattern关联的语句块是可选的。如不提供则默认执行{ print },即打印所读取到的每一行。

1
2
3
4
5
echo -e "line1\nline2" | awk 'BEGIN { print "Start" } { print } END { print "End" } '
#Start
#line1
#line2
#End

print参数:

1
2
3
# 数以逗号分隔,在打印参数时则以空格作为参数之间的分隔符
echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1,var2,var3 ; }' # v1 v2 v3
echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1 "-" var2 "-" var3 ; }' # v1-v2-v3

特殊变量:

  • NR:表示记录编号,该变量相当于当前行号。
  • NF:表示字段数量,相当于字段数量,默认的字段分隔符是空格。
  • $0:该变量包含当前记录的文本内容。
  • $1:该变量包含第一个字段的文本内容。
  • $2:该变量包含第二个字段的文本内容。
1
2
3
4
# 打印出每一行的第二和第三个字段
awk '{ print $3, $2 }' file
# 使用NR统计文件的行数
awk 'END{ print NR }' file

字符分隔符:

默认为空格,可以使用-F指定。

1
2
3
awk -F: '{ print $NF }' /etc/passwd
awk -F ':' '{ print $NF }' /etc/passwd
awk 'BEGIN { FS=":" } { print $NF }' /etc/passwd

过滤模式进行行过滤:

1
2
3
4
awk 'NR < 5' # 行号小于5的行
awk 'NR==1,NR==4' # 行号在1到4之间的行
awk '/linux/' # 包含模式为linux的行(可以用正则表达式来指定模式)
awk '!/linux/' # 不包含模式为linux的行

getline:

得到的并不是当前行,而是当前行的下一行。getline之后,awk会改变对应的NF,NR,FNR和$0等内部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
seq 10 | awk '{getline; print $0}'
#2
#4
#6
#8
#10
seq 10 | awk '{print $0; getline}'
#1
#3
#5
#7
#9
seq 10 | awk '{getline tmp; print tmp; print $0}' # getline得到的下一行的内容放在了tmp这个变量里, NF NR FNR和$0等内部变量并不会被改变
#2
#1
#4
#3
#6
#5
#8
#7
#10
#9

从awk中读取命令输出:

把命令放入引号中,然后利用管道将命令输出传入getline:"command" | getline output ;

1
2
echo | awk 'BEGIN {FS=":"} { "grep root /etc/passwd" | getline; print $1,$6 }'
#root /root

关联数组和循环:

1
awk 'BEGIN {FS=":"} {nam[$1]=$5} END {for (i in nam) {print i,nam[i]}}' /etc/passwd

E. 日常demo

1. 统计特定文件中的词频

1
2
3
4
5
6
7
8
9
10
11
12
if [ $# -ne 1 ];
then
echo "Usage: $0 filename";
exit -1
fi
filename=$1
egrep -o "\b[[:alpha:]]+\b" $filename | \
awk '{ count[$0]++ }
END{ printf("%-14s%s\n","Word","Count") ;
for(ind in count)
{ printf("%-14s%d\n",ind,count[ind]);}
}

模式\b[[:alpha:]]+\b能够匹配每个单词并去除空白字符和标点符号。

2. 按列合并多个文件

默认的分隔符是制表符,也可以用-d指定分隔符:

1
2
3
4
5
6
paste file1.txt file2.txt -d ","
#1,slynux
#2,gnu
#3,bash
#4,hack
#5,

3. 打印行中第N个单词或列

1
2
3
awk '{ print $5 }' filename
# 可以打印多列数据并在各列间插入指定的字符串
ls -l | awk '{ print $1 " : " $8 }'

4. 打印指定行或模式之间的文本

1
2
3
4
5
6
7
8
9
10
11
12
13
awk 'NR==M, NR==N' filename
seq 100 | awk 'NR==4,NR==6'
# 打印位于模式start_pattern与end_pattern之间的文本
awk '/start_pattern/, /end_pattern/' filename
#cat section.txt
#line with pattern1
#line with pattern2
#line with pattern3
#line end with pattern4
#line with pattern5
awk '/pa.*3/, /end/' section.txt
#line with pattern3
#line end with pattern4

5. 逆序打印行

tac命令默认使用\n作为行分隔符。但我们也可以用选项-s指定其他分隔符

1
2
3
4
5
6
7
#tac命令默认使用\n作为行分隔符
seq 5 | tac
#5
#4
#3
#2
#1

或者通过awk:

1
seq 9 | awk '{ lifo[NR]=$0 } END { for(lno=NR;lno>-1;lno--){ print lifo[lno]; } }'

6. 解析文本中的电子邮件地址和URL

1
2
egrep -o '[A-Za-z0-9._]+@[A-Za-z0-9.]+\.[a-zA-Z]{2,4}' a.txt
egrep -o "http://[a-zA-Z0-9.]+\.[a-zA-Z]{2,3}" b.txt

7. 删除文件中包含特定单词的句子

1
2
# [^.]可以匹配除句点之外的任意字符
sed 's/ [^.]*mobile phones[^.]*\.//g' sentence.txt

8. 基础变量替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var="This is a line of text"
echo ${var/line/REPLACED}
#This is a REPLACED of text

# 字符串起始字符的索引从0开始
string=abcdefghijklmnopqrstuvwxyz
echo ${string:4}
#efghijklmnopqrstuvwxyz

echo ${string:4:8}
#efghijkl

echo ${string:(-1)}
#z
echo ${string:(-2):2}
#yz
CATALOG
  1. 1. 朝花夕拾 - Linux Shell 文本处理
    1. 1.1. A. grep
    2. 1.2. B. cut
    3. 1.3. C. sed
    4. 1.4. D. awk
    5. 1.5. E. 日常demo
      1. 1.5.1. 1. 统计特定文件中的词频
      2. 1.5.2. 2. 按列合并多个文件
      3. 1.5.3. 3. 打印行中第N个单词或列
      4. 1.5.4. 4. 打印指定行或模式之间的文本
      5. 1.5.5. 5. 逆序打印行
      6. 1.5.6. 6. 解析文本中的电子邮件地址和URL
      7. 1.5.7. 7. 删除文件中包含特定单词的句子
      8. 1.5.8. 8. 基础变量替换