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或b | love|hate |
() | 组字符 | love(able|ers) |
(…)(…)\1\2 | 标签匹配字符,将第一个(..) 赋值给\1 ,后面同理 | (love) able\1 er |
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 -E
或 egrep
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 | 取消默认的输出 |
-i | inplace,就地编辑 |
-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…$100awk -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 格式化输出
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