shell高阶

shell高阶

数组、函数、正则、

1、数组

普通数组:只能使用整数作为数组索引

关联数组:可以使用字符串作为数组索引
在这里插入图片描述

1.1 普通数组
1.1.1 定义数组

一次赋一个值:数组名[下标]=变量值

[root@localhost zhu]# array[0]=hello
[root@localhost zhu]# array[1]=world!

一次赋多个值:数组名=(变量值1 变量值2 ...)

[root@localhost zhu]# array2=(tom jack bob)
##希望是将该文件中的每一个行作为一个元数赋值给数组array3
[root@localhost zhu]# array3=(`cat ip.txt`)
[root@localhost zhu]# array4=(`ls /zhu/ping*`)
[root@localhost zhu]# array5=(hello "bash shell")
[root@localhost zhu]# colors=($red $blue $pink)
[root@localhost zhu]# array6=(1 2 "linux shell" [20]=pup)
1.1.2 查看已定义的普通数组

declera -a:查看所有普通数组

[root@localhost zhu]# declare -a
declare -a array=’([0]=“hello” [1]=“world!”)’
declare -a array2=’([0]=“tom” [1]=“jack” [2]=“bob”)’

1.1.3 访问数组元素

访问数组元素

[root@localhost zhu]# echo ${array[0]}  #访问数组中第一个元素
hello
[root@localhost zhu]# echo ${array[@]}  #访问数组所有元素
hello world!
[root@localhost zhu]# echo ${array[*]}  #访问数组所有元素
hello world!
[root@localhost zhu]# echo ${#array[@]} #访问数组所有元素的个数
2
[root@localhost zhu]# echo ${!array[@]} #获取元素的索引
0 1

使用类似切片查看数组元数

[root@loca zhu]# echo ${array6[@]:2}    #从数组下标2开始
3 4 5 6 linux shell pupp
[root@loca zhu]# echo ${array6[@]:2:3}  #从数组下标2开始取3个
3 4 5
1.1.4 遍历数组
  • 方法1:通过元素的个数进行遍历
  • 方法2:通过数组元数的索引进行遍历
1.1.5 实现普通数组遍历输出hosts文件内容

while

[root@localhost zhu]# vim array_while_hosts.sh
#! /usr/bin/bash
while read line
do
        hosts[++i]=$line
done </etc/hosts

for i in ${!hosts[@]}
do
        echo "$i: ${hosts[$i]}"
done

for

[root@localhost zhu]# vim array_for_hosts.sh 

#! /usr/bin/bash
OLD_IFS=$IFS
IFS=$'\n'

for line in `cat /etc/hosts`
do
        hosts[++i]=$line
done

for i in ${!hosts[*]}
do
        echo "$i: ${hosts[$i]}"
done
IFS=$OLD_IFS
1.2 关联数组

关联数组,需要先通过declera -A去声名,才可以使用

1.2.1 定义关联数组

申明关联数组变量

[root@localhost zhu]# declare -A ass_array1
[root@localhost zhu]# declare -A ass_array2

一次赋一个值:数组名[索引]=变量值

[root@localhost zhu]# ass_array1[name]=zhujb
[root@localhost zhu]# ass_array1[age]=25

一次赋多个值:数组名=([索引1]变量值1 [索引2]变量值2 ...)

[root@localhost zhu]# ass_array2=([color]=red [sex]=man [address]="china beijing")
1.2.2 查看已定义的关联数组

declera -A:查看所有关联数组

[root@localhost zhu]# declare -A
declare -A ass_array1=’()’
declare -A ass_array2=’()’

1.2.3 访问数组元素
[root@localhost zhu]# echo ${ass_array2[@]}     #访问数组所有元素
red man
[root@localhost zhu]# echo ${ass_array2[color]} #访问数组中指定索引的元素
red
[root@localhost zhu]# echo ${#ass_array2[@]}    #访问数组所有元素的个数
2
[root@localhost zhu]# echo ${!ass_array2[@]}    #获取元素的索引
color sex
1.2.4 遍历数组

通过数组元数的索引进行遍历

1.2.5 简单脚本

测试远程连接当前主机的各种状态的连接数

watch -n2 ./count_tcpconn_status. sh:表示实时执行该脚本,并清屏

[root@localhost zhu]# vim count_tcpconn_status.sh 
#! /usr/bin/bash
declare -A status

type=`ss -an | grep :80 | awk '{print $2}'`
for i in ${!type[@]}
do
let status[$i]++
done

for j in ${!status[@]}
do
echo "$j: ${status[$j]}"
done

计算文件sex.txt中男女数量各多少

let sex[$type]++:这里只对$type这个索引的value加1

[root@localhost zhu]# vim sex.txt
zhangsan   m    
lisi       f
wangwu     f
zhaoliu    m
[root@localhost zhu]# vim count_sex.sh

#! /usr/bin/bash
declare -A sex

while read line
do
        type=`echo $line | awk '{print $2}'`
        let sex[$type]++
done < sex.txt

for i in ${!sex[@]}
do
        echo "$i: ${sex[$i]}"
done

[root@localhost zhu]# ./count_sex.sh 
f: 2
m: 2

计算系统内**/etc/passwd**不同内置的解释器的个数

[root@localhost zhu]# vim count_shell.sh
#!/usr/bin/bash
declare -A shells

while read line
do
        type=`echo "$line" | awk -F":" '{print $NF}'`
        let shells[$type]++
done < /etc/passwd

for i in ${!shells[@]}
do
        echo "$i: ${shells[$i]}"
done

[root@localhost zhu]# ./count_shell.sh 
/sbin/nologin: 16
/bin/sync: 1
/bin/false: 1
/bin/bash: 26
/sbin/shutdown: 1
/sbin/halt: 1

2、函数

函数完成特定功能的代码片段(块),在shell中定义函数可以使用代码模块化,便于复用代码,函数必须先定义才可以使用

2.1 定义函数
2.1.1 方法一
函数名 (){
	函数要实现的功能代码块
}
2.1.2 方法二
function 函数名{
	函数要实现的功能代码块
}
2.2 简单函数阶乘
[root@localhost zhu]# vim factorial.sh

#! /usr/bin/bash
factorial(){
factorial=1
for i in `seq $1`
do
        #factorial=$[ $factorial * $i ]
        #let factorial=$factorial*$i
        let factorial*=$i

done
echo "$1的阶乘是:$factorial"
}

factorial $1

[root@localhost zhu]# ./factorial.sh 9
9的阶乘是:362880

factorial $1:这里表示外部执行脚本传入的位置参数

for i in seq $1:这里表示调用函数后面跟随的位置参数

2.3 return函数值
2.3.1 错误的return

这里的return就类似exit num,相当于程序执行的返回码,shell默认最大返回码是255,所以超过计算结果超过255,返回的就不是想要的值了

[root@localhost zhu]# vim return.sh

#! /usr/bin/bash
function fun2{
read -p "enter num:" num
return $[2*$num]
}

fun2
echo "fun2 return value: $?"

执行发现最后超过255的有问题

[root@localhost zhu]# ./return.sh 
enter num:8
fun2 return value: 16
[root@localhost zhu]# ./return.sh 
enter num:100
fun2 return value: 200
[root@localhost zhu]# ./return.sh 
enter num:200
fun2 return value: 144
2.3.2 正确的return

将函数中的值赋值输出给另一个变量,然后返回

[root@localhost zhu]# vim return_out.sh 

#! /usr/bin/bash
function fun2(){
read -p "enter num:" num
echo $[2*$num]
}

result=`fun2`
echo "fun2 return value: $result"

[root@localhost zhu]# ./return_out.sh 
enter num:99
fun2 return value: 198
[root@localhost zhu]# ./return_out.sh 
enter num:800
fun2 return value: 1600
2.4 传参数组,返回数组
2.4.1 函数传参数组
[root@localhost zhu]# vim func_receive_array.sh
#! /usr/bin/bash
num=(1 2 3)
num2=(3 6 9)

array() {
        local factorial=1
        for i in $*
        do
                let factorial*=$i
        done
        echo "$factorial"
}

array ${num[@]}  #数组所有的元素值
array ${num2[*]}  #数组所有的元素值

array ${num[@]}:这里函数后面跟的是一个数组参数

$* | $@:函数接收数组变量,函数里的$*和程序预定义是一样的,取得是函数后跟的所有的参数

local:代表局部变量,只能在所在的函数里使用

注意:

这里for i in $*可以直接写成for i,效果是一样的,默认取到函数后所有的参数

2.4.2 函数返回数组
[root@localhost zhu]# vim func_return_array.sh
#! /usr/bin/bash
num=(2 4 6)
num2=(3 6 9)

array() {
        local newarray=($*)
        local i
        for ((i=0;i<$#;i++))
        do
                newarray[i]=$[ ${newarray[i]} * 5  ]
        done
        echo "${newarray[*]}"
}

result=`array ${num[*]}`
echo "${result[*]}"

res=`array ${num2[*]}`
echo "${res[*]}"

$#:函数后面参数的个数,和程序预定义是一样的

$1 $2 $3 ... $n:函数接收位置参数

newarray=($*):函数将接收到的所有参数赋值给数组

2.5 分析local

不在同一个子shell,所以$a在脚本的全局变量中是未定义的,输出为空

#! /usr/bin/bash
num=(2 4 6)
array() {
        local a=555
        local j
        for i
        do
                outarray[i++]=$[ $i * 5  ]
        done
        echo "${outarray[*]}"
}

result=`array ${num[*]}`
echo $a
echo "${result[*]}"

[root@localhost zhu]# ./func_return_array2.sh 

10 20 30

在同一个子shell,就可以看到局部变量的结果

3、正则

3.1 什么是正则

正则表达式( regular expression, RE )是一种字符模式,用于在查找过程中匹配指定的字符。

在大多数程序里,正则表达式都被置于两个正斜杠之间。例如/L[oO]ve/就是由正斜杠界定的正则表达式

它将匹配被查找的行中任何位置出现的相同模式。在正则表达式中,元字符是最重要的概念。

3.1.1 简单使用

匹配数字:^[0-9]+$

匹配Mail:[a-z0-9_]+@[a-z0-9]+\.[a-z]+ 例如:yangsheng131420@126.com

匹配IP:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}

[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}

3.1.2 匹配配置文件中的ip
[root@localhost ~]# grep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/sysconfig/network-scripts/ifcfg-enp0s3 
[root@localhost ~]# egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/sysconfig/network-scripts/ifcfg-enp0s3 
IPADDR=192.168.56.200
NETMASK=255.255.255.0
GATEWAY=192.168.56.1
DNS1=114.114.114.114

请到grep里查看具体的区别

3.2 元字符
3.2.1 定义

定义:元字符是这样一类字符,它们表达的是不同于字面本身的含义

shell元字符(也称为通配符):由shell来解析 ,如rm -f \*.pdf,元字符*Shell将其解析为任意多个字符

正则表达式元字符:由各种执行模式匹配操作的程序来解析,比如vim、sed、awk、grep、python

3.2.2 正则表达式元字符
1、基本正则表达式元字符
元字符功能示例
^行首定位符^love
$行尾定位符love$
.匹配单个字符l.e
*匹配前导符0到多次ab*love
.*任意多个字符
[]匹配指定范围内的一个字符[L]ove
[ - ]匹配指定范围内的一个字符[a-z0-9]ove
[^]匹配不在指定组内的字符[^a-z0-9]ove
\用来转义元字符love\.
\<词首定位符\<love
\>词尾定位符love\>
\(…\)匹配稍后使用的字符的标签:% s/172.16.130.1/172.16.130.5/
:% s/\(172.16.130.\)1/\15/
x\{m\}字符x重复出现m次o\{5\}
x\{m,\}字符x重复出现m次以上o\{5,\}
x\{m,n\}字符x重复出现m到n次o\{5,10\}
2、扩展正则表达式元字符
元字符功能示例
+匹配一个或多个前导字符[a-z]+ove
匹配一个或零个前导字符lo?ve
a|b匹配a或blove|hate
()组字符love(able|ers)
(…)(…)\1\2标签匹配字符,将第一个(..)赋值给\1,后面同理(love)able\1er
x{m}字符x重复m次o{5}
x{m,}字符x重复至少m次o{5,}
x{m,n}字符x重复m到n次o{5,10}

在这里插入图片描述

3.3 POSIX字符类
表达式功能示例
[:alnum:]字母与数字字符[[:alnum:]]+
[:alpha:]字母字符(包括大小写字母)[[:alpha:]]{4}
[:blank:]空格与制表符[[:blank:]]*
[:digit:]数字字母[[:digit:]]?
[:lower:]小写字母[[:lower:]]{5,}
[:upper:]大写字母[[:upper:]]+
[:punct:]标点符号[[:punct:]]
[:space:]包括换行符,回车等在内的所有空白[[:spac:]]+
3.4 正则匹配示例:vim中

:1,$ s/tom/David/g

[root@localhost zhu]# vim tom.txt
tom、anatomy、tomatoes、tomorrow
~
#底行模式
:1,$ s/tom/David/g

[root@localhost zhu]# cat tom.txt
David、anaDavidy、Davidatoes、Davidorrow

:1,$ s/\<[Tt]om\>/David/g

[root@localhost zhu]# vim tom.txt
tom、anatomy、tomatoes、tomorrow
~
#底行模式
:1,$ s/\<[Tt]om\>/David/g

[root@localhost zhu]# cat tom.txt
David、anatomy、tomatoes、tomorrow

:1,$:表示从1行到随后一行

s:全文检索

/tom/:要替换的原值

/David/:要替换的新值

g:全局

\<[Ttom]\>:精确匹配Tom | tom 的值

grep、Awk、sed

1、grep

1.1 grep家族

grep:在文件中全局查找指定的正则表达式,并打印所有包含该表达式的行

egrep扩展的egrep,支持更多的正则表达式元字符

fgrep固定grep(fixed grep),有时也被称作快速(fast grep),它按字面解释所有的字符

1.2 grep命令格式
1.2.1 格式

grep [选项] PATTERN filename1 filename2 ...

grep 'Tom' /etc/passwd
grep "bash shell' /etc/test
1.2.2 grep返回结果

**找到:**grep返回的退出状态为0

**没找到:**grep返回的退出状态为1

找不到指定文件:grep返回的退出状态为2

1.2.3 grep程序的输入

grep程序的输入可以来自标准输入管道,而不仅仅是文件,例如:

# grep 'tom'
# ps aux | grep 'sshd'
# ll | grep 'nd'
# grep 'alice' /etc/passwd /etc/shadow /etc/group
1.3 grep使用的元字符

grep:使用基本元字符集 ^,$,.,*,[],[^],\< \>,\(\),\{\},\+,\|

egrep(或grep -E):使用扩展元字符集?, +,{},|,()

注:grep也可以使用扩展集中的元字符,仅需要对这些元字符前置一个反斜线

1.3.1 grep专有的元字符

\w:所有字母与数字,称为字符[a-zA-Z0-9]

’L[a-zA-Z0-9]ve‘ ==== ‘L\w*ve’

\W:所有字母与数字之外的字符,称为非字符

‘Love[^a-zA-Z0-9]+'  ==== 'love\W+'

\b:词边界

‘/<love/>====   '\blove\b'
1.3.2 grep示例

grep -Eegrep

egrep 'NW' datafile  		#查找包含内容为’NW’
egrep 'NW' d*				#查找文件名以d开头的包含内容为’NW’
egrep '^n' datafile			#查找n开头的
egrep '4$' datafile			#查找4结尾的
egrep TB Savage datafile	#在Savage和datafile两个中查找TB
egrep 'TB Savage' datafile	#在datafile中查找TB Savage
egrep '5\..' datafile		#查找5.任意字符
egrep '\.5' dataile			#查找.5
egrep '^[we]' datafle		#查找w|e开头的
egrep '[^0-9]' datafile		#查找数字开头的
egrep '[A-Z][A-Z]' datafile	#查找2个任意的大写英文字符的
egrep 's* ' datafile		#查找内容为0-n个’s’加空格
egrep '[a-z]{9}' datafile	#查找连续9个小写字母
egrep '\<north' datafile	#查找以north为词首的行
egrep '\<north\>' datafle	#查找以north为词首,以及为词尾的行
egrep '\<[a-r].*n\>' datafile	#查找a-r中任意一个字符,后面0-n个任意字符,以n为词尾的行
egrep '^n\w*\W' datafile	#查找以n开头的任意多个字符,并且以非字符结尾的
egrep '\bnorth\b' datafile	#查找以north为词首,以及为词尾的行
egrep 'NW|EA' datafile		#查找NW或EA
egrep '3+' datafile			#查找1-n个3
egrep '2\.?[0-9]' datafile	#查找2开头的0-1个.,后面再跟一个任意数字
egrep '(no)+' datafile		#查找1-n个连续的no
egrep 'S(h|u)' datafile		#查找Sh或Su
egrep 'Sh|u' datafile		#查找Sh或u
1.4 grep选项
1.4.1 选项 + 示例

-i | -ignore-case:忽略大小写

-l | -files-with-matches:只列出匹配行所在的文件名

[root@localhost zhu]# egrep -l 'root' /etc/passwd /etc/hosts
/etc/passwd

-n | -line-number:在每一行前面加上它在文件中的相对行号

[root@localhost zhu]# egrep -n 'root' /etc/passwd /etc/shadow /etc/hosts
/etc/passwd:1:root:x:0:0:root:/root:/bin/bash
/etc/passwd:10:operator:x:11:0:operator:/root:/sbin/nologin

-c | --count:显示成功匹配的行数

[root@localhost zhu]# egrep -c 'root' /etc/passwd /etc/hosts
/etc/passwd:2
/etc/hosts:0

-s | --no-messages:禁止显示文件不存在或文件不可读的错误信息

-q | --quiet | --silent:静默(不输出查到的结果)–quiet, --silent

[root@localhost zhu]# egrep -q 'root' /etc/passwd /etc/hosts;echo $?
0

-v | --invert-match:反向查找,只显示不匹配的行

[root@localhost zhu]# egrep -v 'root' /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

-R | -r | --recursive:递归针对目录

--color:颜色

-o | --only-matching:只显示匹配的内容

[root@localhost zhu]# egrep -o 'root' /etc/passwd /etc/hosts
/etc/passwd:root
/etc/passwd:root
/etc/passwd:root
/etc/passwd:root

-B | --before-context=NUM print NUM:(before)显示查找到的当前行的前num行的内容

-A | --after-context=NUM:(after)显示查找到的当前行的后num行的内容

-C | --context=NUM:(context)显示查找到的当前行的上下分别num行的内容

2、sed(流编辑器)

2.1 sed工作流程

在这里插入图片描述

sed是一种在线的、非交互式的编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区
中,称为"模式空间”( pattern space ),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。

Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。

sed -ri. bak

sed -ric --follow-symlinks

2.1.1 暂存区和模式空间数据图

在这里插入图片描述

2.2 命令格式
2.2.1 命令格式

sed [options] 'command' file(s):表示针对这个file文件做指定的命令操作

sed [options] -f scriptile file(s):这里的scriptile是一个文件,里面包含了多个指定集,表示对这个file文件做多个的命令操作

2.2.2 注意

sed和grep不一样,不管是否找到指定的模式,它的退出状态都是0,只有当命令存在语法错误时,sed的退出状态才是非0

2.3 支持正则表达式

与grep一样 ,sed在文件中查找模式时也可以使用正则表达式(RE)和各种元字符。正则表达式是括在斜杠间的模式,用于查找和替换,以下是sed支持的元字符。

sed -r:指定正则模式匹配

sed -i:交互式操作文件,也就是加了-i选项之后,就真的对文件进行操作

使用扩展元字符的方式:\+

使用正则方式和 grep 不一样,需要在元字符前加\,例如:\+

使用基本元字符集:^,$,.,*,[],[^],\< \>,\(\),\{\},\+,\|

使用扩展元字符集:?, +,{},|,()

2.4 sed基本用法
2.4.1 创建一个passwd.txt做测试
[root@localhost zhu]# head /etc/passwd > passwd.txt
[root@localhost zhu]# cat passwd.txt 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
2.4.2 进行简单操作
sed -r '' passwd.txt	#不对文件进行操作,单纯的打印读到的行
sed -r 'p' passwd.txt 	#-p:打印读到的行,默认读取也会输出,就会输出两行
sed -r 'd' passwd.txt	#删除所读到的所有行
sed -r '/root/d' passwd.txt   #删除包含root的行
sed -r '\#root#d' passwd.txt   #删除包含root的行
sed -r '\crootcd' passwd.txt   #删除包含root的行,这里c可以替换任意字符
sed -r -n '' passwd.txt #-n:不输出读到的行
sed -r 's/root/alias/' passwd.txt   #将读取到第一个root,替换成alias
sed -r 's/root/alias/g' passwd.txt 	#将读取到全局的root,替换成alias,g表示全局
sed -r 's/root/alias/gi' passwd.txt	#将读取到全局的root,替换成alias,-i不区分大小写

2.4.3 简单删除替换的操作
[root@localhost zhu]# vim a.txt
/etc/abc/123
etc

#删除指定行  下面两种结果一样
[root@localhost zhu]# sed -r  '/\/etc\/abc\/123/d' a.txt 
etc
[root@localhost zhu]# sed -r  '\#/etc\/abc\/123#d' a.txt 
etc

#替换指定行
[root@localhost zhu]# sed -r 's#/etc/abc/123#lll#' a.txt 
lll
etc
2.5 sed扩展: 地址(定址)

地址用于决定对哪些行进行编辑。地址形式可以是数字正则表达式二者的结合。如果没有指定地址,sed将处理输入文件中的所有行。

sed -r 'd' /etc/passwd				#删除所有行
sed -r '3d' /etc/passwd				#删除第3行
sed -r '1,3d' /etc/passwd			#删除1-3行
sed -r '/root/,5d' /etc/passwd		#删除第一个root所在的行以及后面5行,包括后面所有含有root的行
sed -r '/^adm/,20d' /etc/passwd 	#删除以adm开头的行一直到第20行
sed -r '/^adm/,+20d' /etc/passwd 	#从删除以adm开头的行之后,再删除20行
sed -r '/root/d' /etc/passwd		#删除包含root的行
sed -r '/root/!d' /etc/passwd		#删除不包含root的所有行
sed -r '1~2d' /etc/passwd			#删除所有奇数行   odd-numbered
sed -r '0~2d' /etc/passwd			#删除所有偶数行   even-numbered
2.6 sed命令

sed命令告诉sed对指定行进行何种操作,包括打印、 删除、修改等

命令功能
a在当前行后添加一行或多行
c用新文本修改(替换)当前行中的文本
d删除行
i在当前行之前插入文本.
l列出非打印字符
p打印行
n读入下一输入行,并从下一条命令而不是第一条命令开始对其的处理
q结東或退出sed
!对所选行以外的所有行应用命令
s用一个字符串替换另一个,g在行内进行全局替换,i忽略大小写
r从文件中读
w将行写入文件
y将字符转换为另-字符(不支持正则表达式)
h把模式空间里的内容复制到暂存缓冲区(覆盖)
H把模式空间里的内容追加到暂存缓冲区
g取出暂存缓冲区的内容,将其复制到模式空间,覆盖该处原有内容
H取出暂存缓冲区的内容,将其复制到模式空间,追加在原有内容后面
x交换暂存缓冲区与模式空间的内容
2.7 sed选项
选项功能
-e允许多项编辑
-f指定sed脚本文件名
-n取消默认的输出
-iinplace,就地编辑
-r支持扩展元字符
2.8 sed选项命令示例
1 打印命令: p
sed -r '/root/p' passwd.txt
sed -r -n '/root/p' passwd.txt 
2 刪除命令: d
sed -r '3d' passwd.txt		#删除第3行
sed -r '3{d}' passwd.txt 	#删除第3行
sed -r '3{d;}' passwd.txt	#删除第3行
sed -r '3,$d' passwd.txt	#删除3-$d行,$d代表最后一行
sed -r '$d' passwd.txt		#删除最后一行
sed -r '/root/d' passwd.txt #删除包含root的所有行
3 替换命令: s
sed -r 's/root/alice/' /etc/passwd	#将alice替换所有的root
sed -r 's/^root/abc/' passwd.txt	#将alice替换所有的以root开头的行
sed -r 's/[0-9][0-9]$/&.5/' passwd.txt	#将所有的一两个数字结尾的行后面加上.5
sed -r 's/(roo)t/\1pll/' passwd.txt	#这里将roo用()圈起来,后面替换的时候直接使用\1就可以取到roo的值
sed -r 's#root#hello#' passwd.txt	#使用`#`代替了`/`,效果一样的
4 读文件命令: r
sed -r '/lp/r /etc/hosts' passwd.txt	#读到passwd.txt文件的包含lp行的后面,读取/etc/hosts的内容
sed -r '2r /etc/hosts' passwd.txt		#读到passwd.txt文件的第2行的后面,读取/etc/hosts的内容
sed -r '/2/r /etc/hosts' passwd.txt		#读到passwd.txt文件的包含2的行的后面,读取/etc/hosts的内容
[root@localhost zhu]# sed -r '/lp/r /etc/hosts' passwd.txt 
root:x:0:0:root:/root:/bin/bash
root:x:1:1:bin:/bin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
sync:x:5:0:sync:/sbin:/bin/sync
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
5 写文件命令:w
sed -r '/root/w /zhu/1111.txt' passwd.txt	#将passwd.txt包含root的行,写入到/zhu/1111.txt
sed -r '3,$w /zhu/1111.txt' passwd.txt		#将passwd.txt第三行开始的内容,写入到/zhu/1111.txt
6 追加命令:a
#单行追加
sed -r '2a123123' passwd.txt	#在passwd.txt第2行后追加内容123123
#换行多行追加
sed -r '2a\123\					
> 456\
> 789' passwd.txt
root:x:0:0:root:/root:/bin/bash
root:x:1:1:bin:/bin:/sbin/nologin
123
456
789
7 插入命令:i
sed -r '2i111' passwd.txt		#在passwd.txt第2行前插入内容111
sed -r '2i\123123' passwd.txt 	#换行多行插入
8 修改命令:c
sed -r '2c123123' passwd.txt	#将passwd.txt第2行替换为123123
sed -r '2c123\					#多行修改
> 111\
> 222' passwd.txt
root:x:0:0:root:/root:/bin/bash
123
111
222
9 模式空间、暂存空间:
9.1、h H g G
sed -r '2h;$G' passwd.txt		#将第2行的数据覆盖到暂存空间,在追加到文件最后一行
sed -r '1{h;d};$G' passwd.txt 	#将第1行的数据覆盖到暂存空间,然后删除文件的第1行,在从暂存空间取出,追加到文件最后一行
sed -r '1h;2,$g' passwd.txt		#将第1行的数据覆盖到暂存空间,然后从暂存空间取出,覆盖文件passwd 2-n行
sed -r '1h;2,5H;$G' passwd.txt	#将第1行的数据覆盖到暂存空间,将2-5行的数据追加到暂存空间,在从暂存空间取出,追加到文件最后一行
9.2、x
sed -r '3h;4x;5G' passwd.txt	#将第3行的数据覆盖到暂存空间,然后将暂存空间的和模式空间的第四行交换空间,最后吧暂存空间的追加到passwd的第5行的后面
10 多重编辑:e
sed -r -e '1,3d' -e 's/login/LOL/' passwd.txt	#第一个-e,表示先删除1-3行,第2个-e表示替换

#可以不使用-e ,使用; 隔开操作就好
sed -r '1,3d;s/login/LOL/' passwd.txt 			#和上一行一样
sed -r '2s/root/cib/;2s/sbin/LOL/' passwd.txt	#查找第2行的所有root替换为cib,所有的sbin替换为LOL
sed -r '2{s/root/cib/g;s/bin/LOL/g}' passwd.txt #和上一行一样
2.9 sed常见操作
2.9.1 删除,替换,添加注释
&表示匹配前面查找的内容sed -r '2,5s/^/#/' passwd.txt#删除
sed -r '/^#/d' passwd.txt		#删除文件中以#开头的注释行
sed -r '/[ \t]*/d' passwd.txt	#删除文件中有多个空格或tab以#开头的注释行
sed -r '\Y^[ \t]*//Yd' passwd.txt	#删除文件中有多个空格或tab以//开头的注释行
sed -r '/^[ \t]*$/d' passwd.txt	#删除文件中无内容的空行
sed -r '/^[ \t]*#/d;/^[ \t]*$/d' passwd.txt		#删除注释行和空行
sed -r '/^[ \t]*#|^[ \t]*$/d' passwd.txt		#删除注释行和空行
sed -r '/^[ \t]*($|#)/d' passwd.txt				#删除注释行和空行

#替换
sed -r '$a\root' passwd.txt			#在文件最后一行追加root
sed -ri '/^root/cROOT/' passwd.txt	#将所有文件的以root开头的行替换成ROOT
sed -r '2,7s/^[ \t#]*/#/' passwd.txt#将空格tab和空行替换为#

#添加注释
sed -r '2,5s/^/#/' passwd.txt		#给文件2-5行添加注释
sed -r '2,6s/(.*)/#\1/' passwd.txt	#和上一行一样
sed -r '2,6s/(.*)/#&/' passwd.txt	#和上一行一样
sed -r '2,8s/^#*/#/' passwd.txt		#将行首零个或多个#换成一个#
sed -r '2,8s/^[ \t]*#*/#/' passwd.txt	#将行首零个或多个#换成一个#

\1:表示取第一个组字符的内容

&:表示匹配前面查找的内容

2.9.2 sed中使用外部变量
var1=123
sed -r '3a$var1' passwd.txt     #错误用法
sed -r "3a$var1" passwd.txt		#正确使用
sed -r 3a$var1 passwd.txt		#正确使用

sed -r "$a$var1" passwd.txt		#错误用法
sed -r '$a'"$var1" passwd.txt	#正确使用,添加到最后一行
sed -r "\$a$var1" passwd.txt 	#正确使用
2.9.3 小练习

研究过,没看懂

[root@localhost zhu]# vim num.txt
1
2
3
4
5
[root@localhost zhu]# sed -r '1!G;$!h;$!d' num.txt 
5
4
3
2
1

3、awk

3.1 awk简介

awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。它支持用户自定义函数动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命合行中使用,但更多是作为脚本来使用。

awk的处理文本和数据的方式是这样的,它遵行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho. Brian Kernighan、 Peter Weinberger。gawk是 awk的GNU版本,它提供了Bell实验室和GNU的一些扩展。

3.2 awk格式
3.2.1 awk语法格式

awk [options] 'commands' filenames:表示针对这个文件做指定的命令操作

awk [options] -f awk-script-file filenames:这里的awk-script-file是一个文件,里面包含了多个指定集,表示对这个file文件做多个的命令操作

options:

-F:定义输入字段分隔符,默认的分隔符是空格或制表符(tab)

command:

BEGIN{}行处理前,通常用于定义一些变量,例如BEGIN{FS=";OF-=–"}

{}行处理中

END{}行处理后

实例:awk默认读取文件的每行进行操作

[root@localhost zhu]# awk 'BEGIN{print 1/2} {print "ok"} END{print "-----"}' passwd.txt 
0.5
ok
ok
ok
-----
3.2.1 awk命令格式
awk 'pattern' filename			
# awk '/root/' passwd.txt,			打印包含root的行
awk '{action}' filename			
# awk -F: '{print $1}' passwd.txt 	 打印所有行的第一个参数
awk 'pattern {action}' filename	
# awk -F: '/bin/{print $1,$3}' passwd.txt 		查找所有包含bin的行,使用:分割,分别打印1,3列
command | awk 'pattern {action}'
# df -P | grep '/' | awk '$4>50000 {print $4}'	查看磁盘信息包含/的行,使用:分割,过滤第四列的值判断
3.3 原理、内部变量、格式化输出
3.3.1 解析原理
awk -F: '{print $1,$3}' passwd.txt

(1)、awk使用一行作为输入,并将这行赋给内部变量$0,每一行也可称为一个记录,以换行符结東

(2)、然后,行被:(默认为空格或制表符)分解成字段(或域) , 每个字段存储在已编号的变量中,从$1开始,最多达100个字段

(3)、awk如何知道用空格来分隔字段的呢?因为有一个内部变量FS来确定字段分隔符。初始时, FS赋为空格

(4)、awk打印字段时,将以设置的方法使用print函数打印, awk在打印的字段间加上空格,因为$1,$3之间
有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格

(5)、awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕

3.3.2 记录与字段相关内部变量

$0:awk变量,$0保存当前记录的内容

awk -F: '{print $0}' /etc/passwd

NR:所有参数文件所有行数的数字

awk -F: '{print NR, $0}' /etc/passwd /etc/hosts

FNR:每个参数文件所有行数的数字

awk F: '{print FNR, $0}' /etc/passwd /etc/hosts

NF:保存 记录的字段数,$1…$100

awk -F: '{print $0,NF}' passwd.txt

FS输入字段分隔符,默认空格,可以自定义分隔符

awk -F: '/alice/{print $1,$3}' /etc/passwd
awk -F'[ :\t]' '{print $1,$2,$3}' /etc/passwd
awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd

OFS输出字段分隔符

awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2,$3,$4}' passwd

RS输入行记录分隔符,默认是换行符,可以自定义分隔符

awk F: 'BEGIN{RS=" "} {print $0}' a.txt

ORS输出行记录分隔符

awk F: 'BEGIN{ORS=" "} {print $0}' passwd

FS、OFS、RS、ORS区别

字段分隔符:FS、OFS默认空格或制表符

记录分隔符:RS、ORS默认换行符

3.3.3 格式化输出

print函数

date | awk '{print "mouth: "$2"\nyear: "$1}'	#查看日期,并自定义格式输出,\n在{}表示换行
awk -F":" '{print "username is: "$1"\t uid is: "$3}' /etc/passwd	#\n在{}表示制表符
awk -F":" '{print "\tusername and uid is: "$1,$3}' /etc/passwd

printf函数,自带格式化光环

awk -F":" '{printf"%-15s %-10s %-15s\n",$1,$2,$3}' /etc/passwd
awk -F":" '{printf"|%-15s| %-10s| %-15s\n",$1,$2,$3}' /etc/passwd

%s:字符类型

%d:数值类型

%f:浮点类型

-表示左对齐默认是右对齐,数字15表示占15字符

printf默认不会在行尾自动换行,加\n

3.4 awk模式和动作
3.4.1 正则表达式
匹配记录整行:
awk '/^root/' /etc/passwd
awk '$0 ~ /^root/' /etc/passwd
awk '!/root/' /etc/passwd
awk '$0 !~ /^root/' /etc/passwd
匹配字段:匹配操作符(、!
awk -F":" '$1 ~ /^root/' /etc/passwd
awk -F":" '$1 !~ /^root/' /etc/passwd
3.4.2 比较表达式

比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。比较表达式使用关系运算符,用于比较数字字符串
在这里插入图片描述

awk -F: '$3 == 0' /etc/passwd
awk -F: '$3 < 10' /etc/passwd
awk -F: '$7 == "/bin/bash"' /etc/passwd
awk -F: '$1 == "alice"' /etc/passwd
awk -F: '$1 ~ /alice/ ' /etc/ passwd
awk -F: '$1 !~ /alice/ ' /etc/passwd
df -P | grep '/' | awk '$4 > 25000'
3.4.3 条件表达式
awk -F":" '$3>1000 {print $0}' /etc/passwd			#按照:拆分,查出pid大于1000的行信息
awk -F":" '{ if($3>1000) {print $0}}' /etc/passwd	#同上
awk -F":" '{ if($3>1000) {print $0}}' /etc/passwd	#同上,如果不大于1000,则输出第一个字段
3.4.4 算术运算

+-/% (模)、^(幂2^3)

可以在模式中执行计算, awk都将按浮点数方式执行算术运算

awk -F":" '$3*100>300' /etc/passwd		#计算条件为真,则执行
awk -F":" '{ if($3*100<500) {print $0} }' /etc/passwd
3.4.5 逻辑操作符和复合模式

&&:逻辑与

||:逻辑或

!:逻辑非

awk -F":" '{ if($1~/root/ && $3<=15 ) {print $0}}' /etc/passwd  #查出开头是root,并且pid不大于15
awk -F":" '{ if($1~/root/ || $3<=15){print $0} }' /etc/passwd	#查出开头是root,或者pid不大于15
awk -F":" '!($1~/root/||$3<=15)' /etc/passwd					#查出开头不是root,或者pid大于15
3.4.6 范围模式
awk '/Tom/,/Suza/' filename #表示匹配从Tom到Suza的范围
3.5 awk脚本编程
1、if

if:{if(表达式) {语句;语句…}

awk -F":" '{ if($3==0) {print $1 "is administrator"}}' /etc/passwd
awk -F":" '{if($3>0 && $3<1000) {count++;}} END{print count}' /etc/passwd	#统计系统用户数

if...else:{if(表达式) {语句;语句.;… } else{语句;语句;…}

awk -F":" '{if($3==0){count++} else{i++}} END{print "管理员个数:"count;print "系统用户个数:"i}' /etc/passwd

if..else if..else:{if(表达式1) {语句;语句; …} else if(表达式2) {语句;语句;… } else{语句;语句;…}

2、while

while(条件表达式)

awk -F":" '/^root/{i=1;while(i<=7){print $i;i++}}' /etc/passwd		#逐行打出root信息
awk -F":" '{i=1;while(i<=NF){print $i;i++}}' /etc/hosts
[root@localhost zhu]# vim b.txt
111 222
333 444 555
666 777 888 999
[root@localhost zhu]# awk '{i=1;while(i<=NF){print $i;i++}}' b.tx  
3、for

for()

awk '{for(i=1;i<=10;i++){print $0;i++}}' /etc/passwd	#将每行打印10次
awk -F":" '{for(i=1;i<=NF;i++){print $i}}' /etc/passwd	#分别打印每行的每列
3.6 awk数组
3.6.1 分析++i 和i++
[root@localhost zhu]# awk -F":" '{username[++i]=$1} END{print username[1] }' /etc/passwd
root
[root@localhost zhu]# awk -F":" '{username[i++]=$1} END{print username[1]}' /etc/passwd
bin
[root@localhost zhu]# awk -F":" '{username[i++]=$1} END{print username[0]}' /etc/passwd
root
3.6.2 数组遍历
1、按元素个数遍历,不推荐
awk -F":" '{user[++x]=$1} END{for(i=1;i<=x;i++){print i,user[i]} }' /etc/passwd
awk -F":" '{user[x++]=$1} END{for(i=0;i<x;i++)print i,user[i]}' /etc/passwd
2、按索引遍历,推荐
awk -F":" '{user[x++]=$1} END{for(i in user)print i,user[i]}' /etc/passwd
awk -F":" '{user[++x]=$1} END{for(i in user)print i,user[i]}' /etc/passwd

注意:变量i是索引

3.7 综合练习

思路:将需要统计的内容(某一个字段)作为数组的索引++

3.7.1 统计/etc/passwd中各种类型shell的数量
awk -F":" '{shell[$NF]++} END{for(i in shell) print i,shell[i]}' /etc/passwd
3.7.2 网站访问状态统计< 当前时实状态netstat>
netstat -ant | awk '{access_stat[$NF]++} END{for(i in access_stat)print i,access_stat[i]}'
netstat -ant | awk '{access_stat[$NF]++} END{for(i in access_stat)print i,access_stat[i]}' | sort -k2 -rn | head 5
ss -an | awk '{access_stat[$2]++} END{for(i in access_stat)print i,access_stat[i]}'

sort:表示排序

-k2:指定第二列排序

-rn:倒序排列

-n:正序排列

head 5:只显示前5行

3.7.3 统计当前访问的每个IP的数量<当前时实状态netstat,ss>
netstat -ant | grep :80 | awk -F":" '{ip_count[$8]++} END{for(i in ip_count){print i,ip_count[i]}}'
netstat -ant | grep :80 | awk -F":" '!/LISTEN/{ip_count[$(NF-1)]++} END{for(i in ip_count)print i,ip_count[i]}' | sort -k2 -rn | head
3.7.4 统计Apache/Nginx日志中某一天的PV量<统计日志>
grep '07/Aug/2012' cd.mobiletrain.org.log | wc -l

wc -l:只显示数量

3.7.5 统计Apache/Nginx日志中某-天不同IP的访问量 <统计 日志>
grep '07/Aug/2012' access.log | awk '{ips[$1]++} END{for(i in ips){print i,ips[i]}}' | sort -k2 -rn | head
# 过滤访问量大于100的
grep '07/Aug/2012' access.log | awk '{ips[$1]++} END{for(i in ips){print i,ips[i]}}' | awk '$2>100' | sort -k2 -rn
# 吧后面的过滤放在判断里
grep '07/Aug/2012' access.log | awk '{ips[$1]++} END{for(i in ips){if(ips[i]>100){print i,ips[i]}}' | sort -k2 -rn
3.7.6 统计用户名为4个字符的用户
awk -F":" '$1~/^....$/{count++;print $1} END{print "count is: " count}' /etc/passwd
awk -F":" 'length($1)==4{count++;print $1} END{print "count is: " count}' /etc/passwd

length($1):取得第一个字段的长度

3.8 awk使用外部变量
1 在双引号的情况下使用
[root@localhost ~]# var="bash"
[root@localhost ~]# echo "unix script" | awk "gsub(/unix/,\"$var\")"
bash script
2 在单引号的情况下使用
[root@localhost ~]# var="bash"
[root@localhost ~]# echo "unix script" | awk 'gsub(/unix/,"'"$var"'")'
bash script

[root@localhost ~]# i=10
[root@localhost ~]# df -h | awk '{if(int($5)>"'"$i"'"){print $6":"$5}}'
/:71%
/boot:15%
[root@localhost ~]# df -h | awk '{if(int($5)>'''$i'''){print $6":"$5}}'
/:71%
/boot:15%

sub(/unix/,"asdf"):替换一个

gsub(/unix/,"asdf"):替换全局

int(4%):格式化为数字4

3 awk参数-v(建议)

-v:指定变量,-v 变量名=变量值

[root@localhost ~]# echo "unix script" | awk -v var=hello 'gsub(/unix/,var)'
hello script
[root@localhost ~]# awk -v user=root -F":" '$1==user' /etc/passwd
root:x:0:0:root:/root:/bin/bash
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值