Linux-Chap4-Shell编程进阶

本文详细介绍了Linux Shell中的过滤器命令,如pr、head、tail、cut、paste、sort、uniq、tr、cmp、comm、diff等,并重点讲解了grep和sed的正则表达式应用,以及awk的强大文本处理能力,包括字段处理、数值计算、正则匹配和程序设计构造。通过实例分析,帮助读者掌握这些实用工具的用法。

 

简单过滤器命令

简单过滤器功能:处理文件中的数据、编辑页面格式、截取文件内容、比较文件内容、字段和排序。

pr:改变文件打印格式。

-l n 设定页面长度为n行,默认为66 
-w n 设定页面宽度为n字符,默认为72,仅分列显示时有效(宽度不够时截掉)
-h str 设定页眉为str 
-n 对行进行编号 
-k 分k列输出 
-t 不显示页眉、页脚和边距

head:显示文件的开头。

tail:显示文件的结尾。

head -n k 指定显示文件前k行的内容
tail -n k 指定显示文件后k行的内容 
     -f 监控文件内容的增长

wh1516@PinkRAY:~/桌面$ ls -F | pr -n -l 30 -w 40 -3 | head -n 10


2021-01-13 19:01                 第 1 页


    1	cach 	 6   f1	     11	  lab4
    2	ch3/ 	 7   f2	     12	  linu
    3	CH4/ 	 8   f3	     13	  linu
    4	ch6e 	 9   lab2    14	  linu
    5	data 	10   lab3    15	  OS/

cut:垂直划分文件。

-c 按列剪切,每列宽度为一个字符 
-f 按字段剪切,字段分隔符由-d选项指定,默认为制表符 
-d 指定字段分隔符

cut -c1-5 passwdcp
root:
daemo
bin:x

cut -d: -f1-3 passwdcp
root:x:0
daemon:x:1
bin:x:2

paste:垂直粘贴文件。

-d 指定分隔符 
-s 合并行

wh1516@PinkRAY:~/桌面$ ls | paste -s -d"::\n"
1.txt:c:cachelab-handout
ch3:CH4:ch6emp.tar.gz
cond_exp.c:datalab-handout:lab2
lab3:lab4:linux
linuxch6:OS

//paste把所有行合并为一行,分隔符三个: : \n
//按次序插入分隔符

sort:对文件内容排序。(以行为单位)

-tchar 用char作为分隔符识别字段 
-k m,n 对第m个字段开始到第n个字段结束进行排序 
-k m.n 对第m个字段的第n个字符进行排序 
-u 删除重复行 
-n 数值排序 
-r 反转顺序排序 
-f 不区分大小写 
-c 查看文件是否有序 
-o file 将输出存入文件file中,可以和源文件同名

uniq(unique):定位文件中的重复行和非重复行。

-d 选择并显示输入中的重复行 
-u 选择并显示输入中的非重复行 
-c 显示每行出现的次数

//可与sort配合使用

tr(translate):字符替换将SET1中的字符替换为SET2中的相应字符。【只接受标准输入中的数据,不接受文件作为参数】

tr -option SET1 SET2
-d 删除字符 
-s 压缩连续字符

tr ":" ";" < psdex | head -n 3 //:替换为;
tr "[a-z]" "[A-Z]" < psdex | head -n 3 //小写字母替换为大写
tr -d "/" < psdex | head -n 3 //删除/

cmp(compare):逐字对比两个文件的内容,在遇到差异时终止。

cmp -option FILE1 FILE2 SKIP1 SKIP2
-b 输出第一个差异字节数、行数和字符 
-l 输出所有差异字节数 
-i 忽略文件1起始SKIP1字节和文件2起始SKIP2字节

comm(common):逐行对比文件内容,显示相同行和不同行【需要文件是有序的】

输出结果分三列显示:
    第1列显示FILE1中独有的行
    第2列显示FILE2中独有的行
    第3列显示共同行
常用选项:
    -1,-2,-3: 隐藏显示1,2,3列

diff(difference):显示两个文件的差别,并给出如何使其变得一样。

-q 只在文件不同时显示 
-s 只在文件相同时显示 
-c 显示相同内容 
-u 显示不同内容 
-i 忽略大小写 
-b 忽略重复空白

前 第一个文件 后 第二个文件

示例分析:

ls -l | sort -k 5 -nr | tr -s " " | cut -d" " -f5,9
以从大到小的顺序列出当前目录中的文件。
    ls -l输出列表,用空格分隔的
    给列表排序,sort按照第五个字段给其排序(-k 5),按数值排序-n,从大到小-r(不加则从小到大)
    替换,把多余空格去掉,tr -s " "
    截取 cut -d 只需要文件大小和名字。第五列为大小,第九列为名字
    
tr " \t:" "\n\n\n" <psdex | sort | uniq -c | pr -t -3
统计一个文件中每个单词出现的次数。

正则表达式过滤器

grep命令查找文件中的某一模式、使用正则表达式描述该模式。

基本正则表达式(BRE),扩展正则表达式(ERE)。sed命令。

基本正则表达式:

模式语句匹配含义
[pqr]p,q,r中的单个字符
[a-z]a到z之间的任意单个字符
.任意单个字符
a*0个或任意多个a字符
a\{m,n\}a字符出现m到n次
[^pqr]不是p,q,r的单个字符
^expexp模式位于行首
exp$exp模式位于行尾
 通配符正则
单个字符?.
任意长度**不能单独出现,前面要跟字符,表示该字符出现任意多次
a*以a开头的任意字符串任意长度的a
不是!^
  ^从字符串开头匹配,$从字符串尾部匹配
abc*
    ab+任意多个c 字符串中间出现匹配的即可

^[a-z]\{1,\}[0-9]*
    从字符串开头开始匹配 [a-z]任意字母开头 且该字母 重复大于等于1次 后面是0-9的数字 可以重复任意多次
    即字母开头 数字结尾 中间可以为字母可以为数字

^[^a-zA-Z]
    从头开始匹配 字符不是小写字母或大写字母 
    开头不是字母的都可以匹配

^.*[a-z]*\_[0-9].*$
    ^开头 $结尾——必须整个字符串都满足
    .开头为任意字符
    .* 相当于通配符里的*(任意字符任意多次)
    任意小写字母
    \_ 转义符—— 下划线
    后面跟1位数字
    结尾任意

扩展正则表达式:

模式语句匹配含义
a+1个或任意多个a字符
a?0个或1个a字符(至多出现一次)
exp1|exp2匹配exp1或exp2
(a1|a2)a3

匹配a1a3或a2a3(匹配一个即可)

[0-9]*\.?[0-9]+
    任意长度数字或没有
    \.表示转义 小数点(加了?意为可有可无)
    再加0-9数字
    【判断其是否为合法正数】

(a|b|cd)[0-9]
    a或b或cd 加上0-9的一个数字

grep:在文件中查找指定模式。(find是找文件,grep找文件内容,用到?或者| 加-E)

-G 匹配基本正则表达式(默认) 
-E 匹配扩展正则表达式 
-n 显示匹配行的行号 
-c 显示出现次数 
-e 匹配以连字符开头的表达式 
-x 以整行匹配模式 
-i 忽略大小写 
-v 列出不匹配的行 
-l 只给出匹配模式的文件名

在grep命令中使用正则表达式
grep –i "^draco" /etc/passwd  //passwd文件内以draco开头的一行
grep –E "[0-9]+$" files  //文件名可以用通配符
grep –l "#include<class1.h>" *.c
grep –e "-mtime" files
grep –v "^draco" files
find ./ –name ".c" –exec grep –l "#include<class1.h>" {} \;
    //先用find找.c文件 然后再找是否匹配这一行
    //-exec 在找到的所有文件里运行后面的命令
    //功能同第三行
grep –iE "^(draco|qin).*/bash$" /etc/passwd

sed流编辑器:对数据流进行定位操作。

sed [option] 'address action' Files
    address 定位指定行
    action 执行指定的编辑操作

-e 使用多条指令
-f 从文件中获取指令
-n 使用p指令时禁止默认输出方式

定位方式:
    指定行号:k 第k行
             m,n 从m到n的连续行
    指定模式:/pattern/ 模式匹配的行

sed内部action:

action功能
i、a、c插入、追加和修改文本
d删除行
p在标准输出上打印结果
q读取到指定行后退出
r fname将文件fname的内容放在行后
w fname将指定行写入文件fname
=打印指定行号
s/str1/str2/用str2替换指定行中出现的第一个str1
s/str1/str2/g用str2替换所有的str1
截取文件前五行:
    sed '5q' file
    //相当于head -5
    //5——定位到第五行 q——读取到指定行后退出

替换指定模式:
    sed '/pattern/s/str1/str2/' file
    //第一个/到第二个/之间为要匹配的模式
    sed '/.*:x/s/:x/:x    /' examlist
    /.*:x/表示有:x的就拿出来
    以s开头 表示做替换
    /str1/str2/ 加g就是替换所有 否则替换这一行第一个

打印指定模式的行:
    sed -n '/pattern/p' file

打印文件行数:
    sed -n '$=' file
    //wc -l 统计行数
    //内置变量$ 定位到文件最后一行
    //= 输出这一行的行号

打印文件前三行:
    sed -n '4,$!p' file
    //!p 表示不做
    //第4行到最后一行 ($) 不是想要的行
    //【要1-3行 print】

截取文件后五行:
    sed -n "$((`sed -n '$=' psdcp` -4)),\$p"
    //-n 屏蔽默认输出
    //'倒数第五行,$p' psdcp即可
    //整数运算 $(( )) 得到倒数第五行的行号(但sed中不接受shell命令 为了替换$ 外面加上双引号)
    //获取最后一行行号 $=
    //改成双引号后p前的$也会被替换 p被解释成变量--> \$p \转义符号

将指定行写入文件:
    sed -n '/<head>/,/<\/head>/w output.html' input.html

文本编辑:输入时每行以\结束
    在指定行之前插入行:sed '1i\ ...' file
    在指定行之后插入行:sed '1a\ ...' file
    修改指定行:sed '1c\ ...' file

重定向临时文件 然后将临时文件输入到源文件
输入输出文件不可同名 否则文件变为空白
sed -i 可以输入输出同名
sed '3i\newline' 在第三行插入“newline”

删除注释行:
    sed '/^#/d' file
删除空白行:
    sed '/^[ ]*$/d' file
    //匹配空格后加上*

将文件中的分隔符:替换为|:
    sed 's/:/|/g' examlist
    // 要替换的东西/替换成什么/(如要替换所有)g

删除行中的指定模式:
    sed 's/pattern//g' file

在替换模式中使用&匹配源模式:
    sed 's/Professor/Associate &/g' files
    //Associate & == Associate
    //Professor (指代前面的模式)

查找具有写权限的文件:
    ls -l | sed -n '/^.\{2,8\}w/p'

查找通讯录contact文件中以021开头的11位电话号码:
    sed -n '021[0-9=\{8\}p' contact

将通讯录contact文件中的姓名顺序颠倒:
    sed 's/\([a-zA-Z]*\) *\([a-zA-Z]*\)/\2, \1/' contact
    \1 表示前面的 \2 表示后面的
    \2, \1 表示前后颠倒

awk编程

awk是Perl之前功能最强大的文本处理和报表制作工具。awk合并了几个过滤器的功能,可以独立进行程序设计。awk可以识别和处理一行中的各个字段。awk能够执行数值计算。awk接受扩展的正则表达式进行模式匹配。awk具有类似C语言的程序设计构造。awk可以使用内置变量和函数。

awk基础:

awk将每一行分成多个字段,默认分隔符为空格和制表符,分隔符可以由-F选项自定义。(文件创建时要制定分隔符)

awk用$1,$2等来引用每个字段,类似于shell中的命令行位置参数。($0表示整行)

可以对各个字段进行正则表达式匹配、比较、条件判断、数值计算等。

awk命令格式:
    awk [options] 'selection_criteria {action}' files
selection_criteria和action构成了一个awk程序:
    selection_criteria决定了程序作用的对象
    action决定了程序操作的行为(指令用花括号括起)

awk '$4 ~ /.*sbin.*/ {print }' examlist
    //第四字段包含sbin的 输出

awk -F: '$3 > 2 {print}' examlist
    //-F指定分隔符为:
    //把第三个字段内容大于2的部分拿出来print
    //可做数值比较

action:print和printf:awk使用print和printf向标准输出写入内容,不带action时默认执行print。

        print生产未格式化的输出,printf生成带格式的输出(%s字符串格式,%d整数格式,%f浮点数格式)(printf需要在每行最后加\n输出换行符)

printf格式:printf "含格式符号的字符串", 参数

        格式符号+类型符号

格式符号含义
%格式起始符,必要
-左对齐,省略为右对齐
0表示指定空位填补0
m.nm指输出占用的宽度,n表示数值输出精度,默认n=6
l长整型或双精度数
hshort型
类型符号含义
d十进制整数
o八进制数
x十六进制数
u无符号十进制整数
c单个字符
s

字符串

f浮点数
e指数形式输出实数

管道和重定向:每个print和printf都可以用 > 和 | 进行重定向。每个重定向的文件名和管道命令都要加双引号" " 。

awk '{ printf "%10s %6d\n", $1, $2 | "sort" }' file //输出排序
awk '{ printf "%10s %6d\n", $1, $2 > "outputfile" }' file //输出重定向

数值操作:awk命令支持的数值运算符:+,-,*,/,%(取余),^(乘方)。

read num1 num2; echo $num1 $num2 | awk '{print $1/$2}'
33 5
6.6

用printf命令带格式输出浮点数
echo $num1 $num2 | awk '{printf "%1.5f\n", $1/$2}'
47.14286

赋值运算:+=,++,-=,-- ……
awk -F: '{i++; printf("%3d %6d %8.2f\n", i, $6, $6/12)}' lst //i默认是0

awk中的变量:不需要事先声明,第一次使用时即视为声明,初始值为0或空字符串。变量名区分大小写。变量类型根据上下文解释成字符串或是数字,并自动转换。变量直接引用,不需要加$。

awk中的表达式:由字符串、数字、变量和运算符组合在一起。字符串总是加双引号,可以使用转义符\,八进制字符前加\,十六进制字符前加\x。将字符串作为操作数与数字计算时,字符串会被转换为数字0。

echo " " | awk '{x="sun";y="com";print x"."y}'
sun.com

echo " " | awk '{x="1";y="com";print x+y}'
1

比较与逻辑运算符:用于比较表达式的值,返回真值结果。

运算符含义
<, <=, >, >=

小于,小于等于,大于,大于等于

==, !=等于,不等于
&&, ||, !逻辑与,逻辑或,逻辑非
~ /rexp/匹配正则表达式rexp
!~ /rexp/不匹配正则表达式rexp
i in array数组元素array[i]是否存在
$4 == "sales" //第4个字段是字符串"sales"
NR ==5, NR==8 //第5到第8行连续4行
$6 >= 12000   //第6个字段的数值大于等于12000

匹配正则表达式:awk支持基本正则表达式(不包括IRE和TRE)和扩展正则表达式。

$2 ~ /john/        //第2个字段中包含john
$0 ~ /^john/       //整行以john开头
$3 !~ /^[0-9]+$/   //第三个字段不是数字
/^$/               //空行

将awk程序写成文件:将awk程序部分写在文件中。使用-f选项指定文件名,执行文件中的awk程序。注意:文件中的程序不放在引号内

awk -F: -f script.awk lst

BEGIN和END程序段:可以在awk文件中加入BEGIN和END程序段。BEGIN程序段在awk程序主体之前执行。END程序段在awk程序主体之后执行。

BEGIN {
    printf "\tEmployee List\n\n"
}
{
    total+=$6
    printf "%3d %-20s %-12s %d\n", ++count, $2, $3, $6
}
END {
    printf "\n\tThe average salary is %6d\n", total/count
}

位置参数:当在shell脚本中运行awk命令时,awk程序可以引用shell脚本的命令行参数。引用时为了跟自身的字段引用相区别,对位置参数的引用需要加引号

awk '$6 > '$1' {print}' $2
//命令行参数要加单引号
//第六号大于$1的,输出
//最后一个$2不在awk里面,不用加单引号

数组:数组不需要事先定义,第一次使用即时声明。数组元素默认初始化为0或空字符串。数组自动扩展。数组索引是字符串,array[1]和array["01"]是不一样的。

BEGIN {
    month[1]="Jan"; month[2]="Feb"; month[1000]="illegal month";
    printf("Month 1 is %s and month 5000 is %s\n", month[1], month[1000]);
    printf("Month 500 is %s and month 5000 is %s\n", month[500], month[5000]);
    delete month[1];
    printf("Month 1 is %s and month 2 is %s\n", month[1], month[2]);
}

wh1516@PinkRAY:~/桌面$ awk -f exparray.awk
Month 1 is Jan and month 5000 is illegal month
Month 500 is  and month 5000 is 
Month 1 is  and month 2 is Feb

环境数组:awk的内置数组。用于存储shell环境变量。

awk 'BEGIN {
print "HOME=" ENVIRON["HOME"]
print "PATH=" ENVIRON["PATH"]
 }'
wh1516@PinkRAY:~/桌面$ awk 'BEGIN {
> print "HOME=" ENVIRON["HOME"]
> print "PATH=" ENVIRON["PATH"]
>  }'
HOME=/home/wh1516
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

内置变量:awk内部变量,会自动赋值。用户可根据需要对其重新赋值。

变量名功能默认值备注
NR当前读取行的记录数- 
FS输入字段分隔符空格FS的赋值放在BEGIN中
OFS输出字段分隔符空格print语句中参数的分隔符
OFMT默认浮点数格式%.6f 
RS记录分隔符换行符可将多行合并成一条记录
NF当前行的字段数- 
FILENAME当前输入文件- 
ARGC命令行中的参数个数- 
ARGV包含参数列表的数组- 
ENVIRON包含所有环境变量的数组- 

内置函数:

函数说明
int(x)返回x的整数值
sqrt(x)返回x的平方根
length()返回整个行的长度
length(x)返回x的长度
tolower(s)将字符串s变为小写
toupper(s)将字符串s变为大写
substr(str,m)从str的第m个位置开始截取剩余子串
substr(str,m,n)从str的第m个位置开始截取长度为n的子串
index(s1,s2)返回字符串s2在s1中的位置
split(str,arr,ch)以ch为分隔符,将str划分到数组arr中,返回字段数
system("cmd")运行命令cmd,并返回其退出状态
查找file中长度超过255的行:
    awk 'length() > 255' file

查找1940-1950年之间出生的员工:
    awk -F: 'substr($5,7,2) >= 40 && substr($5,7,2) <=50' lst

将日期格式转换为YYYYMMDD格式:
    awk -F: '{split($5,ar,"/"); print "19"ar[3]ar[1]ar[2]}' lst

在文件开头打印当前日期:
    awk 'BEGIN {system("date")}' file

控制流语句:

if (条件) {语句}
else {语句}

while (条件) {语句}

for (i=1; i<=10; i++) {语句}
for (x in array) {语句}

为每一行薪资计算税款:
awk –F: '{ 
    if ($6 <= 30000) tax=0 
    else { if ($6 > 30000 && $6 <=50000) tax=($6-30000)*0.15 } 
}' lst

将每一行的字段逆向输出:
awk –F: '{ line=$NF 
    for (i=NF-1;i>0;i--) 
        line=line":" $i 
    print line 
}' lst

输出所有环境变量的值:
awk 'BEGIN { 
    for (key in ENVIRON)
        print key "=" ENVIRON[key] 
}'

根据职位分组,统计每个职员的员工数量:
awk -F: '{count[$3]++}
END { for (pos in count)
    printf "%10s %4d\n", pos, count[pos]
}' lst

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值