第十一章 shell编程
正则表达式
正则表达式与通配符
- 区别 :
正则表达式是在文件中匹配字符串的,
grep, awk, sed
等命令可以支持正则表达式, 它是包含匹配.
通配符是匹配文件名的,
ls, find, cp
等命令不支持正则表达式, 只能使用通配符来匹配, 它是完全匹配.
他们的区别只限于linux系统, 其他编程语言不受限.
基础正则表达式
- 配合grep命令使用
元字符 | 作用 |
---|---|
* | 前一个字符匹配0次或多次 |
. | 匹配任意一个字符 ( 除了换行符 ) |
^ | 匹配行首, 如^hello 会匹配以hello开头的行 |
$ | 匹配行尾 |
[] | 匹配括号中指定的任意一个字符 |
[^] | 匹配非括号中的 |
\ | 转义符 |
\{n\} | 前面的字符恰好出现n次, 如[0-9]\{4\} 匹配数字出现4个的行 |
\{n,\} | 前面的字符出现不小于n次 |
\{n,m\} | 前面的字符至少出现n次, 最多出现m次 |
如:
[root@localhost tmp]# grep "c\{1,4\}" test
cccc123
cccc
字符截取命令
- cut
cut [参数] 文件名
: 匹配符合条件的列
参数 :
-f 列号
: 提取第几列
-d 分隔符
: 按照指定分隔符割列
局限性 : cut命令的默认分隔符为
tab键
, 如果匹配以空格为分隔符的内容, 则需使用awk命令.
[root@localhost tmp]# cat student
name age gender id
a 20 m 1
b 21 f 2
c 20 f 3
d 19 m 4
[root@localhost tmp]# cut -f 1,4 student
name id
a 1
b 2
c 3
d 4
[root@localhost tmp]# cat /etc/passwd #查看/etc/passwd文件
{
root:x:0:0:root:/root:/bin/bash
省略
ntp:x:38:38::/etc/ntp:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
centos:x:1000:1000::/home/centos:/bin/bash
}
[root@localhost tmp]# cat /etc/passwd | grep "/bin/bash$" #查看可登录的用户及信息
{
root:x:0:0:root:/root:/bin/bash
centos:x:1000:1000::/home/centos:/bin/bash
}
[root@localhost tmp]# cat /etc/passwd | grep "/bin/bash$" | grep -v "^root" #查看除了root用户的信息
{
centos:x:1000:1000::/home/centos:/bin/bash
}
[root@localhost tmp]# cat /etc/passwd | grep "/bin/bash$" | grep -v "^root" | cut -d ":" -f "1,3" #查看其他用户的第1和3列信息
{
centos:1000
}
- printf
printf '输出类型输出格式' 输出内容
- 输出类型 :
%s
: 输出字符串%i
: 输出整数%m.nf
: 输出浮点数, m-n是整数位, n是小数位
- 输出格式 :
\n
: 换行\r
: 回车\t
: tab键
- 输出类型 :
[root@localhost tmp]# printf '%s\n' 1 2 3
1
2
3
[root@localhost tmp]# printf '%s %s %s\n' 1 2 3 4 5 6
1 2 3
4 5 6
[root@localhost tmp]# printf '%s\t %s\t %s\t %s\n' $(cat student)
name age gender id
a 20 m 1
b 21 f 2
c 20 f 3
d 19 m 4
[root@localhost tmp]# cat student
name age gender id
a 20 m 1
b 21 f 2
c 20 f 3
d 19 m 4
- awk
awk命令是截取列的, 当cut命令无法实现功能时, 使用awk命令.
awk '条件1 {动作1} 条件2 {动作2} ...' 文件名
[root@localhost tmp]# awk '{printf $1 "\t" $2 "\n"}' test.student # $1表示提取第一列
name id #条件为任意
a 1
b 2
c 3
d 4
[root@localhost tmp]# df -h | awk '{printf $1 "\t" $5 "\t" $6 "\n"}'
Filesystem Use% Mounted # 解决cut命令的缺陷
devtmpfs 0% /dev
tmpfs 0% /dev/shm
tmpfs 2% /run
tmpfs 0% /sys/fs/cgroup
/dev/mapper/centos-root 56% /
/dev/sda1 83% /boot
tmpfs 1% /run/user/42
tmpfs 0% /run/user/0
BEGIN 动作
: 条件, 在最开始的时候执行这个动作
END 动作
: 条件, 在最后的时候执行这个动作
FS="分隔符"
: 指定分隔符, awk中默认分隔符为空格或制表符
[root@localhost tmp]# awk 'BEGIN{FS=":"} {printf $1 "\t" $3 "\n"} END{printf "ok" "\n"}' /etc/passwd
root 0
省略
centos 1000
ok
关系运算符
: 条件, 符合条件的才匹配
[root@localhost tmp]# cat test.student
name id gender age
a 1 m 10
b 2 m 11
c 3 f 10
d 4 f 11
[root@localhost tmp]# awk '$4>10 {printf $1 "\t" $4 "\n"}' test.student
name age
b 11
d 11
- sed
是一种流编辑器, 它与vi的区别在于, 它可以编辑管道符后面的内容.
sed [参数] '动作' 文件名
参数:
-n
: 不再显示全部内容, 而是显示具体的操作的内容, 与动作p
一块使用
-e
: 使多个动作同时进行
-i
: 会直接修改文件 ( 其他都不会修改文件内容 )
动作:
p
: 打印指定的行
d
: 删除指定的行
a
: 追加, 在行的下面
c
: 行替换
s/旧/新/g
: 字符串替换
i
: 插入, 在行的上面
[root@localhost tmp]# cat test.student
name id gender age
a 1 m 10
b 2 m 11
c 3 f 10
d 4 f 11
[root@localhost tmp]# sed -n '2p' test.student
a 1 m 10
[root@localhost tmp]# sed '2a curry' test.student
name id gender age
a 1 m 10
curry
b 2 m 11
c 3 f 10
d 4 f 11
[root@localhost tmp]# sed '3i cu' test.student
name id gender age
a 1 m 10
cu
b 2 m 11
c 3 f 10
d 4 f 11
[root@localhost tmp]# sed '3d' test.student
name id gender age
a 1 m 10
c 3 f 10
d 4 f 11
[root@localhost tmp]# sed '3c curry' test.student
name id gender age
a 1 m 10
curry
c 3 f 10
d 4 f 11
[root@localhost tmp]# sed 's/name/n/g' test.student
n id gender age
a 1 m 10
b 2 m 11
c 3 f 10
d 4 f 11
[root@localhost tmp]# sed -e 's/name/n/g ; s/age/a/g' test.student
n id gender a
a 1 m 10
b 2 m 11
c 3 f 10
d 4 f 11
字符处理命令
sort命令
sort [参数] 文件名
: 排序- 参数 :
-f
: 忽略大小写-r
: 反向排序-t
: 指定分隔符, 默认为制表符-k 3
: 对第三列进行排序
- 参数 :
[root@localhost tmp]# sort test.student
a 1 m 10
b 2 m 11
c 3 f 10
d 4 f 11
name id gender age
[root@localhost tmp]# sort -r test.student
name id gender age
d 4 f 11
c 3 f 10
b 2 m 11
a 1 m 10
[root@localhost tmp]# cat test.student
name id gender age
a 1 m 10
b 2 m 11
c 3 f 10
d 4 f 11
wc命令
wc [参数] 文件名
: 统计- 参数 :
-l
: 只统计行数-w
: 单词数-m
: 字符数
- 参数 :
[root@localhost tmp]# wc test.student
5 20 55 test.student
[root@localhost tmp]# wc -l test.student
5 test.student
[root@localhost tmp]# wc -w test.student
20 test.student
[root@localhost tmp]# wc -m test.student
55 test.student
条件判断
- 按照文件类型进行判断
-d 文件
: 判断文件是否存在, 并且是否为目录文件 ( 是目录为真 )
-e 文件
: 判断文件是否存在 ( 存在为真 )
-f 文件
: 判断文件是否存在, 并且是否为普通文件 ( 是普通文件为真 )- 判断格式:
[ -e 文件 ]
[root@localhost tmp]# [ -e test.student ] && echo "yes" || echo "no" yes
- 判断格式:
- 按照文件权限进行判断
-r 文件
: 判断文件是否存在, 并且是否文件拥有读权限 ( 有读权限为真 )
-w 文件
: 判断文件是否存在, 并且是否文件拥有写权限 ( 有写权限为真 )
-x 文件
: 判断文件是否存在, 并且是否文件拥有执行权限 ( 有执行权限为真 ) - 两个文件之间进行比较
文件1 -nt 文件2
: 判断文件1的修改时间是否比文件2的新 ( 如果新则为真 )
文件1 -ot 文件2
: 判断文件1的修改时间是否比文件2的旧 ( 如果旧则为真 )
文件1 -ef 文件2
: 判断文件1是否和文件2的inode号一致 ( 如果一致为真 ), 可以判断硬链接 - 两个整数之间比较
整数1 -eq 整数2
: 判断整数1是否和整数2相等
-ne
: 不相等
-gt
: 大于
-lt
: 小于
-ge
: 大于等于
-le
: 小于等于 - 字符串的判断
-z 字符串
: 判断字符串是否为空
-n
: 非空
1 == 2
: 相等
1 != 2
: 不相等 - 多重条件判断
判断1 -a 判断2
: 逻辑与, 都成立为真
1 -o 2
: 逻辑或, 有一个成立就为真
! 判断
: 逻辑非, 使判断式取反
流程控制
if
- 单分支
if [ 条件判断 ]
then
执行语句
fi
[root@localhost tmp]# cat if1.sh
#!/bin/bash
rate=$(df -h | grep "/dev/sda1" | awk '{printf $5 "\n"}' | cut -d "%" -f 1)
if [ $rate -ge 80 ]
then
echo "waring : /dev/sda1 is full!"
fi
- 多分支
if [ 条件判断1 ]
then
执行语句1 # 条件1成立时, 执行
else
elif [ 条件判断2 ]
then
执行语句2 # 条件2成立时, 执行
else
执行语句3 # 当所有条件都不成立时, 执行
fi
[root@localhost tmp]# cat if2.sh
#!/bin/bash
read -p "please input a filename :" file
if [ -z $file ]
then
echo "it is null"
exit 0
elif [ ! -e $file ]
then
echo "it is unlive"
exit 1
elif [ -d $file ]
then
echo "it is a directory"
elif [ -f $file ]
then
echo "it is a file"
fi
[root@localhost tmp]# ./if2.sh
please input a filename :
it is null
[root@localhost tmp]# ./if2.sh
please input a filename :asd
it is unlive
[root@localhost tmp]# ./if2.sh
please input a filename :test
it is a file
[root@localhost tmp]# ./if2.sh
please input a filename :/tmp
it is a directory
case
- case语句只能判断一种条件关系, 而if语句可以判断多种条件关系.
- 格式 :
case $变量 in
"值1")
语句1 ( 如果变量的值为1, 则执行 )
;;
"值2")
语句2
;;
...等
*)
语句 ( 如果变量的值都不是以上的值, 则执行 )
;;
esac
[root@localhost tmp]# cat case3.sh
#!/bin/bash
read -p "please input a day : " day
case $day in
[1-5])
echo "workday"
;;
[6-7])
echo "weekday"
;;
*)
echo "error"
;;
esac
[root@localhost tmp]# ./case3.sh
please input a day : 2
workday
[root@localhost tmp]# ./case3.sh
please input a day : 4
workday
[root@localhost tmp]# ./case3.sh
please input a day : 7
weekday
[root@localhost tmp]# ./case3.sh
please input a day : 10
error
for
- 循环
- 格式1 :
for 变量 in 值1 值2 值3 ...等
do
语句
done
[root@localhost tmp]# vi for1.sh
1 #!/bin/bash
2
3 cd /tmp
4
5 ls *.sh > sh
6
7 j=1
8
9 for i in $(cat sh)
10 do
11 echo $j
12 j=$(( j+=1 ))
13 done
~
~
~
~
~
~
"for1.sh" 13L, 96C written
[root@localhost tmp]# ./for1.sh
1
2
3
4
5
6
7
8
9
10
11
12 # 一共12个shell脚本
- 格式2 :
for (( 初始值; 循环控制条件; 变量变换 ))
do
语句
done
[root@localhost tmp]# cat for2.sh
#!/bin/bash
i=1
for ((i=1; i<=10 ;i+=1))
do
echo $i
done
[root@localhost tmp]# ./for2.sh
1
2
3
4
5
6
7
8
9
10
[root@localhost tmp]# vi for3.sh
1 #!/bin/bash
2
3 sum=0
4
5 for ((i=1;i<=100;i+=1))
6 do
7 sum=$(($sum+$i))
8 done
9
10 echo $sum
"for3.sh" 10L, 84C written
[root@localhost tmp]# ./for3.sh
5050
while
- while循环只要判断条件成立, 循环就会一直继续 ( 可以无限循环 )
- 格式
while [ 条件判断 ]
do
语句
done
[root@localhost tmp]# cat while1.sh
#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
sum=$(($sum+$i))
i=$(($i+1))
done
echo "sum=$sum"
[root@localhost tmp]# ./while1.sh
sum=5050
[root@localhost tmp]# cat while2.sh
#!/bin/bash
sum1=0
sum2=0
i=0
while [ $i -le 100 ]
do
if [ $(($i % 2)) != 0 ]
then
sum1=$(($sum1+$i))
fi
if [ $(($i % 2)) == 0 ]
then
sum2=$(($sum2+$i))
fi
i=$(($i+1))
done
echo "sum1=$sum1"
echo "sum2=$sum2"
[root@localhost tmp]# ./while2.sh
sum1=2500
sum2=2550