shell 学习笔记 常用命令 grep sed awk

摘自 Linux Shell 脚本攻略 第四章 让文本飞

使用grep在文件中搜索文本

在stdin中搜索匹配特定模式的文本行

$ echo -e "this is a word\nnext line" | grep word
this is a word

在文件中搜索匹配特定模式的文本行

$ grep bin coco.sh 
#!/bin/bash
/USSR/bin/expect <<-OF &>/Devi/null

在多个文件中搜索匹配特定模式的文本行

$ grep "match_text" file1 file2 file3 ...

选项–color可以在输出行中着重标记出匹配到的模式。尽管该选项在命令行中的放置 位置没有强制要求,不过惯常作为第一个选项出现

$ grep --color=auto bin coco.sh 
#!/bin/bash
/USSR/bin/expect <<-OF &>/Devi/null

grep命令默认使用基础正则表达式。这是先前描述的正则表达式的一个子集。选项-E可以 使grep使用扩展正则表达式

$ grep -E "bin" coco.sh 
#!/bin/bash
/USSR/bin/expect <<-OF &>/Devi/null

选项-o可以只输出匹配到的文本

$ echo this is a line. |egrep -o "[a-z]+\."
line.

选项-v可以打印出不匹配match_pattern的所有行

$ echo -e "coco is handsome\nzues is handsome" |grep -v zues
coco is handsome

选项-c能够统计出匹配模式的文本行数

$ echo -e "1 2 3 4\nhello\n5 6" | egrep -c "[0-9]"
2

要统计文件中匹配项的数量,可以使用下面的技巧

$ echo -e "1 2 3 4\nhello\n5 6" | egrep -o "[0-9]" | wc -l
6

选项-n可以打印出匹配字符串所在行的行号

$ cat sample1.txt 
gnu is not unix
linux is fun
bash is art

$ cat sample2.txt 
planetlinux

$ cat sample1.txt |grep linux -n
2:linux is fun

如果涉及多个文件,该选项也会随输出结果打印出文件名

$ grep linux -n sample1.txt sample2.txt
sample1.txt:2:linux is fun
sample2.txt:1:planetlinux

选项-b可以打印出匹配出现在行中的偏移。配合选项-o可以打印出匹配所在的字符或 字节偏移

$ echo gnu is not unix | grep -b -o "not"
7:not

字节偏移(下标)从0开始计数

选项-l可以列出匹配模式所在的文件

$ grep -l linux sample1.txt sample2.txt coco.sh 
sample1.txt
sample2.txt

递归搜索多个文件

$ grep 'bin' for_fun/  -R -n
for_fun/aa.json:81:  'Robin Zhu': {
for_fun/coco.py:15:    'Robin Zhu': {'issue_info': {'OPEN': 1}}, 'Guofeng Tang': {'issue_info': {'OPEN': 5}}, 'Zhiheng Cao': {
# 等价于下面这条命令
$ find for_fun/ -type f | xargs grep "bin"
for_fun/aa.json:  'Robin Zhu': {
for_fun/coco.py:    'Robin Zhu': {'issue_info': {'OPEN': 1}}, 'Guofeng Tang': {'issue_info': {'OPEN': 5}}, 'Zhiheng Cao': {

忽略模式中的大小写

$ echo hello world | grep -i "HELLO"
hello world

使用grep匹配多个模式

$ echo this is a line of text | grep -o -e "this" -e "line"
this
line

可以将多个模式定义在文件中。选项-f可以读取文件并使用其中的模式(一个模式一行)

$ cat pat_file
hello
cool

$ echo hello this is cool | grep -f pat_file
hello this is cool

在grep搜索中指定或排除文件

grep可以在搜索过程中使用通配符指定(include)或排除(exclude)某些文件
使用–include选项在目录中递归搜索所有的 .c和 .cpp文件

$ grep "main()" . -r  --include=*.c --include=*.cpp

示例
在当前目录下递归查找 包含 bin 字符的 .sh 以及 .py 文件

$ grep --include=*.sh --include=*.py "bin" . -r 
./for_fun/coco.py:    'Robin Zhu': {'issue_info': {'OPEN': 1}}, 'Guofeng Tang': {'issue_info': {'OPEN': 5}}, 'Zhiheng Cao': {
./coco.sh:#!/bin/bash
./coco.sh:/USSR/bin/expect <<-OF &>/Devi/null
./shift.sh:#!/bin/bash
./filestat.sh:# !/bin/bash
./debug.sh:#!/bin/bash
./cecho.sh:#!/bin/bash 
./build1.sh:#!/bin/bash
./remove_duplicates.sh:# !/bin/bash 
./a/remove_duplicates.sh:# !/bin/bash 

在当前目录下递归查找 包含 bin 字符的 .sh 以及 .py 文件 但不包括 coco.sh

$ grep --include=*.sh --include=*.py  --exclude=coco.sh "bin" . -r 
./for_fun/coco.py:    'Robin Zhu': {'issue_info': {'OPEN': 1}}, 'Guofeng Tang': {'issue_info': {'OPEN': 5}}, 'Zhiheng Cao': {
./shift.sh:#!/bin/bash
./filestat.sh:# !/bin/bash
./debug.sh:#!/bin/bash
./cecho.sh:#!/bin/bash 
./build1.sh:#!/bin/bash
./remove_duplicates.sh:# !/bin/bash 
./a/remove_duplicates.sh:# !/bin/bash 

在当前目录下递归查找 包含 bin 字符的 .sh 以及 .py 文件 但不包括 for_fun 文件夹

$ grep --include=*.sh --include=*.py  --exclude-dir=for_fun/ "bin" . -r 
./coco.sh:#!/bin/bash
./coco.sh:/USSR/bin/expect <<-OF &>/Devi/null
./shift.sh:#!/bin/bash
./filestat.sh:# !/bin/bash
./debug.sh:#!/bin/bash
./cecho.sh:#!/bin/bash 
./build1.sh:#!/bin/bash
./remove_duplicates.sh:# !/bin/bash 
./a/remove_duplicates.sh:# !/bin/bash 

使用0值字节后缀的xargs与grep

xargs命令可以为其他命令提供命令行参数列表。当文件名作为命令行参数时,建议用0值 字节作为文件名终结符,而非空格。因为一些文件名中会包含空格字符,一旦它被误解为终结符, 那么单个文件名就会被视为两个(例如,New file.txt被解析成New和file.txt两个文件名)。这个问 题可以利用0值字节后缀来避免。我们使用xargs从命令(如grep和find)中接收stdin文本。 这些命令可以生成带有0值字节后缀的输出。为了指明输入中的文件名是以0值字节作为终结,需 要在xargs中使用选项-0

$ echo "test" > file1
$ echo "cool" > file2
$ echo "test" > file3

$ grep "test" file* -lZ | xargs -0 rm
$ ls file*
file2  filestat.sh

选项-l告诉grep只输出有匹配出现的文件名。选项-Z使得grep使用0值字节(\0)作为文 件名的终结符。这两个选项通常都是配合使用的。xargs的-0选项会使用0值字节作为输入的分 隔符

grep的静默输出 -q

$ cat silent_grep.sh 
#!/bin/bash
# 文件名: silent_grep.sh
# 用途: 测试文件是否包含特定的文本内容
if [ $# -ne 2 ]; then
      echo "Usage: $0 match_text filename"
      exit 1
fi
match_text=$1
filename=$2
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

$ ./silent_grep.sh bin coco.sh 
The text exists in the file

打印出匹配文本之前或之后的行

  • 选项-A可以打印匹配结果之后的行
$ seq 10 | grep 5 -A 3
5
6
7
8
  • 选项-B可以打印匹配结果之前的行
$ seq 10 | grep 5 -B 3
2
3
4
5
  • 选项-A和-B可以结合使用,或者也可以使用选项-C,它可以分别打印出匹配结果之前及之 后的n行
$ seq 10 | grep 5 -C 3
2
3
4
5
6
7
8
  • 如果有多个匹配,那么使用–作为各部分之间的分隔
$ echo -e "a\nb\nc\na\nb\nc" | grep a -A 1
a
b
--
a
b

使用sed替换文本

sed是stream editor(流编辑器)的缩写。它最常见的用法是进行文本替换。这则攻略中包括
了大量sed命令的常见用法

sed可以使用另一个字符串来替换匹配模式。模式可以是简单的字符串或正则表达式

$ sed 's/pattern/replace_string/' file

选项-i会使得sed用修改后的数据替换原始文件

$ sed -i 's/text/replace/' file

g标记可以使sed执行全局替换

$ sed 's/pattern/replace_string/g' file
  • /#g标记可以使sed替换第N次出现的匹配
$ echo thisthisthisthis | sed 's/this/THIS/2g'
thisTHISTHISTHIS
$ echo thisthisthisthis | sed 's/this/THIS/3g'
thisthisTHISTHIS
$ echo thisthisthisthis | sed 's/this/THIS/4g'
thisthisthisTHIS

sed命令会将s之后的字符视为命令分隔符

将默认分隔符修改为:

sed 's:text:replace:g'

将默认分割符修改为

sed 's|text|replace|g'

如果作为分隔符的字符出现在模式中,必须使用\对其进行转义

sed 's|te\|xt|replace|g'

删除空行

$ sed '/^$/d' file

直接在文件中替换

如果将文件名传递给sed,它会将文件内容输出到stdout。要是我们想就地(in place)修
改文件内容,可以使用选项-i

$ sed 's/PATTERN/replacement/' -i filename

示例

$ cat sed_data.txt 
11 abc 111 this 9 file contains 111 11 88 numbers 0000
$ sed -i 's/\b[0-9]\{3\}\b/NUMBER/g' sed_data.txt
$ cat sed_data.txt 
11 abc NUMBER this 9 file contains NUMBER 11 88 numbers 0000

上面的单行命令只替换了所有的3位数字。正则表达式\b[0-9]{3}\b用于匹配3位数字。[0-9] 表示数字取值范围是从0到9。{3}表示匹配之前的数字3次。{3}中的\用于转义{和}。\b表示 单词边界

已匹配字符串标记(&)

$ echo this is an example | sed 's/\w\+/[&]/g'
[this] [is] [an] [example]

在这个例子中,正则表达式\w+匹配每一个单词,然后我们用[&]替换它。&对应于之前所匹配 到的单词

子串匹配标记(\1)

$ echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
this is 7 in a number

这条命令将digit 7替换为7。(pattern)用于匹配子串,在本例中匹配到的子串是7。子模式 被放入使用反斜线转义过的()中。对于匹配到的第一个子串,其对应的标记是\1,匹配到的第 二个子串是\2,往后以此类推

$ echo seven EIGHT | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
EIGHT seven

([a-z]+)匹配第一个单词,([A-Z]+)匹配第二个单词。\1和\2分别用来引用这两个单 词。这种引用形式叫作向后引用(back reference)。在替换部分,它们的次序被更改为\2 \1, 因此就呈现出了逆序的结果

组合多个表达式

可以利用管道组合多个sed命令,多个模式之间可以用分号分隔,或是使用选项-e PATTERN:

# 以下三个相等
$ 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

引用

sed表达式通常用单引号来引用。不过也可以使用双引号。shell会在调用sed前先扩展双引
号中的内容。如果想在sed表达式中使用变量,双引号就能派上用场了

$ echo hello world | sed "s/$text/HELLO/"
HELLO world

使用awk进行高级文本处理

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

awk ‘BEGIN{ print “start” } pattern { commands } END{ print “end” }’ file

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

输出文件行数

$ awk 'BEGIN {i=0}{i++}END{print i}' a.txt 
4
$ awk "BEGIN {i=0}{i++}END{print i}" a.txt 
4

当使用不带参数的print时,它会打印出当前行

$ echo -e "line1\nline2" | awk 'BEGIN {print "Start" } { print } END { print "End" }'
Start
line1
line2
End

print能够接受参数。这些参数以逗号分隔,在打印参数时则以空格作为参数之间的分隔符。
在awk的print语句中,双引号被当作拼接操作符(concatenation operator)使用

$ echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1,var2,var3;}'
v1 v2 v3

echo命令向标准输出写入一行,因此awk的 { } 语句块中的语句只被执行一次。如果awk的 输入中包含多行,那么 { } 语句块中的语句也就会被执行相应的次数

对输出进行拼接

$ echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1 "-" var2 "-" var3;}'
v1-v2-v3

特殊变量

  • NR:表示记录编号,当awk将行作为记录时,该变量相当于当前行号。
  • NF:表示字段数量,在处理当前记录时,相当于字段数量。默认的字段分隔符是空格。  $0:该变量包含当前记录的文本内容。
  • $1:该变量包含第一个字段的文本内容。
  • $2:该变量包含第二个字段的文本内容。
$ echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | awk '{print "Line No:"NR",No of fileds:"NF, "$0="$0, "$1="$1,"$2="$2,"$3="$3}'
Line No:1,No of fileds:3 $0=line1 f2 f3 $1=line1 $2=f2 $3=f3
Line No:2,No of fileds:3 $0=line2 f4 f5 $1=line2 $2=f4 $3=f5
Line No:3,No of fileds:3 $0=line3 f6 f7 $1=line3 $2=f6 $3=f7
$ seq 5 | awk 'BEGIN{ sum=0;print "Summation:" }{print $1"+";sum+=$1}END{print "==";print sum}'
Summation:
1+
2+
3+
4+
5+
==
15

将外部变量值传递给awk

借助选项-v,我们可以将外部值(并非来自stdin)传递给awk

$ VAR=10000
$ echo | awk -v VARIABLE=$VAR '{print VARIABLE}'
10000

$ var1="Variable1" ; var2="Variable2"
$ echo | awk '{print v1,v2}' v1=$var1 v2=$var2
Variable1 Variable2

$ awk '{ print v1,v2 }' v1=$var1 v2=$var2 file2 
Variable1 Variable2

用getline读取行

awk默认读取文件中的所有行。如果只想读取某一行,可以使用getline函数。它可以用于

在BEGIN语句块中读取文件的头部信息,然后在主语句块中处理余下的实际数据

该函数的语法为:getline var。变量var中包含了特定行。如果调用时不带参数,我们可
以用 $0、$1和$2访问文本行的内容

$ seq 5 | awk 'BEGIN { getline; print "Read ahead first line", $0 } { print $0 }'
Read ahead first line 1
2
3
4
5

使用过滤模式对awk处理的行进行过滤

# 行号小于5的行
$ awk '$NF < 4' coco.sh 
#!/bin/bash


/USSR/bin/expect <<-OF &>/Devi/null

# 行号在1到5之间的行
$ awk 'NR==1,NR==4' coco.sh 
#!/bin/bash


/USSR/bin/expect <<-OF &>/Devi/null

# 包含模式为bin的行(可以用正则表达式来指定模式)
$ awk '/bin/' coco.sh 
#!/bin/bash
/USSR/bin/expect <<-OF &>/Devi/null

# 不包含模式为linux的行
$ awk '!/bin/' coco.sh 


spawn ssh amlogic@10.18.19.233
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "Linux2021\r" }
}
expect of
OF

设置字段分隔符

默认的字段分隔符是空格。我们也可以用选项-F指定不同的分隔符

$ awk -F: '{ print $NF }' /etc/passwd |head 
/bin/bash
/usr/sbin/nologin
/usr/sbin/nologin
/usr/sbin/nologin
/bin/sync
/usr/sbin/nologin
/usr/sbin/nologin
/usr/sbin/nologin
/usr/sbin/nologin
/usr/sbin/nologin

# 等同于上面那条命令
$ awk 'BEGIN { FS=":" } { print $NF }' /etc/passwd

从awk中读取命令输出

awk可以调用命令并读取输出。把命令放入引号中,然后利用管道将命令输出传入getline

$ echo | awk '{"ls -l" |getline;print}'
total 391772

awk的关联数组

$ cat A.txt 
apple
gold
iron
orange
silver
steel 

$ awk '{a[$1]++}END{for (i in a){print i,a[i]}}' A.txt 
steel 1
gold 1
silver 1
orange 1
iron 1
apple 1

在awk中使用循环

% awk -F : '{name[$1]=$5}END{for (i in name){print i name[i]}}' /etc/passwd |head -n 5
_appownerApplication Owner
rootSystem Administrator
_krb_changepwOpen Directory Kerberos Change Password Service
_appserverApplication Server
_applepayapplepay Account

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包含了匹配内容的长度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值