Shell - 初探(四)

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命令的工作方式如下。

  1. 首先执行BEGIN { commands } 语句块中的语句

  2. 接着从文件或stdin中读取一行,如果能够匹配pattern,则执行随后的commands语句块。重复这个过程,直到文件全部被读取完毕。

  3. 当读至输入流末尾时,执行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包含了匹配内容的长度。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值