shell脚本

shell脚本

一、shell脚本常用的命令

1. cat,head,tail命令
求/etc/passwd文件第20行内容

[root@manager test1]# cat -n /etc/passwd | head -20 | tail -1
2. rev,tac命令

rev左右颠倒
tac上下颠倒

3. find命令
常用选项
            -name
            -type
            -user
            -nouser
            -group
            -nogroup
            -mtime
            -size
可以使用 -o 或者 -a 连接多个条件
可以使用-exec或者-ok来执行shell命令 
 find /etc/ -name hosts -exec cp {} /tmp/ \;
如: 
 find /var/logs -type f -mtime +7 -exec rm {} \;

xargs
在使用f i n d命令的- e x e c选项处理匹配到的文件时, f i n d命令将所有匹配到的文件一起传递 给e x e c执行。不幸的是,有些系统对能够传递给 e x e c的命令长度有限制,这样在 f i n d命令运行 几分钟之后,就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。
如:
[root@manager home]#  find / -name "core" -exec file {} \;
[root@manager home]#  find / -name "core" |xargs  file
4. 计划任务crond和crondtab
crontab -e   #编辑
*/10 * * * *   脚本|命令

-l #查看
-r #删除

5. &命令

当在前台运行某个作业时,终端被该作业占据;而在后台运行作业时,它不会占据终端。
xclock -update 1 & 后台运行

6. nohup命令

如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用 n o h u p命令。该命令可以在你退出帐户之后继续运行相应的进程。 N o h u p就是不挂起的意思( n o hang up)。
该命令的一般形式为: nohup command &
nohup xclock -update 1 &

7. shell的通配符
*
?
[...][!...]  [a-z] [0-9] [!a12d]
{..} 

8. echo命令
-e 使转义符生效 如:  解释\t \n含义
-n 不换行输出

字颜色:30—–37 
echo -e “\033[30m 黑色字 \033[0m” 
echo -e “\033[31m 红色字 \033[0m” 
echo -e “\033[32m 绿色字 \033[0m” 
echo -e “\033[33m 黄色字 \033[0m” 
echo -e “\033[34m 蓝色字 \033[0m” 
echo -e “\033[35m 紫色字 \033[0m” 
echo -e “\033[36m 天蓝字 \033[0m” 
echo -e “\033[37m 白色字 \033[0m”

字背景颜色范围:40—–47 
echo -e “\033[40;37m 黑底白字 \033[0m” 
echo -e “\033[41;37m 红底白字 \033[0m” 
echo -e “\033[42;37m 绿底白字 \033[0m” 
echo -e “\033[43;37m 黄底白字 \033[0m” 
echo -e “\033[44;37m 蓝底白字 \033[0m” 
echo -e “\033[45;37m 紫底白字 \033[0m” 
echo -e “\033[46;37m 天蓝底白字 \033[0m” 
echo -e “\033[47;30m 白底黑字 \033[0m”

改变提示符文件的颜色
[root@manager home]# echo -e "\033[40;32m"

报警声音
[root@manager home]# echo -e "\007 the bell ring"
9. printf命令
[root@manager home]# printf aa
aa[root@manager home]# printf "aa\n"
aa

格式化输出
[root@manager home]# printf "%s,%s,%d\n" hello world 123
hello,world,123

%s  字符串  %d十进制整数
10. read命令
可以使用r e a d语句从键盘或文件的某一行文本中读入信息,并将其赋给一个变量。如果只指定了一个变量,那么 r e a d将会把所有的输入赋给该变量,直至遇到第一个文件结束符或回 车。
如:
赋值
[root@manager home]# read name
zhangsan
[root@manager home]# echo $name
zhangsan

赋多值
[root@manager home]# read firstname lastname
huibin zhang
[root@manager home]# echo $firstname $lastname
huibin zhang

交互式:
[root@manager home]# read -p "input a num: " var
input a num: 123
[root@manager home]# echo $var
123
11. |管道命令
管道(Pipe)实际是用于进程间通信的一段共享内存. 创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机
管道命令的两个作用:
1.管道两边产生两个子进程
2.前一个进程的标准输出和后一个进程的标准输入 

注意以下情况不能赋值
echo 123 | read aa
echo $aa

ps -f | cat
UID    PID PPID C STIME TTY     TIME CMD
root   14412 14405 0 04:56 pts/3  00:00:00 -bash
root   15485 14412 0 05:53 pts/3  00:00:00 ps -f
root   15486 14412 0 05:53 pts/3  00:00:00 cat
3个进程没有任何父子进程关系
如图:
bash ps -f | cat
1000 2000   3000
   echo 123 | read a
bash
|______ps -f > 
|______cat <

bash
|______echo 123 > 
|______read a <
12. 重定向(文件描述符)
文件描述符:进程连接到文件时,获得的一个标记.当进程操作文件时,首先
打开文件 获得打开文件的一个状态,给它一个标记 成为文件描述符
0标准输入
1标准输出
2标准错误输出

> >> 定向符(重定向) >覆盖  >>追加
1> 标准正确输出,文件存在则覆盖,不存在则创建
1>> 标准正确输出,文件存在则追加,不存在则创建
2> 标准错误输出,文件存在则覆盖,不存在则创建
2>> 标准错误输出,文件存在则追加,不存在则创建
&> 标准正确和标准错误输出,文件存在则覆盖,不存在则创建

cat < /dev/sda > /dev/null 测试改变文件描述符
 
ls >cleanup.out 2>&1
在上面的例子中,我们将 ls命令的输出重定向到 cleanup.out文件中,而且其错误也 被重定向到相同的文件中。
2>&1 标准错误输出定向到标准正确输出



< 输入重定向 后边跟的是文件     > >>
<< here document 后边跟的是一个文本
如下
cat > x.txt << EOF 
> sdfsadlkf
> asdfsadhf
> asfdhkasfd
> EOF ------------直到遇到EOF结束

[root@manager tmp]# fdisk /dev/sda <<EOF
> n
> 
> +200M
> w
> EOF

<<<here string 后边跟字符串 直接交给文本 如:
cat >x.txt <<<asdadad
cat x.txt
13. tee命令
t e e命令作用可以用字母 T来形象地表示。它把输出的一个副本输送到标准输出,另一个 副本拷贝到相应的文件中。如果希望在看到输出的同时,也将其存入一个文件, 这种情况可以使用tee命令
如:
[root@manager home]# who | tee who.out
root     pts/1        2016-09-18 07:50 (192.168.10.102)
[root@manager home]# cat who.out 
root     pts/1        2016-09-18 07:50 (192.168.10.102)

[root@manager home]# find /etc -name hosts | tee aa.txt
/etc/hosts
[root@manager home]# cat aa.txt 
/etc/hosts
14. sort命令
[root@manager tmp]# cat aa.txt 
2
4
3
21
90
78
45
23
2
3
5
1
[root@manager tmp]# sort aa.txt 
1
2
2
21
23
3
3
4
45
5
78
90
[root@manager tmp]# sort -n aa.txt 
1
2
2
3
3
4
5
21
23
45
78
90
[root@manager tmp]# sort -n -r aa.txt    按完整数字排序 降序
90
78
45
23
21
5
4
3
3
2
2
1
[root@manager tmp]# sort -u aa.txt  去掉重复值 
1
2
21
23
3
4
45
5
78
90

[root@manager tmp]# sort -t: -k3nr /etc/passwd
15. uniq命令(默认去掉连续的重复值)
[root@manager tmp]# cat aa.txt 
2
4
4
3
3
21
90
78
45
23
2
3
5
1
[root@manager tmp]# uniq aa.txt  连续的去掉
2
4
3
21
90
78
45
23
2
3
5
1
[root@manager tmp]# uniq -u aa.txt  显示未重复值
2
21
90
78
45
23
2
3
5
1
[root@manager tmp]# sort -n aa.txt  | uniq -u 排序去重
1
5
21
23
45
78
90

[root@manager tmp]# sort -n aa.txt  | uniq -d  显示重复行
2
3
4
[root@manager tmp]# sort -n aa.txt  | uniq -d -c  统计重复次数
      2 2
      3 3
      2 4
16. grep命令
--color 
-i   忽略大小写
-A  后几行
-B  前几行
-C  前后几行
-r   递归目录
-l   只显示匹配文件名
-x  完全一样
-n  显示行号



[root@manager tmp]# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

[root@manager tmp]# grep ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@manager tmp]# grep halt$ /etc/passwd
halt:x:7:0:halt:/sbin:/sbin/halt

[root@manager tmp]# grep -A 2 root /etc/passwd
[root@manager tmp]# grep -B 2 root /etc/passwd
[root@manager tmp]# grep -C 2 root /etc/passwd
[root@manager tmp]# grep -c root /etc/passwd 统计行数
2

[root@manager tmp]# grep -n root /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
11:operator:x:11:0:operator:/root:/sbin/nologin

[root@manager tmp]# grep -r root /etc/  过滤所有文件
[root@manager tmp]# grep -rl root /etc/  列出文件名
[root@manager farm]# grep -rl  'localhost' /usr/local/apache/htdocs/farm/

[root@manager tmp]# cat aa.txt 
abc
ABC
xyz
XYZ 
[root@manager tmp]# grep abc aa.txt 
abc
[root@manager tmp]# grep -i abc aa.txt 
abc
ABC

[root@manager tmp]# grep -ix abc aa.txt 
ABC
[root@manager tmp]# grep -i ^abc$ aa.txt 
ABC
17. cut命令(截取)
[root@manager tmp]# cat aa.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
[root@manager tmp]# cut -c 1 aa.txt 
r
b
d
a
l
[root@manager tmp]# cut -c 1,3,5 aa.txt 
ro:
bnx
deo
amx
l::
[root@manager tmp]# cut -c 1-5 aa.txt 
root:
bin:x
daemo
adm:x
lp:x:

[root@manager tmp]# cut -d: -f 1 aa.txt 
root
bin
daemon
adm
lp

[root@manager tmp]# cut -d: -f 1,3,5 aa.txt 
root:0:root
bin:1:bin
daemon:2:daemon
adm:3:adm
lp:4:lp
[root@manager tmp]# cut -d: -f 1-5 aa.txt 
root:x:0:0:root
bin:x:1:1:bin
daemon:x:2:2:daemon
adm:x:3:4:adm
lp:x:4:7:lp

[root@manager tmp]# cat cc.txt 
aa cc
kk hh
[root@manager tmp]# cut -d' ' -f 1 cc.txt 
aa
kk
18. tr命令(替换)
• 大小写转换。 
• 去除控制字符。 
• 删除空行。 
[a-z]    a-z内的字符组成的字符串。 [A-Z]   A-Z内的字符组成的字符串。 [0-9]    数字串

 \- s选项去掉重复字符
[root@manager etc]# echo "hellooooooo worlddddddd" | tr -s "[a-z]"
helo world

[root@manager tmp]# cat tt.txt 
hellooooooooooooo worldddddddddddddddddddd
[root@manager tmp]# cat tt.txt | tr -s '[a-z]'
helo world
[root@manager tmp]# cat tt.txt | tr -s '[a-k][m-z]'
hello world


删除空行
[root@manager home]# tr -s "[\n]" < aa.txt 
1.robin 19
2.zorro  30
3.tom  35

大小写转换
[root@manager home]# tr "[a-z]" "[A-Z]" < cc.txt 
1.ROBIN MAN
2.ZORRO MAN
3.TOM MAN

如果需要删除文件中^M,并代之以换行。使用命令:
tr -s "[\r]" "[\n]" < file.txt
19. wc命令
-l
-w
-c
[root@manager ~]# wc -l /etc/passwd
49 /etc/passwd

[root@manager tmp]# wc -w kk.txt 
3 kk.txt

[root@manager tmp]# wc -c kk.txt 
6 kk.txt
20. eval,date,logger,bc命令
eval命令

[root@manager home]# aa="cat cc.txt"
[root@manager home]# echo $aa
cat cc.txt
[root@manager home]# eval $aa
1.robin man
2.zorro man
3.tom man

date命令
[root@manager tmp]# date
2016年 09月 15日 星期四 01:20:48 CST
[root@manager tmp]# date +%F
2016-09-15

[root@manager tmp]# date 月日时份年.秒
[root@manager tmp]# date -s "20161015 10:10:10"
20161015日 星期六 10:10:10 CST

[root@manager tmp]# date +%Y-%m-%d-%H-%M-%S
2016-09-20-13-58-09


logger命令
[root@manager tmp]# logger "hello i am robin"

自定日志保存位置
[root@manager tmp]# vim /etc/rsyslog.conf
local5.*                                                /tmp/test.log
[root@manager tmp]# service rsyslog restart

[root@manager tmp]# logger -p local5.err -t test  -f /tmp/test.log  hhhhhhh
-p日志级别
-t 标记
-f 日志位置

bc命令(支持小数运算)
整数 
[root@manager ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
1+2
3
3-1
2
2*2
4
2/2
1
5%2
1
5^2
25

小数
[root@manager ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
scale=3
7/3
2.333

进制转换
[root@manager ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
ibase=10;obase=2
10
1010
2
10
ibase=8;obase=10
9
11
ibase=10;obase=16
11
B

非交互式
[root@manager ~]# echo "1+2" | bc
3
[root@manager ~]# echo "scale=3;3/2" | bc
1.500
[root@manager ~]# echo "ibase=10;obase=2;7" | bc
111

[root@manager tmp]# echo "obase=8;19" | bc
23
[root@manager tmp]# echo "obase=2;F" | bc
1111

[root@manager tmp]# echo "2^10" | bc
1024
计算平方根
[root@manager ~]# echo "sqrt(100)" | bc
10
21. 其他
basename命令
[root@manager ~]# basename /var/log/messages
messages
[root@manager ~]# basename /var/log/
log

split命令(分割文件)
[root@manager home]# cp /etc/passwd ./
[root@manager home]# split -l 2 passwd 
[root@manager home]# ls
passwd  xaa  xab  xac  xad  xae  xaf  xag  xah  xai  xaj
[root@manager home]# cat xaa
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin

[root@manager tmp]# cat passwd | split -l 10
[root@manager tmp]# ls
passwd  xaa  xab  xac  xad  xae

[root@manager tmp]# split -b 1 aa.txt 
[root@manager tmp]# ls
aa.txt  xaa  xab  xac  xad  xae  xaf



join命令
有匹配域
[root@manager home]# cat aa.txt 
1.robin 19
2.zorro  30
3.tom  35
[root@manager home]# cat cc.txt 
1.robin man
2.zorro man
3.tom man
[root@manager home]# join aa.txt cc.txt 
1.robin 19 man
2.zorro 30 man
3.tom 35 man
如果一个文件与另一个文件没有匹配域时怎么办?这时 j o i n不可以没有参数选项,经常指 定两个文件的- a选项。下面的例子显示匹配及不匹配域。
[root@manager home]# join -a1 -a2 aa.txt cc.txt 
1.robin 19 man
2.zorro 30 man
3.tom 35 man
4.jerry man
[root@manager home]# join aa.txt cc.txt 
1.robin 19 man
2.zorro 30 man
3.tom 35 man

paste用法
paste将按行将不同文件行信息放在一行。缺省情况下, p a s t e连接时,用空格或t a b键分隔 新行中不同文本,除非指定- d选项,它将成为域分隔符。 
[root@manager home]# paste aa.txt cc.txt 
1.robin 19	1.robin man
2.zorro  30	2.zorro man
3.tom  35	3.tom man
	4.jerry man

paste命令还有一个很有用的选项( -)。意即对每一个( -),从标准输入中读一次数据。 使用空格作域分隔符,以一个4列格式显示目录列表。方法如下:
[root@manager etc]# ls | paste -d" " - - - - - - 



||逻辑或 前边命令失败执行后边命令
&&逻辑与 前边命令成功运行后边命令
pwd && echo ok
adfa && echo ok
 
pwd || echo ok
adfa || echo ok

22. 练习
1.统计当前系统中有多少个用户可以登录
[root@manager ~]# grep "/bin/bash$" /etc/passwd | wc -l
14
[root@manager ~]# grep -c "/bin/bash$" /etc/passwd 
14
[root@manager ~]# cut -d: -f 7 /etc/passwd | grep bash | uniq -c
     14 /bin/bash

2.取ip地址?
[root@manager ~]# ifconfig eth2 | head -2 | tail -1 | cut -d':' -f 2 | cut -d' ' -f 1
[root@manager ~]# ifconfig eth2 | grep Bcast | cut -d':' -f 2 | cut -d' ' -f 1
172.16.20.1
[root@manager ~]# ifconfig | grep Bcast | cut -d':' -f 2 | cut -d' ' -f 1
172.16.20.1
172.16.20.201
3.
[root@manager ~]# stat install.log
  File: "install.log"
  Size: 8003      	Blocks: 16         IO Block: 4096   普通文件
Device: 802h/2050d	Inode: 130307      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2016-09-09 06:29:21.250999892 +0800
Modify: 2016-09-09 06:30:57.320999775 +0800
Change: 2016-09-09 06:30:59.526999773 +0800

取时间
06:29:21
06:30:57
06:30:59

[root@manager ~]# stat /root/install.log | tail -3 | cut -d" " -f3 | cut -d'.' -f 1
18:02:07
09:54:49
09:54:49
[root@manager ~]# stat /root/install.log | tail -3 | cut -d" " -f1,3 | cut -d'.' -f 1
Access: 18:02:07
Modify: 09:54:49
Change: 09:54:49

4.将ip地址192.168.10.100转换成点分二进制
[root@manager ~]# echo "obase=2;192" | bc 
11000000
[root@manager ~]# echo "obase=2;168" | bc 
10101000
[root@manager ~]# echo "obase=2;10" | bc 
1010
[root@manager ~]# echo "obase=2;100" | bc 
1100100

5.将passwd文件按uid排序?按gid排序?
[root@manager ~]# sort -t: -k3n /etc/passwd
[root@manager ~]# sort -t: -k4n /etc/passwd


作业:
写一个 一键配置yum库脚本
[root@manager test1]# cat yum.sh
#!/bin/bash
#configure yum scripts

#mount cdrom
umount /yum
mount /dev/cdrom /yum

#configure yum
rm -rf /etc/yum.repos.d/*
touch /etc/yum.repos.d/yum.repo
cat > /etc/yum.repos.d/yum.repo <<EOF
[CentOS6.6]
name=server
baseurl=file:///yum
gpgcheck=0
enabled=1
EOF

#test yum
yum clean all
yum makecache


[root@mycat ~]# mount /dev/cdrom /mnt/
[root@mycat ~]# cd /etc/yum.repos.d/
[root@mycat yum.repos.d]# mkdir old
[root@mycat yum.repos.d]# mv * old
[root@mycat yum.repos.d]# touch local.repo
[root@mycat yum.repos.d]# cat > local.repo <<EOF
> [local]
> name=local
> baseurl=file:///mnt
> gpgcheck=0
> enabled=1
> EOF
[root@mycat yum.repos.d]# yum clean all 
[root@mycat yum.repos.d]# yum makecache 



#!/bin/bash
mount /dev/cdrom /mnt/

cd /etc/yum.repos.d/
mkdir old
mv * old
touch local.repo
cat > local.repo <<EOF
[local]
name=local
baseurl=file:///mnt
gpgcheck=0
enabled=1
EOF

yum clean all
yum makecache

二、shell脚本基本支持及变量

1. 基础知识

1.shell脚本的格式注意事项
2.shell脚本文件的扩展名
3.shell脚本执行顺序以及产生后果(如出现错误)
a /tmp/test
rm –rf a / ∗ 4. 用 户 身 份 的 不 同 执 行 脚 本 的 区 别 5. s h e l l 种 类 的 介 绍 ( n o l o g i n 和 锁 定 区 别 ) 及 用 户 切 换 切 换 s h e l l 方 式 ( b a s h c h s h − l ) 6. 历 史 命 令 的 介 绍 h i s t o r y ! ! ! 100 ! s e r ! a/* 4.用户身份的不同执行脚本的区别 5.shell种类的介绍(nologin和锁定区别)及用户切换切换shell方式(bash chsh -l) 6.历史命令的介绍history !! !100 !ser ! a/4.5.shell(nologin)shell(bashchshl)6.history!!!100!ser! alt+.
7.Shell退出时执行的命令.bash_logout
8.别名的介绍alias(以及命令的回顾)

2. 变量

1.环境变量
环境变量是一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息。例如path,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。用户通过设置环境变量,来更好的运行进程
环境变量:系统在启动过程中预先指定好的一系列的变量.比如当前用户是谁 当前shell是什么 当前用户的家目录在什么位置等等
2.预定义变量
预定义变量:系统预定义好的 和进程名称 进程编号 进程参数 进程返回值相关
3.位置变量:和命令行参数相关
4.自定义变量(用户自己定义的变量)

第一类 环境变量
环境变量 echo
env 查看所有环境变量
echo $变量名 输出变量
PATH
USER
HOME
HOSTNAME
PWD
UID
PS1
LANG=zh_CN.UTF8 (setup,yum groupinstall.系统时间)

set 查看所有变量(包括环境变量和非环境变量)
非环境变量:为用户定义的变量

export x=100 环境变量 可以被子进程所调用
y=200 非环境变量 不能被子进程所调用

环境变量和非境变量永久生效,写入配置文件

每个用户家目录下的环境变量配置文件:
.bash_history 保存用户执行过来历史命令,当用户退出时保存
.bash_logout 保存用户退出时执行的命令

.bash_profile 保存用户定义环境和启动项目,用户执行命令时的搜索路径
.bashrc 保存用户别名和函数

.bash_profile 登录级别环境配置文件
.bashrc shell级别的环境配置文件

/etc/bashrc 全局shell级别环境配置文件
/etc/profile 全局登录级别环境配置文件

登录时加载的配置文件顺序
/etc/profile
.bash_profile
.bashrc
/etc/bashrc

su - robin 和 su robin 切换帐号的区别
su - robin 登录级别切换
su robin shell级别切换

练习:
1.当robin用户退出时,清除自己所有的历史命令
[root@manager robin]# vim /home/robin/.bash_logout
 rm -rf /home/robin/.bash_history
 history -c

2.设置别名myip 要求:所有用户可以调用这个别名
[root@manager robin]# vim /etc/bashrc 
alias myip="ifconfig eth2 | grep Bcast | cut -d':' -f 2 | cut -d' ' -f 1"
3.当一个用户登录时将这个用户登录的用户名,时间写入到/tmp/login.txt文件
[root@manager robin]# vim /etc/profile
echo "$USER" 'login' `date`  >> /tmp/login.txt


第二类 预定义变量$0 KaTeX parse error: Can't use function '$' in math mode at position 2: $̲# $? $* $0 进程名(… 当前进程号(/var/run 模拟系统结束进程)
$# 位置参数的数量
$* 表示在命令行中实际给出的所有实参字符串
$? 命令执行后的返回状态.0为执行正确,非0为执行错误

$! 后台运行的最后一个进程的进程号

第三类 位置变量
位置变量: 和命令行参数相关 (命令后跟的参数$1-$9)

第四类自定义变量
自定义变量:当用户变量不够用时,自定义的变量

如下测试脚本
#!/bin/bash
cd /tmp
touch a.txt
ls –ld /tmp
du –sh /tmp
改为
#!/bin/bash
$DIR=/tmp
cd $DIR
touch a.txt
ls –ld $DIR
du –sh $DIR

练习:
计算器 四则运算
./脚本 1 + 2
3

再进一步完善脚本read 命令的使用

算式运算符:

+、-、*、/、()                 #$()优先执行    $(())运算
1.$((5+3))
2.$[ 5+3 ]
3.expr操作符:
+、-、\*、/、%取余(取模)
expr 1 + 2
4.a=1;b=2
let c=$a+$b
echo $c

export作用范围
父子shell的说明,及变量的定义
如例子:
a.sh

#!/bin/bash
echo IN a.sh’
aa=123
./b.sh

b.sh

#!/bin/bash
echo ‘IN b.sh’
echo $aa

运行脚本方式的介绍
./a.sh
bash a.sh
source a.sh #加载脚本
. a.sh #加载脚本,多用于调用函数库
source a.sh 和. a.sh

别名 内部命令 外部命令

[root@manager tmp]# type ls   别名
ls is aliased to `ls --color=auto'

[root@manager tmp]# type useradd   外部 产生子进程
useradd is /usr/sbin/useradd

[root@manager tmp]# type cd  内部  不产生子子进程在当前环境下运行
cd is a shell builtin

别名>外部命令>内部

利用当前的shell执行后边的脚本 如果没有外部命令 则不产生子进程
如测试脚本如下
cat aa.sh
#!/bin/bash
cd /
pwd

./ aa.sh
. aa.sh

函数库定义
函数库:将常用的变量定义到一个文件里 直接加载这个文件 就不用重复定义变量了
如系统中的确定与失败

子进程定义的变量能否被父进程集成?

nologin shell 和 login shell
/etc/bashrc
/etc/profile
~/.bashrc
~/.bash_profile

read命令的使用
read命令:将脚本后边跟着的变量的值读取到脚本中
-p –t 参数的说明
如下边这个有趣的脚本:

#!/bin/bash
read -p "请输入银行卡账号: " num
read -p "请输入银行卡密码: " -t 5 pass
echo 账号$num 密码$pass >> /tmp/haha.txt

yum install postfix
service postfix restart

#!/bin/bash
read -p "输入帐号: " account
stty -echo    #去掉终端输出
read -p "输入密码: " -t 5  pass
stty echo     #恢复终端输出
echo
echo "帐号:$account 密码:$pass" >> /tmp/login.txt
echo "帐号:$account 密码:$pass" | mail -s "auth" root@localhost


算式置换

a=10+20
a= ( ( 10 + 20 ) ) 命 令 置 换 ( 将 命 令 执 行 结 果 赋 给 变 量 ) a = ‘ d a t e + a = ((10+20)) 命令置换(将命令执行结果赋给变量) a=`date +%m%d` a= ((10+20))()a=date+a=(date +%m%d) 推荐
[root@manager test1]# file=lsdate +%F`` 报错
[root@manager test1]# file=$(ls $(date +%F))
[root@manager test1]# echo $file
2016-09-20

原因
a=lsdate +%m%d`` 该赋值失败
a=$(ls $(date +%m%d ))

通配符
通配符是shell解释的 正则表达式是命令解释的
*匹配任所有字符
?匹配一个字符
[]匹配一个范围
{}如touch abc{a,b,c}{1…3}.txt

变量的引用
echo 命令介绍
echo -n -e参数说明 “”’’说明 \n \t
echo $
echo ’$’

[root@manager test1]# echo hello world;i am robin 报错

[root@manager test1]# echo "hello world;i am robin"
hello world;i am robin
[root@manager test1]# echo 'hello world;i am robin'
hello world;i am robin

[root@manager test1]# name=robin
[root@manager test1]# echo 'hello world;i am $name'
hello world;i am $name
[root@manager test1]# echo "hello world;i am $name"
hello world;i am robin

三、test命令及判断语法

1. test命令的使用

语法:test EXPRESSION 或者 [ EXPRESSION ]

字符串判断(用于看用户有没有输入指定的变量 指定用户输入目录 如没
填)
-n 字符段长度是否非零的 如果结果为真值 返回值为0 如果结果为假值
返回值非0
-z 判断是否为空 ,和-n相反
例:判断两个文件名字是否一致
mkdir /a /b
touch /a/x.txt /b/x.txt
[ “/a/x.txt”="/b/x.txt"] 可定错误不同 目录名称不同
应为
[ “ ( b a s e n a m e / a / x . t x t ) " = " (basename /a/x.txt)"=" (basename/a/x.txt)"="(basename /b/x.txt)” ]

test 整数

eq 等于
ge 大于等于
gt 大于
le 小于等于
lt 小于
ne 不等于

test 文件
ef 两个文件有相同的设备编号和inode编号 (判断硬链接)
touch aa
ln aa bb
ls -i
456733 aa 456733 bb

根据文件类型判断
-d 文件存在必须是个目录
-e 有文件名存在就行不管类型
-f 文件存在而且是个标准普通文件
-h 文件存在并且是否为符号链接文件
-r 判断文件权限是否有r权限
-w 写权限
-x 执行权限

条件判断语句
if cmd;如为真值
then
fi 执行

如为假值则不执行


if [ -f /etc/passwd ]
then
echo ok
fi

若文件不存 则 不执行

if useradd uu3
then
id uu3
fi
添加成功则显示用户信息

if [ -f /etc/ssh/sshd_config ]
then
service sshd start
else
echo ssh is not install
fi

练习:

1.提示输入一个用户名字,判断该用户是否存在?存在显示其信息(uid gid  家目录 shell),不存在添加该帐号
#!/bin/bash
read -p "输入用户名: " username
if id $username &> /dev/null
then
	echo "用户存在,显示信息"
	grep ^${username}:  /etc/passwd | cut -d':' -f 1,3,4,6,7
else
	echo "用户$username不存在,添加用户"
	useradd $username
fi

2.提示输入文件路径及文件名,判断该文件是否存在,存在显示其详细信息,不存在创建该文件
[root@manager test1]# cat file.sh 
#!/bin/bash
read -p "输入完整文件名: " file
if [ -f $file ]
then
	echo "文件存在,显示文件信息"
	ls -l $file
else
	echo "文件不存在,创建文件"
	touch $file
fi

if语句的嵌套
if cmd
then
………
else
if cmd
then
……….
else
…………
fi
fi

提示输入文件路径及文件名,判断改文件是否存在,
判断改文件是否size为0
如果不为0 显示其详细信息,
如果为0 删除改文件 重新创建并写入内容hello world

#!/bin/bash

read -p "输入文件信息:" filename
if [ -f $filename ]
then
        echo "${filename}存在"
        if [ -s $filename ]   #判断大小是否大于0,大于0时返>回0
        then
                ls -l $filename
        else
                rm -f $filename
                touch $filename
                echo "hello world" > $filename
        fi
else
        echo "${filename}不存在"
fi

if语句的完整写法
if cmd1
then
run cmd1-1
run cmd1-2
elif cmd2
then
run cmd2-1
run cmd2-2
elif cmd3
then
run cmd3-1
run cmd3-2
else
then
run cmd4-1
run cmd4-2
fi

100数字内猜数字游戏
\#/bin/bash
guess=80
read -p "please insert yao number(range 1-100): " num
if [ $num -eq $guess ]
then
    echo "you are win!!!!"
else
    echo "you are lose!!!!"
fi

如果是猜随机数怎么办?($RANDOM).
正常情况应该为产生一个随机数,猜数人员有5次机会.这就需要用到循环语句
那么循环语句的结构式怎样的呢?带着上边的问题我们先学习一下循环语句
循环语句主要有两个循环语句for和while

2.练习和作业
1.判断tmp下是否存在普通文件aa.txt,文件存在则输出文件详细信息,文件不存在则创建文件
#!/bin/bash
file=/tmp/aa.txt
if [ -f $file ]
then
        ls -l $file
else
        touch $file
fi

2.写脚本判断脚本后边的变量个数是否超过2个 不足提示变量不足,超过提示超过
#!/bin/bash
sum=$#
if [ ${sum} -gt 2 ]
then
        echo "变量超过"
elif [ $sum -lt 2 ]
then
        echo "变量不足"
fi

3.提示用户输入一个变量值 判断输入的值是否为空
#!/bin/bash
read -p "输入一个变量值:"  num
if [ -n "$num" ]
then
        echo "不为空"
else
        echo "为空"
fi

4.从系统中搜索文件man_db.config是否存在,判断该文件和/root/install.log是否为硬链接
#!/bin/bash
file=`find / -name man_db.conf`
if [ $file -ef /root/install.log ]
then
        echo "yes"
else
        echo "no"
fi

5.从系统搜索光盘镜像文件,如果有挂载使用 否则提示下载
#!/bin/bash
if [ -L /dev/cdrom ]
then
        echo "存在"
        mount /dev/cdrom /mnt
else
        echo "不存在,请前往下载!"
fi


6.写一个文本文件/tmp/user.txt 内容为
帐号
密码
写一个交互式脚本,让用户输入帐号 密码,判断用户输入帐号密码是否正确,如果正确提示登录成功
touch /tmp/user.txt
echo "zhangsan" > /tmp/user.txt
echo "123456" >> /tmp/user.txt

#!/bin/bash
user=`head -1 /tmp/user.txt`
pass=`tail -1 /tmp/user.txt`
read -p "input username:" account
read -p "input password:" password
if [ $user == $account -a $pass == $password ]
then
        echo "登陆成功"
else
        echo "失败"
fi


7.写一个判断vsftpd是否正在运行的脚本
#!/bin/bash

systemctl status vsftpd
ftp=$?
if [ $ftp -eq 0 ]
then
        echo "有ftp进程"
else
        echo "没有ftp进程"
fi



8.写一个判断内存使用是否大于50%的脚本
#!/bin/bash
use=`free | head -2 | tail  -1 | cut -d' ' -f16`        #awk命令更好
free=`free | head -2 | tail  -1 | cut -d' ' -f23`
if [ $use -gt $free ]
then
        echo "内存使用大于50%"
else
        echo "no"
fi


9.写一个检查系统中登录用户超过5个的脚本 uptime 或者 who
#!/bin/bash
usernum=`who | wc -l`
if [ $usernum -gt 5 ]
then
        echo "over"
else
        echo "no over"
fi


10.完善你写过的yum库脚本
11.判断nmap命令是否存在 如果不存在则安装对应的软件包
#!/bin/bash
rpm -q nmap
nmap=$?
if [ $nmap -eq 0 ]
then
        echo "nmap命令存在"
else
        echo "nmap命令不存在"
        yum -y install nmap
fi


12.查看光盘上有哪些软件没有安装,将没有安装的软件包,安装到系统中 *
13.如何判断一个目录为空目录
#!/bin/bash
read -p "input a dir:" dir
num=`ls -A $dir`
if [ -z "$num" ]
then
        echo "null"
else
        echo "no null"
fi

查看咱们的网络内有多少ip地址是活跃的,并且那些ip的ssh服务是开启的?



作业:
1.判断当前用户是否为root 如果为root用户启动ssh服务 如果非root
切换用户提示用户启动服务
#!/bin/bash
if [ $USER = "root" ]
then
        echo "yes"
        systemctl start sshd
else
        echo "please cut user,use sshd"
fi


2.每隔3秒调用自己一次

3.分析下边脚本  
a.sh
#!/bin/bash
echo $$
./a.sh | ./a.sh &

4.判断自己是否为重复运行脚本,如果为重复运行的脚本则自动退出(同一
时间该脚本只有一个实例运行)

四、循环语法

首先学习一下for语句
如下脚本:
#!/bin/bash
for i in 1 3 5 7
do
     echo $i
     echo ok
done 

我们还可以将for循环读取的语句写到一个文件里如a.txt
#!/bin/bash
for I in `cat a.txt`
do
echo $i
echo ok
done

添加100个用户
#!/bin/bash

for i in {1..100}
do
        useradd zhang"$i"
done

time 命令的使用
real墙上时间,也就是实际消耗时间多少
user用户态消耗的时间.
sys 系统底层消耗的时间(操作硬盘)
 
计算1-100的累加和(注意初始化值)
#!/bin/bash
sum=0
for i in {1..100}
do
        let sum=$i+$sum
done
        echo $sum
计算1-100奇数的累加和 偶数呢?
#!/bin/bash
sum1=0
sum2=0
for i in {1..100}
do
        if [ $(($i%2)) -eq 1 ]
        then
                let sum1=$i+$sum1
        else
                sum2=$(($i+$sum2))
        fi
done
        echo "偶数和:$sum2"
        echo "奇数和:$sum1"


更好的方式:
seq 1 2 100 产生1-100个数字 步长为2 脚本就可以变得更简单了
 
for循环的另一种写法 模拟c语言的写法 
for ((i=0;i<10;i++))
do
     echo $i
done


i+=2  i=i+2
i-=2  i=i-2
i*=2 i=i*2
i/=2 i=1/2


数值运算时这种写法更简单 如果文件处理for in的语法更容易写
 
好了 我们开始完成上边遗留的问题
猜数字给5次机会
#!/bin/bash
number=$(($RANDOM%100+1))
for i in {1..5}
do

        read -p "输入数字[1~100]:" guess
        if [ $guess -eq $number ]
        then
                echo "you are win"
        elif [ $guess -gt $number ]
        then
                echo "you are big"
        else
                echo "you are small"
        fi
done
又遇到问题.猜对了的情况下还让猜 这时应该跳出脚本 不在继续猜
 
break 和continue 跳出循环
break 跳出当前循环 脚本继续执行
continue 跳出本次循环,脚本继续执行
exit 退出脚本, 但是exit可以设置脚本返回值


遇到某些条件时这一次的循环跳出continue (如再来一个小游戏:大家说数字1-100 遇到被7整除和含有的7的数就跳出)



 
我们再来看看其他循环如select循环
select i in ls pwd whoami
do
     $i
done
 
while和until循环
while 后边跟命令 条件为真值时循环
until 后边跟命令 条件为假值时循环
更多用while做死循环
语法
while cmd
do
list
done
 
 
如 true为真值 0
while true
do
     sleep
     echo ok
done
 
:空指令
死循环
while true
do
     :
done
 
不会死机 cpu发现为死循环 降低该进程优先级 
#!/bin/bash
#14.sh
x=0
while [ $x -lt 10 ]
do
echo $x
x=`expr $x + 1`
done
 
 
#!/bin/bash
#15.sh
sum=0
while [ $sum -lt 10 ]
do
sum=`expr $sum + 1`
useradd user$sum
echo "123456" | passwd --stdin user$sum
done
 
 
until语法
until cmd
do
list
done
 
#!/bin/bash
#16.sh
x=1
until [ $x -ge 10 ]
do
echo $x
x=`expr $x + 1`
done
相当于
x=1
while [ ! $x -ge 10 ]
do
echo $x
x=`expr $x + 1`
done
 

当用户robin 登录系统时 提示robin登录系统,记录用户信息(名字 时间  tty)

五、case分支语法及函数

1. case语法结构
case word in
pattern1)
	list1
;;
pattern2)
	list2
;;
... ...
patternN)
	listN
;;

* )
  list *
  ;;
  esac

例如:超市卖水果
#!/bin/bash
read –p “请输入你要查询的商品: ”var
case $var in
apple)
  echo "apple 1.4元每斤"
;;
orange)
  echo "orage 1.5元每斤"
;;
banana)
  echo "banana 1.6元每斤"
::
esac

/etc/init.d/sshd

用法:/etc/init.d/sshd {start|stop|restart|reload|condrestart|status}
服务用法的实现

#!/bin/bash
case $1 in
start)
  echo "start"
;;
stop)
  echo "stop"
;;
restart|reload)
  echo "restart"
;;
*)
  echo "Usage: $0 start|stop|restart"
esca

在这个基础上我们来实现一个小服务的脚本
nc 命令可以监听段口
nc –l 9999
好了,我们可以启动一个小服务了

#!/bin/bash
case $1 in
start)
  echo "start"
  nc –l 9999
;;
stop)
  echo "stop"
  pkill nc
;;
restart|reload)
  echo "restart"
  pkill nc
  nc –l 9999
;;
*)
  echo "Usage: $0 start|stop|restart"
esca

pidof取一个进程的pid

完善一些这个模拟服务的脚本,定义函数

#!/bin/bash
start(){                                    #定义start函数
    if [ -f /tmp/nc.lock ]
    then
        echo "nc is runing"
    else
        echo "start"
        nc -l 9999 &
        touch /tmp/nc.lock
    fi
}
stop(){                                     #定义stop函数
    if [ ! -f /tmp/nc.lock ]
    then
        echo "nc is not runing"
    else
        echo "stop"
        PID=$(pidof nc)
        kill -9 $PID
        rm -rf /tmp/nc.lock
    fi
}
case $1 in
start)
    start                                   #调用函数
;;
stop)
    stop
;;
restart)
    stop
    sleep 1
    start
;;
*)
    echo "Usage:$0 start|stop|restart"
esac
2.函数
函数也可以让我们死机 如下
:(){ :|:& };:

可以看我们的系统服务启动脚本了

函数的参数
函数外的参数也能被函数调用
#!/bin/bash
a=123
func(){
  echo $a
}

func

./a.sh
变量在函数内 外边也能调用
#!/bin/bash
func(){
     a=123
}

func
echo $a

只在函数里调用 外部看不到 隔离变量
#!/bin/bash
func(){
local a=123
echo "in func"
echo $a
}

func
echo "out of func"
echo $a

给函数传递参数
#!/bin/bash
sum(){
echo $(($1+$2))
}
sum 10 20

 

如果命令参数传递给函数
#!/bin/bash
a=$1
b=$2
sum(){
echo $(($1+$2))
}

sum $a $b
执行a.sh 10 20

变量起名字别偷懒 起的有意义一些 让别人一目了然 如:argv1 argv2

#!/bin/bash
#注明 $1 $2 为脚本参数 以免脚本过长不知道那个$1 $2
argv=$1
argv=$2
sum(){
# $1 $2 为函数参数
func_argv1=$1
func_argv2=$2
echo $(($func_argv1+$func_argv2))
} 
sum $a $b

练习脚本:
提取主机名 函数
提取ip地址 函数 (多网卡)
检查自己主机启动什么服务的 函数
3. 练习
1.添加user1-user50个用户.再添加过程中.如果这50个用户中有已存在的用户则显示The user is in system!!!!!如果不存在则添加,并且添加密码 
能不能让上边的脚本加快执行速度?
#!/bin/bash
for i in {1..50}
do
        useradd user"$i" &> /dev/null
        if [ $? -ne 0 ]
        then
                echo "The user is in system!!!!!"
        else
                echo 123 | passwd --stdin user"$i" &> /dev/null
        fi
done

2.打印一下矩阵
****
****
****
#!/bin/bash
for ((i=0;i<4;i++))
do
        for ((j=0;j<4;j++))
        do
                echo -n "* "
        done
        echo
done

3.打印以下3角型
* 
**
***
****
#!/bin/bash
for ((i=0;i<4;i++))
do
        for ((j=0;j<=i;j++))
        do
                echo -n "* "
        done
        echo
done

4.按用户输入数字打印一下三角型 如输入行数  n-i个空格 2i-1个*
   *
  ***
 *****
*******
#!/bin/bash
for ((i=1;i<=4;i++))
do
        for ((m=1;m<=4-i;m++))
        do
                echo -n "  "
        done
        for ((j=1;j<=2*i-1;j++))
        do
                echo -n "* "
        done
        echo
done

掏空
#!/bin/bash
for ((i=1;i<=4;i++))
do
        for ((m=1;m<=4-i;m++))
        do
                echo -n "  "
        done
        if [ $i -eq 1 -o $i -eq 4 ]
        then
                for ((j=1;j<=2*i-1;j++))
                do
                        echo -n "* "
                done
                echo
        else
                for ((j=1;j<=2*i-1;j++))
                do
                if [ $j -eq 1 -o $j -eq $((2*$i-1)) ]
                then
                        echo -n "* "
                else
                        echo -n "  "
                fi
                done
                echo
        fi
done


倒三角
#!/bin/bash
for ((i=4;i>=1;i--))
do
        for ((m=1;m<=4-i;m++))
        do
                echo -n "  "
        done
        for ((j=1;j<=2*i-1;j++))
        do
                echo -n "* "
        done
        echo
done


5. 按用户输入数字打印一下三角型 如输入4
   *
  ***
 *****
*******
 *****
  ***
   *
梯形
圣诞树
6.打印9x9乘法表如下格式
1x1=1
1x2=2 2x2=4
……………………………
…………………………………..
……………………………………….
…………………………………………………….9x9=81

#!/bin/bash
for ((i=1;i<=9;i++))
do
        for ((j=1;j<=i;j++))
        do
                num=$(($i*$j))
                echo -n "${i}x${j}=$num  "
        done
        echo
done

7.计算12345经过加减乘除等于15的式子
#!/bin/bash

for i in + - \* /
do
        for j in {"+","-","*","/"}
        do
                for m in {"+","-","*","/"}
                do
                        for n in {"+","-","*","/"}
                        do
                                num=$((1${i}2${j}3${m}4${n}5))
                                if [ $num -eq 15 ]
                                then
                                        echo "1${i}2${j}3${m}4${n}5"
                                fi
                        done
                done
        done
done
+

8.写一个脚本监控你的/分区,当你的/分区的剩余空间小于10G时.给root管理员发一封邮件(测试时可以再某个文件里写一句话)
#!/bin/bash

while true
do
free=`df -T | grep /boot | awk {'print $5'}`
if [ $free -lt 512000 ]
then
        echo "/boot不足500M,剩余$free"
        echo "/boot不足500M,剩余$free" | mail -s 磁盘 root@localhost
fi
done


9.测试网络中有多少个ip是活跃,并且查询那些80端口开放
10.判断自己是否为重复运行脚本,如果为重复运行的脚本则自动退出(同一时间该脚本只有一个实例运行)
11.mysql的备份脚本(mysqldump  +  binlog日志)

六、变量替换及数组

变量替换(可以用if实现 这是另外一种方式)

${parameter:-word}
若 parameter 为空或未设置,则用 word 代替 parameter 进行替换,parameter 的值不变
\# a=1
\# unset b
\# a=${b:-3}
\# echo $a

\# a=1
\# b=2
\# a=${b:-3}
\# echo $a
\# echo $b${parameter:=word}
若 parameter 为空或未设置,则 parameter 设为值 word
\# a=1
\# unset b
\# a=${b:=3}
\# echo $a
\#echo $b

\# a=1
\# b=2
\# a=${b:=3}
\# echo $a
\# echo $b${parameter:+word}
若 parameter 设置了,则用 word 代替 parameter 进行替换,parameter 的值不变
\# a=1
\# unset b
\# a=${b:+3}
\# echo $a
\# echo $b
\#
\# a=1
\# b=2
\# a=${b:+3}
\# echo $a
\# echo $b${parameter:?message}
若 parameter 为空或未设置,则 message 作为标准错误打印出来,这可用来检查变量是否正确设置
\# unset a
\# ${a:?unset a}
-bash: a: unset a


字符串切片,替换
$ a=12345678
$ echo ${a:5}
678
$ echo ${a:3:4}
4567
$ a=123456123789
$ echo ${a#1*3}     #最短匹配截取
456123789
$ echo ${a##1*3}    #最长匹配截取
789
$ a=123
$ echo ${#a}         #表示$var的长度
3
$ a=123456123789
$ echo ${a/1/}     #第一次匹配的被替换(去掉)  
23456123789
$ echo ${a//1/}     #全局的匹配被替换
2345623789
$ echo ${a/1/x}
x23456123789
$ echo ${a//1/x}
x23456x23789

shell的数组分为普通数组和关联数组
普通数组
定义数组
[root@manager ~]# ary=(a b c)
数组取值
[root@manager ~]# echo ${ary[0]}
a
[root@manager ~]# echo ${ary[1]}
b
[root@manager ~]# echo ${ary[2]}
c
[root@manager ~]# echo ${ary[3]}

[root@manager ~]# echo $ary
a

或者设置数组的值为字符串
[root@manager ~]# ary=("robin" "zorro" "lucy")
[root@manager ~]# echo ${ary[0]}
robin
[root@manager ~]# echo ${ary[1]}
zorro
[root@manager ~]# echo ${ary[2]}
lucy

取数组所有值:
[root@manager ~]# ary=("robin" "zorro" "lucy")
[root@manager ~]# echo ${ary[@]}
robin zorro lucy
或者
[root@manager ~]# echo ${ary[*]}
robin zorro lucy

数组的重新赋值
[root@manager ~]# ary[0]="jerry"
[root@manager ~]# echo ${ary[0]}
jerry

删除数组赋值
[root@manager ~]# unset ary[0]
[root@manager ~]# echo ${ary[0]}

删除数组
[root@manager ~]# unset ary
[root@manager ~]# echo ${ary[@]}

统计数组成员个数
[root@manager ~]# ary=("robin" "zorro" "lucy")
[root@manager ~]# echo ${#ary[@]}
3

统计数组成员字符个数 
[root@manager ~]# ary=("robin" "zorro" "lucy")
[root@manager ~]# echo ${#ary[1]}
5
[root@manager ~]# echo ${#ary[2]}
4

数组切片
[root@manager ~]# ary=("robin" "zorro" "lucy")
[root@manager ~]# ary=("robin" "zorro" "lucy" "tom" "jerry")
[root@manager ~]# echo ${ary[@]:1:2}
zorro lucy
[root@manager ~]# echo ${ary[@]:1:3}
zorro lucy tom

数组成员切片
[root@manager ~]# echo ${ary[0]:1:2}
ob
[root@manager ~]# echo ${ary[0]:1:3}
obi
[root@manager ~]# echo ${ary[0]:1:4}
obin
[root@manager ~]# echo ${ary[1]:2:2}
rr

遍历数组所有的值
[root@manager ~]# for i in ${ary[@]}; do echo $i; done
a
b
c



关联数组
Bash支持关联数组,它可以使用字符串作为数组索引,有时候采用字符串索引更容易理解。

1.利用内嵌索引-值列表的方法
[root@manager ~]# declare -A ary
[root@manager ~]# ary=([robin]=beijing [zorro]=shanghai)
[root@manager ~]# echo ${ary[robin]}
beijing
[root@manager ~]# echo ${ary[zorro]}
shanghai

2.使用独立的索引-值进行赋值
[root@manager ~]# ary[jack]=hebei
[root@manager ~]# ary[rose]=henan
[root@manager ~]# echo ${ary[jack]}
hebei
[root@manager ~]# echo ${ary[rose]}
henan

取数组值
[root@manager ~]# echo ${ary[*]}
shanghai beijing
取数组的键
[root@manager ~]# echo ${!ary[*]}
zorro robin

获取所有键值对
[root@manager ~]# for key in ${!ary[@]}
do
	echo "$key = ${ary[$key]}"
done
zorro = shanghai
robin = beijing


练习:
排序 数字

134  223 45 98 76 243 8 6  1 47



石头剪刀布游戏

#!/bin/bash

count1=0
count2=0
while true
do
guess=("石头" "剪刀" "布")
num1=$(($RANDOM%3))
computer=${guess[$num1]}

menu="
0.石头
1.剪刀
2.布
请输入[0][1][2]:"
read -p "$menu" num2
player=${guess[$num2]}

declare -A win_case
win_case=(["石头"]="剪刀" ["剪刀"]="布" ["布"]="石头")

if [ ${win_case["$player"]} == "$computer" ]
then
        echo "player is win"
        count1=$(($count1+1))
elif [ ${win_case["$computer"]} == "$player" ]
then
        echo "computer is win"
        count2=$(($count2+1))

else
        echo "pingju"
fi

echo "$computer"
echo "$player"
done

七、ssh远程操作及终端控制

1. tput命令
tput 可以操作光标,定位光标
tput sc 保存光标位置
tput rc 返回光标位置
tput cols  读取列数
tput lines 读取行数
tput cup lines cols
tput civis # 光标不可见 
tput cnorm # 光标可见

定位倒计时
\#!/bin/bash
col=`tput cols`
line=`tput lines`
ncol=$(($col/2))
nline=$(($line/2))
tput sc                          爆炸
for i in {1..1000}
do
	usleep $i
	tput cup $nline $ncol
	echo "$i"
done
tput cup $nline $ncol
echo "爆炸"
for i in {1..10}
do
	usleep 100000
	echo -e "\a"
	
done
tput rc


2. ssh远程脚本
expect非交互式
脚本代码如下: 
#!/usr/bin/expect 
set timeout 30 
spawn ssh -l username 192.168.1.1 
expect "password:" 
send "ispass\r" =
interact 

  1. [#!/usr/bin/expect]
    这一行告诉操作系统脚本里的代码使用那一个shell来执行。这里的expect其实和linux下的bash、windows下的cmd是一类东西。
    注意:这一行需要在脚本的第一行。

  2. [set timeout 30]
    基本上认识英文的都知道这是设置超时时间的,现在你只要记住他的计时单位是:秒

  3. [spawn ssh -l username 192.168.1.1]
    spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。所以不要用 “which spawn“之类的命令去找spawn命令。好比windows里的dir就是一个内部命令,这个命令由shell自带,你无法找到一个dir.com 或 dir.exe 的可执行文件。
    它主要的功能是给ssh运行进程加个壳,用来传递交互指令。

  4. [expect “password:”]
    这里的expect也是expect的一个内部命令,有点晕吧,expect的shell命令和内部命令是一样的,但不是一个功能,习惯就好了。这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒

  5. [send “ispass\r”]
    这里就是执行交互动作,与手工输入密码的动作等效。
    温馨提示: 命令字符串结尾别忘记加上 “\r”,如果出现异常等待的状态可以核查一下。

  6. [interact]
    执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行一段命令就退出,可改为[expect eof]

shell脚本嵌套expect脚本
cat ssh.sh
#!/bin/bash
pass=123
/usr/bin/expect <<EOF
set timeout 30
spawn scp /root/install.log 192.168.1.1:/home
expect "password:"
send "$pass\r"
expect eof
EOF            #必须顶行

添加用户natasha  密码redhat

终端控制
#!/bin/bash
gnome-terminal --geometry=34x8+0+0 -e /tmp/aa.sh
gnome-terminal --geometry=34x8+700+0 -e /tmp/aa.sh
gnome-terminal --geometry=34x8+0+700 -e /tmp/aa.sh
gnome-terminal --geometry=34x8+700+700 -e /tmp/aa.sh

八、正则表达式

1. 正则对照表
find  grep  sed   awk  vim 
python php 

正则表达式
算术表达式:
1+2 3*5 1+2*3 (1+2)*3
正则表达式在匹配的时候一定要有一定规律,否则无法匹配 如下
特定的模式:
A B C … … -> a b c … …
ABC ADC AEC … …
ab abb abbb abbbb abbbb… …
正则表达式的匹配过程

如:

grep halt /etc/passwd
那我们通过grep来学习正则吧
grep命令是linux下的行过滤工具,其参数繁多, 
grep -- print lines matching a pattern (将符合样式的该行列出)
◎语法: grep [options] 
PATTERN [FILE...] 
grep用以在file内文中比对相对应的部分,或是当没有指定档案时, 
由标准输入中去比对。 在预设的情况下,grep会将符合样式的那一行列出。
其中egrep就等同于grep -E 

常用参数
-A  匹配行的后多少行
-B  匹配行的前多少行
-C  前后行
-c  统计匹配行
-i  忽略大小写匹配
-E  扩展正则
-n  显示匹配行行号
-x  显示完全匹配的行
-v  取反

如下文件 a.txt
我要找到含有ABC ADC AEC 的行
asdfasdf ABC aasdajsd
afdafjal ADC qqweqeqwe
qweqe AEC adfajf

grep A.C a.txt
这里的.称为元字符
元字符
. 匹配除换行符之外的任意单个字符,awk中可以匹配换行符
* 匹配任意一个(包括零个)在它前面的字符
如下
a
ab
ac
abb
abc
abbc
abbbbbbbbbbbc

grep ab* b.txt
grep ab*c b.txt
grep a* b.txt

.*任意一个字符出现一次到多次
asadfsadfc
ac
afdsdfasdfasdfasdfc
aacc
aaaaaa1ccccccccc2aaaaaaaaaa3ccccccccc4

grep a.*c c.txt

[...] 匹配方括号中的任意一个字符,^为否定匹配, -表示字符的范围
acc
a.c
abc
a2c
a6c
a7c
a8c

grep [6789]c a.txt
grep [1-9]c a.txt
grep [^1-9]c a.txt

1+2
1-2
1*2
1/2
grep[+-*/]’ a.txt   #必须将-放在开头或者结尾

^和$
root
aroot
roota

grep ^root a.txt
grep root$ a.txt
grep ^root$ a.txt
只包含3个字符?    ^...$
最少包3个字符?    ...
匹配空行?显示行号?  grep -n ^$
多个空格的空行?     grep -n "^ *$"
grep "\t"是不行的,应该输入一个正在的制表符,方法为先按 CTRL+V,再按 Tab 键。

\ 转意字符
abc
a,c

grep a\,c a.txt

扩展元字符
+ 匹配前面的正则表达式的一次出现或多次出现

a
ab
abb
abbb

grep ab+ a.txt

? 前边字符出现0或1次 可能有可能没有
y
yes
Y
Yes

grep -E [Yy][es]? a.txt

| 替代方案l
company
companies

grep -E compan'[y|iess]' a.txt
grep -E compan’(y|iess)’a.txt

{n,m}匹配出现的n到m次数, {n}匹配出现n次。{n,}匹配至少出现n次,
大多数awk都不支持,用于POSIX egrep和POSIX awk
aaaa    4
aaaaa   5
aaaaaa  6
aaaaaaa 7

grep -E a\{4,5\} a.txt

字符类
[Ww]hat
\.H[12345]
字符的范围
[a-z]
[0-9]
[Cc]hapter[1-9]
[-+*/]
[0-1][0-9][-/][0-3][0-9][-/][0-9][0-9]
[root@manager test5]# grep -E "(15:[0-2][0-9]|15:4[0-5]:)" /var/log/secure
排除字符类
[^0-9]

重复出现的字符
5
10
50
100
500
1000
5000
[15]0*
[15]00*
字符的跨度
\* 与 \{n,m\}
电话号码的匹配
[0-9]\{3,4\}-[0-9]\{7,8\}
分组操作
compan(y|ies)




sed和awk 
sed和awk用于处理文本,在linux中大量操作都涉及到文本,如系统日志,应用程序日志.批量处理时对配置文件做修改.操作文件时有些文件的语法不规范,如messages有空格分隔 :分隔 分号分隔 ,号分隔
如apache日志,分隔符不明显,而且分割时都是特殊字符,提取时我们可能只要ip 访问目标地址 访问时间 还有格式修改,sed主要处理不规范的格式改为规范.
sed是一个“非交互式的”面向字符流的编辑器,awk是一种负责模式匹配的程序设计语言,它的典型示例是将数据转换成格式化的报表。
sed流媒体编辑器的使用

练习文本:
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA
(名字 街道 城市 大州)

dos2unix 文件转换
unix2dos 
在windows和linux之间文本格式互转的工具
linux \n
windows \n\r 换行

语法
sed ‘args’ file.txt file2.txt 但是官方文档称args为命令
sed ‘cmd’file.txt file.txt
替换:
sed ‘s/old/new/’ file1.txt 结果显示在屏幕上云文件不变
sed 's/MA/Massachusett/' file1.txt  
格式调整
sed 's/ MA/, Massachusetts/' file1.txt
多条处理
sed 's/ MA/, Massachusetts/ ; s/ PA/, Pennsylvania/' file1.txt
或者
sed -e 's/ MA/, Massachusetts/' -e 's/ PA/, Pennsylvania/' file1.txt
脚本支持
脚本:sedsrc
s/ MA/, Massachusetts/
s/ PA/, Pennsylvania/
s/ CA/, California/
s/ VA/, Virginia/
s/ OK/, Oklahoma/

sed -f sedsrc file1.txt
保存输出
sed -f sedsrc file1.txt > newfile.txt

阻止输入行自动显示
sed -n 's/MA/Massachusetts/p' file1.txt



awk的简单使用
语法
awk{主输入循环}’file
输出每个人的名字
awk{print $1}’file   #$0代表所有
第二列 默认分隔符(一个空格 多个空格 一个制表符 多个制表符) 所以,显示
awk{print $2}’file
指定分隔符
awk –F, ‘{ print$1 }’file1.txt
//匹配功能如
匹配包含MA的行
awk ‘/MA/’file1.txt
还想要用户名字
awk ‘/MA/{print $1}’file1.txt
取root用户的pid
awk –F: ‘/root/{print $1 $3}’/etc/passwd
匹配字符串 $1~表示匹配第一个字段 
awk –F: ‘$1~/root/{print $3 $1}’/etc/passwd

找到所有uid并求出和

练习:
ps aux  rss总和?

awk取ip地址 ?
ifconfig eth2 | awk -F':| +' '/Bcast/{print $4}'




2. 练习
正则表达式及字符处理
1.使用grep显示出/usr/share/dict/words文件中,例如显示出所有含有fish的行:
[root@mycat ~]# grep fish /usr/share/dict/words 

2.使用grep显示出/usr/share/dict/words文件中,输出任何包含fish的所有行,还要输出紧接着这行的上下各两行的内容:
[root@mycat ~]# grep -C 2 fish /usr/share/dict/words 


3. 使用grep显示出/usr/share/dict/words文件中,来显示出在words文件中有多少行含有fish。
[root@manager test5]# grep -c  fish /usr/share/dict/words
[root@manager test5]# grep fish /usr/share/dict/words | wc -l


4.使用grep显示出/usr/share/dict/words文件中,显示出那些行含有fish,并将行号一块输出,看一看starfish在哪行
[root@manager test5]# grep -n  fish /usr/share/dict/words

[root@manager test5]# grep -xn  starfish /usr/share/dict/words

5.想列出/usr/share/dict/words中包含先有字母t然后有一个元音字母,之后是sh的单词,命令为
[root@manager test5]# grep -x t[aeiou]sh /usr/share/dict/words

6.在/usr/share/dict/words文件中,创建可以符合abominable,abominate,anomie和atomize的正则表达式,但是不要选到别的单词
[root@manager test5]# grep -xE '(abominable|abominate|anomie|atomize)' /usr/share/dict/words
[root@manager test5]# grep -xE 'a[bnt]omi(nabl|nat|z)?e' /usr/share/dict/words
[root@manager test5]# grep -xE 'a[bnt]omi(na(bl|t)|z)?e' /usr/share/dict/words


7.在/usr/share/dict/words文件中包含多少先有字母t然后有一个元音字母,之后是sh的单词,只输出数量
grep -c t[aeiou]sh /usr/share/dict/words 

8.列出/usr/share/dict/words中刚好包含16个字母的单词 
[root@manager test5]# grep -xE '[a-zA-Z_]{16}' /usr/share/dict/words 
[root@manager test5]# grep -xE '\w{16}' /usr/share/dict/words 

9.我们将要使用/usr/share/doc文件夹来完成
列出/usr/share/doc/bash-* 文件夹中,所有包含单词expansion的文件
[root@manager test5]# grep -rl "expansion" /usr/share/doc/

10. 从message里面取出10:00:00-11:20:50这个区间的日志

九、sed使用

1.sed基础
元字符
. 匹配除换行符之外的任意单个字符,awk中可以匹配换行符
例:
abc adc aec
a.c

*匹配0到多个前边字符
a
ab
ac
abb
abc
abbc
abbbbbbbbbbbc
sed 's/ab*/x/' a.txt  匹配a开头b出现0到多次

asdfsdkjfhskc
ac
asdfjslkfjslfjslfjsfljc
a23113131c
aacc
aaaaaaa1ccccccccc2aaaaaaaaaaa3c4
sed 's/a.*c/x/' a.txt 匹配a0-多个c结尾的字段 *的贪婪性尽可能长的c

[...] 匹配方括号中的任意一个字符,^为否定匹配, -表示字符的范围
acc
a.c
abc
a2c
a6c
a7c
a8c

sed 's/a.c/x/' a.txt
sed 's/a[67c89]c/x/' a.txt
sed 's/a[1-9]c/x/' a.txt
sed 's/a[^1-9]c/x/' a.txt

1+2
1-2
1*2
1/2

sed 's/1[+-*/]2/x/' a.txt -号报错
sed 's/1[-+*/]2/x/' a.txt
^ 作为正则表达式的第一个字符,匹配行的开始。在awk中可以嵌入
换行符
$ 作为正则表达式的最后一个字符,匹配行的结尾。在awk中可以嵌
入换行符
root
aroot
roota

sed 's/root/x/' a.txt
sed 's/^root/x/' a.txt
sed 's/root$/x/' a.txt
sed 's/^root$/x/' a.txt
sed 's/^...$/x/' a.txt 只包含3个字符
sed 's/.../x/' a.txt 最少包含3个字符
sed 's/^$/x/' a.txt  sed不能匹配换行符 匹配空行
sed 's/^ *$/x/' a.txt 多个空格的行

\{n,m\} 匹配出现的n到m次数, \{n\}匹配出现n次。\{n,\}匹配至少出现
n次(看扩展)
\ 转义字符
如
a/b
sed 's/a\/b/x\/y/' tt.txt
我们可以替换分隔符
sed 's@a/b@x/y@' tt.txt
sed 's;a/b;x/y;' tt.txt
任意的3个字符 都可以 避免产生冲突
date +%Y/%m/%d | sed ‘s@/@:@g’  g全局替换

扩展元字符

+ 匹配前面的正则表达式的一次出现或多次出现
  a
  ab
  abb
  abbb

sed 's/abb*/x/' a.txt
sed -r 's/ab+/x/' a.txt
? 匹配前面的正则表达式的零次出现或一次出现
y
yes
Y
Yes

sed -r 's/[yY](es)?/x/' a.txt
| 可以匹配前面的或后面的正则表达式(替代方案)
() 替换方案 对正则表达式分组 
company
companies
compan(y|ies)
[abc] (a|b|c) 某些情况下一样 还有分组功能后边会用到
{n,m} 匹配出现的n到m次数, {n}匹配出现n次。{n,}匹配至少出现n次
sed -r 's/a/!/' a.txt  只替换第一次匹配到的
sed -r 's/a{4}/!/' a.txt
sed -r 's/a{4,6}/!/' a.txt
sed -r 's/a{,4}/!/' a.txt
sed -r 's/a{5,}/!/' a.txt

匹配一个字符出现0-1次  基本正则也可以实现
.\{,1\}

sed ‘s/元字符在这里起作用匹配/+-|则不再起元字符作用/’

编写sed脚本 要求如下
原始 完成后
apple banana
banana orange
一条命令完成
是不是我们想的结果?

为什么产生上边的问题:
首先sed在运行中会维护一段内存空间,这段内存空间称为模式空间:
模式空间存储被替换的文件,默认是按行为单位
如上文件读取过程
首先进入模式空间s-----apple 替换为banana
在执行s-----banana 替换为orange
结果apple被替换成orange 没有-n参数 输出终端上
第二条进入模式空间 第一条命令匹配不上,第二条匹配 banana
被替换为orange

寻址上的全局透视(定址)
sed将命令应用于每个输入行,它可以指定零个、一个或两个地址。每个地址
都是一个描述模式、行号或者行寻址符号的正则表达式。(默认全文寻址)
范例file2.txt

.TS
Beijing,CN
.TE
Shanghai,CN

guangzhou,CN
shenyang,CN

sed ‘2s/CN/China/’file2.txt   #行号定值
sed '/Beijing/s/CN/China/' file2.txt  #字符定值
sed$s/CN/China/’file2.txt
sed ‘/^\.TS/,/^\.TE/s/CN/China/’file2.txt  #起始,结束定值
sed '4,6s/CN/China/' file2.txt
sed '4,$s/CN/China/' file2.txt
sed '4,/guangzhou/s/CN/China/' file2.txt
删除命令d

删除所有的行
d
只删除第一行
1d
使用寻址符号$,删除最后一行
$d
删除空行,正则表达式必须封闭在斜杠//当中
/^$/d
删除.TS 和.TE 标记的tbl 输入
/^\.TS/,/^\.TE/d
删除第五行到结尾所有的行
5,$d
混合使用行地址和模式地址
$ sed '1,/^$/d' file2.txt
删除除了那些行以外的行
1,5!d

分组命令
.TS
Beijing,CN
.TE
Shanghai,CN

guangzhou,CN
shenyang,CN

sed ‘/^\.TS/,/^\.TE/s/,/:/ ; /^\.TS/,/^\.TE/s/CN/China/ file2.txt
使用分组
sed '/^\.TS/,/^\.TE/{ s/,/:/ ; s/CN/china/ }' file2.txt

练习:
文件:datafile
Steve Blenheim:238-923-7366:95 Latham Lane, Easton, PA 83755:11/12/56:20300
Betty Boop:245-836-8357:635 Cutesy Lane, Hollywood, CA 91464:6/23/23:14500
Igor Chevsky:385-375-8395:3567 Populus Place, Caldwell, NJ 23875:6/18/68:23400
Norma Corder:397-857-2735:74 Pine Street, Dearborn, MI 23874:3/28/45:245700
Jennifer Cowan:548-834-2348:583 Laurel Ave., Kingsville, TX 83745:10/1/35:58900
Jon DeLoach:408-253-3122:123 Park St., San Jose, CA 04086:7/25/53:85100
Karen Evich:284-758-2857:23 Edgecliff Place, Lincoln, NB 92086:7/25/53:85100
Karen Evich:284-758-2867:23 Edgecliff Place, Lincoln, NB 92743:11/3/35:58200
Karen Evich:284-758-2867:23 Edgecliff Place, Lincoln, NB 92743:11/3/35:58200
Fred Fardbarkle:674-843-1385:20 Parak Lane, DeLuth, MN 23850:4/12/23:780900
Fred Fardbarkle:674-843-1385:20 Parak Lane, DeLuth, MN 23850:4/12/23:780900
Lori Gortz:327-832-5728:3465 Mirlo Street, Peabody, MA 34756:10/2/65:35200
Paco Gutierrez:835-365-1284:454 Easy Street, Decatur, IL 75732:2/28/53:123500
Ephram Hardy:293-259-5395:235 CarltonLane, Joliet, IL 73858:8/12/20:56700
James Ikeda:834-938-8376:23445 Aster Ave., Allentown, NJ 83745:12/1/38:45000
Barbara Kertz:385-573-8326:832 Ponce Drive, Gary, IN 83756:12/1/46:268500
Lesley Kirstin:408-456-1234:4 Harvard Square, Boston, MA 02133:4/22/62:52600
William Kopf:846-836-2837:6937 Ware Road, Milton, PA 93756:9/21/46:43500
Sir Lancelot:837-835-8257:474 Camelot Boulevard, Bath, WY 28356:5/13/69:24500
Jesse Neal:408-233-8971:45 Rose Terrace, San Francisco, CA 92303:2/3/36:25000
Zippy Pinhead:834-823-8319:2356 Bizarro Ave., Farmount, IL 84357:1/1/67:89500
Arthur Putie:923-835-8745:23 Wimp Lane, Kensington, DL 38758:8/31/69:126000
Popeye Sailor:156-454-3322:945 Bluto Street, Anywhere, USA 29358:3/19/35:22350
Jose Santiago:385-898-8357:38 Fife Way, Abilene, TX 39673:1/5/58:95600
Tommy Savage:408-724-0140:1222 Oxbow Court, Sunnyvale, CA 94087:5/19/66:34200
Yukio Takeshida:387-827-1095:13 Uno Lane, Ashville, NC 23556:7/1/29:57000
Vinh Tranh:438-910-7449:8235 Maple Street, Wilmington, VM 29085:9/23/63:68900

1.把Jon's的名字改成Jonathan.
[root@mycat shell]# sed -n "s/Jon's/Jonathan/p" test.txt   #-n不输出 p打印
2.删除头三行
[root@mycat shell]# sed '1,3d' test.txt 
3.显示5-10行
[root@manager test6]# cat -n test6.txt | sed -n '5,10p'
[root@mycat shell]# sed '5,10!d' test.txt 
4.删除包含Lane的行.
[root@mycat shell]# sed '/Lane/d' test.txt 
5.显示所有生日在November-December之间的行
[root@mycat shell]# sed -n '/:1[12]\//p' test.txt 
6.把三个星号(***)添加到也Fred开头的行
[root@manager test6]# grep Fred test6.txt | sed '/^Fred/s/^/***/'
[root@mycat shell]# grep Fred test.txt | sed 's/^Fred/***Fred/'
7.用JOSE HAS RETIRED取代包含Jose的行
[root@manager test6]# grep Jose test6.txt | sed '/Jose/s/.*/JOSE HAS RETIRED/'
8.把Popeye的生日改成11/14/46
[root@manager test6]# grep Popeye test6.txt | sed -r '/Popeye/s@[1]?[0-9]/[1-3]?[0-9]/[0-9][0-9]@11/14/46@'
9.删除所有空白行
[root@manager test6]# sed '/^$/d' test6.txt 

替换命令
n 可以是1-512,表示第n次出现的情况进行替换
如:
ababab
sed ‘s/ab/oo/2’ file

g 全局更改
如:
apple apple apple
sed ‘s/apple/ooo/g’file

p 打印模式空间的内容(完成替换后)

apple apple apple
123 123 123
sed ‘s/apple/123/p’file.txt
只打印apple行 所以能用-n参数

w file 写入到一个文件file中(只保存替换后行)
如:
sed ‘s/ab/OOO/w a.txt’ test

& 用正则表达式匹配的内容进行替换
\n 回调参数 (前边的分组再拿回来)
如:
apple
123
apple

sed ‘s/apple/&&/’a.txt &表示前边的正则表达式 2个&代表输出2次

比如:
this is linux

sed ‘s/linux/& redhat/’file.txt

\n如:
$ cat test1
first:second
one:two
$ sed ‘s/(.)😦.)/\2:\1/’ test1
second:first
two:one

匹配交换第一第二个字符
sed -r ‘s/(.)(.)/\2\1/’/etc/passwd
sed –r ‘s/(.)(.)(.*)/\2\1\3/’/etc/passwd

1.交换前两个单词
[root@manager test6]# sed -r 's/([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/\3\2\1\4/' test11.txt
[root@mycat shell]# sed -r 's/([a-Z]+)([^a-Z]+)([a-Z]+)/\3\2\1/' aaa.txt 
[root@mycat shell]# sed -r 's/(.)(..)(.*)/\1\xx\3/' aaa.txt 

2.将2.3字符换成xx
[root@manager test6]# sed -r 's/(.)(..)(.*)/\1XX\3/' test11.txt

3.将第一个单词和最后一个单词换位置
[root@manager test6]# sed -r 's/([a-Z]+)([^a-Z]+)(.*)([^a-Z]+)([a-Z]+)/\5\2\3\4\1/' test11.txt
[root@mycat shell]# sed -r 's/([a-Z]+)([^a-Z]+)(.*)([^a-Z]+)([a-Z]+)/\5\2\3\4\1/' aaa.txt 

4.将第一个字符和最后一个单词换位置
[root@manager test6]# sed -r 's/(.)(.*)([^a-Z]+)([a-Z]+)/\4\2\3\1/' test11.txt
[root@mycat shell]# sed -r 's/(.)(.*)([^a-Z]+)([a-Z]+)/\4\2\3\1/' aaa.txt 

如文件
apple banana egg
一. apple banana egg
apple.Banana egg
apple. Banana egg
apple? Banana egg

删除
[address]d
删除模式空间的内容,同时改编脚本的控制流,执行这个命令后,在“空
的”模式空间不再有命令执行。删除命令会导致读取新的输入行

sed ‘d;s’ 如果执行了d后边所有命令就不执行了,那么s做什么用? 
sed ‘/a/d;s 这样如果匹配了a字符 就删除,否则就执行s 这样就构成了一个判断
sed=’a.txt =显示行号


aaaaa
bbbbb
ccccc
dddd
sed '/c/d;=' test.txt


追加、插入和更改(a添加 i插入 c更改)
[line-address]a text 
例: 
aaaaa
bb xx bbb
ccccc
dddd
sed '/xx/a hello' test.txt

sed '/xx/i hello' test.txt

sed '/xx/c hello' test.txt

以上的操作都是将结果显示在屏幕上:怎么写入到文件
sed –i '/xx/c hello' test.txt
-i 直接对源文件做改变 注意文件inode会变化

转换
[address]y/abc/xyz/

sed ‘y/abcdefghijklmnopqrstu…/ABCDEFGHIJKLMNOPQRS…/’a.txt

打印
[address]p 用于输出模式空间的内容
sed ‘’a.txt
sed ‘3p’a.txt

打印行号
[line-address]=
apple
123
apple

sed -n '/apple/{=;p}' test

下一步
[address]n
n命令输出模式空间的内容,然后读取输入的下一行,而不用返回脚本的顶端
test aa bb
test aa bb

sed ‘/test/{n;s/aa/xx}’test.txt

读和写文件 范例文件 file4.txt
[line-address]r file
[address]w file
sed -e '/<mail list>/r maillist' -e '/<mail list>/d' file4.txt
/MA$/w region.MA
/VA$/w region.VA
/CA$/w region.CA

如:
test aa bb
test aa bb

apple
banana
abc

sed '/banana/r aa.txt' tt.txt


退出(速度更快)
[line-address]q
$ sed '100q' test

测试:
for i in {1..100000}
do
	echo $i >> num.txt
done

time sed –n ‘1,100p’num.txt
time sed ‘100q’num.txt
2.高级sed 命令

高级命令分成3个组:
1 处理多行模式空间(N、D、P)
2 采用保持空间来保存模式空间的内容并使它可用于后续的命令
(H、h、G、g)
3 编写使用分支和条件指令的脚本来更改控制流(:、b、t)
高级脚本都做一件共同的事,那就是他们改变了执行或控制的流程顺序。

多行模式空间
追加下一行
多行Next(N)命令通过读取新的输入行,并将它添加到模式空间的现有内容
之后来创建多行模式。

user1
123
user2
1234
user3
12345
将该文件 用户名,密码追加到一起
每两行合并sed ‘N;s/\n/:/’

3行合并sed ‘N;N;s/\n/:/g’

多行删除
D命令删除模式空间中直到第一个换行符的内容。它不会导致读入新的输入行,
相反,它返回到脚本的顶端,将这些指令应用与模式空间剩余的内容。

user1
123

M

user2
1234

W
user3
12345
M

sed ‘/^KaTeX parse error: Undefined control sequence: \n at position 7: /{N;/^\̲n̲/D}’ user.txt

/^KaTeX parse error: Undefined control sequence: \n at position 8: /{ N /^\̲n̲/D
}
多行删除命令完成工作的原因是,当遇到两个空行时,D命令只删除两个空行
中的第一个。下一次遍历该脚本时,这个空行将导致另一行被读入模式空间。
如果那行不为空,那么两行都输出,因此确保了输出一个空行。换句话说,当
模式空间中有两个空行时,只有第一个空行被删除。当一个空行后面跟有文本
时,模式空间可以正常输出。
多行

多行打印
P命令输出多行模式空间的第一部分,直到第一个嵌入的换行符为止。在执行
完脚本的最后一个命令之后,模式空间的内容自动输出。
P命令经常出现在N命令之后和D命令之前。
这三个命令能建立一个输入、输出循环,用来维护两行模式空间,但是一次只
输出一行。
这个循环的目的是只输出模式空间的第一行,然后返回到脚本的顶端将所有的
命令应用于模式空间的第二行。
没有这个循环,当执行脚本中的最后一个命令时,模式空间中的这两行都将被
输出。
删除文件倒数第二行
sed ‘N; ! P ; D ′ a . t x t 删 除 文 件 最 后 两 行 s e d ′ N ; !P;D' a.txt 删除文件最后两行 sed 'N; !P;Da.txtsedN;!P; ! D ; !D; !D;d’ a.txt
包含

包含那一行
模式空间是容纳当前输入行的缓冲区。还有一个称为保持空间(hode space)
的预留(set-aside)缓冲区。模式空间的内容可以复制到保持空间,而且保持
空间的内容也可以复制到模式空间。有一组命令用于在保持空间和模式空间之
间移动数据。保持空间用于临时存储。单独的命令不能寻址保持空间或者更改
它的内容。
保持空间最常见的用途是,当改变模式空间中的原始内容时,用于保留当
前输入行的副本。

模式空间 保持空间
默认换行符
h—覆盖 全部覆盖
H—追加-> 追加到换行符之后
g覆盖取回–
G追加取回—
例:

1111111
2222222
利用空间输出
2222222
11111111

sed ‘1h;1d;2G’test.txt

每行加一个空行?


11111
22222
33333
44444
反转
sed '1h;1d;2G;2h;2d;3G;3h;3d;4G' test17.txt
sed '1{h;d};2,3{G;h;d};4G' test17.txt 

 sed '1!G;$!h;$!d' test17.txt    #多行
大写转换
样本文件 file6.txt
find the Match statement
Consult the Get statement.
using the Read statement to retrieve data
将 the 和statement之间的单词转换成大写
脚本:changsrc

capitalize statement names

/the .* statement/{
h
s/.*the \(.*\) statement.*/\1/
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
G
s/\(.*\)\n\(.*the \).*\( statement.*\)/\2\1\3/
}

执行过程:
h 将当前输入行复制到保持空间
Pattern Space: find the Match statement
Hold Space: find the Match statement
s/.*the \(.*\) statement.*/\1/ 取出将被替换的语句
Pattern Space: Match
Hold Space: find the Match statement
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
Pattern Space: MATCH
Hold Space: find the Match statement
G 将保持空间中的内容追加到模式空间
Pattern Space: MATCH\nfind the Match statement
Hold Space: find the Match statement
s/\(.*\)\n\(.*the \).*\( statement.*\)/\2\1\3/ 替换并排序
Pattern Space: find the MATCH statement
Hold Space: find the Match statement
3.练习
1,删除文件每行的第一个字符。
[root@manager zuoye]# sed 's/.//' aa.txt  
[root@manager zuoye]# sed -r 's/(.)(.*)/\2/' aa.txt
2,删除文件每行的第二个字符。
[root@manager zuoye]# sed -r 's/(.)(.)(.*)/\1\3/' aa.txt
3,删除文件每行的最后一个字符。
[root@manager zuoye]# sed -r 's/(.*)(.)$/\1/' aa.txt
4,删除文件每行的倒数第二个字符。
[root@manager zuoye]# sed -r 's/(.*)(.)(.)$/\1\3/' aa.txt 
5,删除文件每行的第二个单词。
[root@manager zuoye]# sed -r 's/([a-Z]+)([^a-Z]+)([a-Z]+)/\1\2/' aa.txt 
6,删除文件每行的倒数第二个单词。
[root@manager zuoye]# sed -r 's/(.*)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)/\1\2\4\5/' aa.txt
7,删除文件每行的最后一个单词。
[root@manager zuoye]# sed -r 's/(.*)([^a-Z]+)([a-Z]+)/\1\2/' aa.txt 
8,交换每行的第一个字符和第二个字符。
[root@manager zuoye]# sed -r 's/(.)(.)(.*)/\2\1\3/' aa.txt 
9,交换每行的第一个字符和第二个单词。
[root@manager zuoye]# sed -r 's/(.)([a-Z]+)?([^a-Z]+)?([a-Z]+)/\4\2\3\1/' aa.txt
10,交换每行的第一个单词和最后一个单词。
[root@manager zuoye]# sed -r 's/([a-Z]+)([^a-Z]+)(.*)([^a-Z]+)([a-Z]+)/\5\2\3\4\1/' aa.txt  
11,删除一个文件中所有的数字。
[root@manager zuoye]# sed 's/[0-9]//g' aa.txt
12,删除每行开头的所有空格。
[root@manager zuoye]# sed 's/^ *//g' aa.txt
13,用制表符替换文件中出现的所有空格。
[root@manager zuoye]# sed 's/ /\t/g' aa.txt 
14,把所有大写字母用括号()括起来。
[root@manager zuoye]# sed 's/[A-Z]/(&)/g' /etc/passwd
15,打印每行3次。
[root@manager zuoye]# sed -n 'p;p;p' aa.txt 
16,隔行删除
[root@manager zuoye]# cat -n  /etc/passwd | sed 'n;d'。
[root@manager zuoye]# cat -n  /etc/passwd | sed '1d;n;d'
[root@manager zuoye]# cat -n  /etc/passwd | sed '0~2d' #起始~步距
[root@manager zuoye]# cat -n  /etc/passwd | sed '1~2d'

17,把文件从第22行到第33行复制到第56行后面。
[root@manager zuoye]# cat -n /etc/passwd | sed '22h;23,33H;56G'
18,把文件从第22行到第33行移动到第56行后面。
[root@manager zuoye]# cat -n /etc/passwd | sed '22{h;d};23,33{H;d};56G'

19,只显示每行的第一个单词
[root@manager zuoye]# sed -r 's/([a-Z]+)([^a-Z]+)(.*)/\1/' aa.txt
20,打印每行的第一个单词和第三个单词。
[root@manager zuoye]# sed -r 's/([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/\1:\5/' aa.txt
21,将格式为    mm/yy/dd    的日期格式换成   mm;yy;dd
[root@manager zuoye]# echo "mm/yy/dd " | sed 's@/@:@g'

十、AWK

​ AWK是一种优良的文本处理工具。它不仅是 Linux 中也是任何环境中现有的功能最强大的数据处理引擎之一。这种编程及数据操作语言(其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母)的最大功能取决于一个人所拥有的知识。AWK 提供了极其强大的功能:可以进行样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。它具备了一个完整的语言所应具有的几乎所有精美特性。实际上 AWK 的确拥有自己的语言:AWK 程序设计语言, 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
最简单地说, AWK 是一种用于处理文本的编程语言工具。AWK 在很多方面类似于 shell 编程语言,尽管 AWK 具有完全属于其本身的语法。它的设计思想来源于 SNOBOL4 、sed 、Marc Rochkind设计的有效性语言、语言工具 yacc 和 lex ,当然还从 C 语言中获取了一些优秀的思想。在最初创造 AWK 时,其目的是用于文本处理,并且这种语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。该实用工具扫描文件中的每一行,查找与命令行中所给定内容相匹配的模式。如果发现匹配内容,则进行下一个编程步骤。如果找不到匹配内容,则继续处理下一行。

我们常用sed和awk相结合的方式处理文本sed|awk的方式能让我们的处理命令更快捷,提高工作效率

1.awk语法
 awk [options] ‘commands’ files

option

-F 定义字段分隔符,默认的分隔符是连续的空格或制表符
如:
aaaa   bbbb
aaa bb 
ccc       dd
awk{print $1}’a.txt
awk -F: ‘{print $1}’/etc/passwd
用$1,$2,$3等的顺序表示files中每行以间隔符号分隔的各列不同域.$0代表整行
如:
awk{print $1,$2,$0}’a.txt
NF变量表示当前记录的字段数
如:
awk -F: ‘{print NF}’/etc/passwd
awk -F: ‘{print $NF}’/etc/passwd

-v可以借用此方式从shell变量中引入
command

读前处理 行处理 读后处理

1. 读前处理BEGIN{awk_cmd1;awk_cmd2}
   如:
   awk 'BEGIN {print "hello,World"}'
   awk 'BEGIN{print "start handle file"}{print}END{print"stop handle file"}' /etc/passwd


练习:
先输出 开始处理文件
取当前系统所有用户的名字 uid  和 shell   
最后输出 处理完毕



2. 行处理:定址命令
   定址方法: 正则,变量,比较和关系运算
   正则需要用//包围起来
   awk -F: '/root/{print}' /etc/passwd
   awk -F: '$1~/root/{print}' /etc/passwd    #~是匹配
   awk -F: '$1!~/root/{print}' /etc/passwd
   ^ 行首
   $ 行尾
   . 除了换行符以外的任意单个字符
   * 前导字符的零个或多个
   .* 所有字符
   [] 字符组内的任一字符
   [^]对字符组内的每个字符取反(不匹配字符组内的每个字符)
   ^[^] 非字符组内的字符开头的行
   [a-z] 小写字母
   [A-Z] 大写字母
   [a-Z] 小写和大写字母
   [0-9] 数字
   < 单词头单词一般以空格或特殊字符做分隔,连续的字符串被当做单词
   > 单词尾                       #<root>现在root这个单词


NR变量定址

NR 表示AWK读入的行数
FNR表示读入行所在文件中的行数
# awk '{print NR,FNR,$1}' file1 file2
1 1 aaaaa
2 2 bbbbb
3 3 ccccc
4 1 dddddd
5 2 eeeeee
6 3 ffffff

逻辑运算可直接引用域进行运算
== >= <= != > <
\# awk 'NR==1 {print}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

3.命令{print $0}
4.读后处理END {awk_cmd1;awk_cmd2;}

例子:
cat 2.txt | head -2
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin

awk –F”:”‘{print $1,$6}’2.txt | head -2
root /root
bin /bin
awk –F“:” ‘{print $1”--”$6}2.txt|head -2    #”--”替换分隔符
root	/root
bin		/bin
awk –F”:”	‘BEGIN{print “用户名\t\t家目录”}{print $1\t\t”$6}’2.txt| head -2

OFS在BEGIN里面指定分隔符

例2:cat 1.txt
mona 70 77 85 83 70 89
john 85 92 78 94 88 91
andrea 89 90 85 94 90 95
jasper 84 88 80 92 84 82
dunce 6 80 60 60 61 62
ellis 5 98 89 96 96 92

awk 'BEGIN{print "\t\t某某班14暑假成绩\n学员\t数学\t语文\t英语\t化学\t物理\t历史"}{print $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7}END{print "年级排名请查询数据库"}' test3.txt l

awk 'BEGIN{OFS="\t";print "\t\t某某班14暑假成绩";print "学 员\t数学\t语文\t英语\t化学\t物理\t历史"}{print $1,$2,$3,$4,$5,$6,$7}END{print "年级排名请查询数据库"}' test3.txt

awk{print $0}’1.txt
awk$2~/6/{print $0}’1.txt
awk$2>=6{print $0}’1.txt

操作符

赋值= ++ -- += -= /= *= 
++ Add 1 to variable.
awk 'BEGIN{a=1;a++;print a}'  打印值为2
awk 'BEGIN{a=1;++a;print a}'  打印值为2 先加1在执行命令
awk 'BEGIN{a=1;print a++;print a}' 先执行命令然后a+1
a=1命令先执行a还=1,最后在打印a=2

-- Subtract 1 from variable. awk '
BEGIN{a=1;a--;print a}' 打印值为0

+= Assign result of addition.
awk 'BEGIN{a=1;b=a+5;print b}'  给b赋值
awk 'BEGIN{a=1;a=a+5;print a}'  给自己赋值
awk 'BEGIN{a=1;a+=5;print a}'  上条命令简化写法

-= Assign result of subtraction.
awk 'BEGIN{a=1;a-=5;print a}'  a-5在赋值给a 以下相同
awk 'BEGIN{a=1;a-=5.2;print a}' 支持小数运算
*= Assign result of multiplication.
/= Assign result of division.
%= Assign result of modulo.
^= Assign result of exponentiation


|| Logical OR  逻辑 两边任意一边成立
&& Logical AND 逻辑与 两边成立  前边如果假值后边就不做了
! Logical NOT  逻辑取反 原本真值变成假值
例如:
awk '$1~/root/||NR>40{print}' /etc/passwd
awk '$1~/root/&&NR<2{print}' /etc/passwd

匹配正则或不匹配,正则需要用/正则/ 包围住
~     !~

关系比较字符串时要把字符串用双引号引起来
< <= > >= != ==

字段引用
$ 字段引用需要加$,而变量引用直接用变量名取
运算符+ - * / % ++ --

转义序列

\ \自身
\$ 转义$
\t 制表符
\b 退格符
\r 回车符
\n 换行符
\c 取消换行

awk引用shell变量
a=root
awk –v var=$a –F“:” ’$1==var{print $0}’ /etc/passwd

内置变量

FS 定义字段分隔符,默认为一个空格     #读入分隔符
OFS 输出的字段分隔符,默认为一个空格   #输出分隔符
awk 'BEGIN{ FS=":";OFS="-" }{ print $1,$3,$5 }' /etc/passwd  | head -2
awk 'BEGIN{ FS=":";OFS="-" }{ print $1”@@”$3”##”$5 }' /etc/passwd  | head -2  自己定义
RS 记录分隔符,默认为一个换行符
head -2 /etc/passwd | awk 'BEGIN{ RS=":"}{print}'
ORS 输出的记录分隔符,默认为一个换行符
head -2 /etc/passwd | awk 'BEGIN{ ORS="-"}{print}'
NR 行数(如两个文件一个5行 1个10行 输出为15行)
awk  '{print NR}' aa.txt cc.txt
FNR 行数,多文件操作时会重新排序(如两个文件一个5行,一个10行 输出为5 10) awk  '{print FNR}' aa.txt cc.txt
NF 输出当前输入记录的编号(字段的个数)
aaaa bbb ccc
cccc bbb
aaa ccc bb  nn
awk '{ print NF}' aa.txt
FILENAME 文件名
ARGC 命令行参数个数
ARGV 命令行参数排列
ENVIRON 输出系统环境变量

例子:
cat awk.txt
aaa#123
asdf:234
haha:456

awk –F’#’ ’{print NR,$0,NF,FILENAME }’awk.txt
1 aaaa#123 2 awk.sh
2 asdf:234 1 awk.sh
3 haha:456 1 awk.sh
4  0 awk.sh

awk -F"#" '{ print ARGC,ARGV[0],ARGV[1]}' awk.sh.txt
2 awk awk.sh.txt
2 awk awk.sh.txt
2 awk awk.sh.txt
2 awk awk.sh.txt

awk -F"#" '{ print NR,$0,NF,$1,$2 }' awk.sh.txt
1 aaaa#123 2 aaaa 123
2 asdf:234 1 asdf:234
3 haha:456 1 haha:456
4  0

awk 'BEGIN{print ENVIRON["USER"]}'

练习题:

1打印uid在30~40范围内的用户名
[root@mycat shell]# awk -F":" '$3>=30&&$3<=40{print $1,$3}' /etc/passwd

2打印第5-10行的行号和用户名
[root@mycat shell]# awk 'NR>=5&&NR<=10{print NR,$1}' /etc/passwd

3 打印奇数行
[root@mycat shell]# awk 'NR%2==1{print NR,$0}' /etc/passwd

4 打印偶数行
[root@mycat shell]# awk 'NR%2==0{print NR,$0}' /etc/passwd

5 打印字段数大于5的行
[root@mycat shell]# awk -F":" 'NF>5{print $0}' /etc/passwd

6打印UID不等于GID的用户名
[root@mycat shell]# awk -F":" '$3!=$4{print $0}' /etc/passwd

7.打印1..100以内的7的倍数和包含7的数
seq 1 100 | awk '$1~/7/||$1%7==0{print $1}'

8.计算UID相加的总和;计算GID相加的总和
[root@mycat shell]# awk -F: '{uid+=$3;gid+=$4}END{print uid;print gid}' /etc/passwd

9.计算VSZ和RSS各自的和 并以M单位显示
[root@mycat shell]# ps aux|awk 'NR>1{vsz+=$5;rss+=$6}END{print vsz/1024"M";print rss/1024"M"}'


2.流程控制

分支结构
一. if (条件) 动作
若有多个动作,则要用大括号将动作体包含起来if (条件) {动作1;动作2}

awk -F : '{if ($1 == "root") print $1}' /etc/passwd
awk -F: '{if ($1 == "root") {print $1;print $6} }' /etc/passwd

二.

if(条件1)
动作1
else
动作2

awk -F: '{if ($1 == "root"){print $1}else print $6}' /etc/passwd
awk -F: '{if ($1 == "root") print $1;else print $6}' /etc/passwd

上面两个命令是等价的,要么用分号隔开,表示第一个动作体的结束,要么将动作体用大括号定位范围

三.
if (条件1)
动作1
else if(条件2)
动作2
else if(条件3)
动作3
else
动作

awk -F: '{if ($1 == "root") print $1;else if ($1 == "seker") print $6;else if ($1 == "zorro") print $7;else printNR}' /etc/passwd

读前处理和读后处理
awk -F: 'BEGIN {print NR,NF}' /etc/passwd
0 0
awk -F: 'END {print NR,NF}' /etc/passwd
46 7
awk -F: 'BEGIN{i=1} {i++} END {print i}' /etc/passwd
47

练习:

找出普通用户的用户名并统计数量
[root@mycat shell]# awk -F: '{if($3>=1000) {print $1;sum++}END{print sum}' /etc/passwd

将系统用户按UID分组标记 0 admin; 1-499 sysuser; 1000+ users

用户名  uid   权限
root   0     admin
ftp    21    sysuser
robin  500   users
[root@manager test7]# awk 'BEGIN{FS=":";OFS="\t";print "用户名\tUID\t权限"}{if($3==0)print $1,$3,"admin";else if($3<500)print $1,$3,"sysusers";else print $1,$3,"users"}' /etc/passwd



awk 'BEGIN{OFS="\t";print "名字\t语文\t数学\t英语\t历史\t物理\t生物\t总成绩\t平均\t评价"}{sum=$2+$3+$4+$5+$6+$7;if(sum/6>=90)print $1,$2,$3,$4,$5,$6,$7,sum,sum/6,"优秀";else if(sum/6>=80)print $1,$2,$3,$4,$5,$6,$7,sum,sum/6,"良好";else if(sum/6>=70)print $1,$2,$3,$4,$5,$6,$7,sum,sum/6,"一般";else print $1,$2,$3,$4,$5,$6,$7,sum,sum/6,"烂"}END{print "某某班级期中成绩"}' chengji.txt

循环语句
while(条件) {
动作
条件运算

awk -F: '{while($3<3) {print $3,$1;$3++}}' /etc/passwd

BEGIN块可以独立使用,不需要引入文件
awk 'BEGIN{i=1;while(i<100) {print i;i++}}'

练习 打印100以内的偶数
awk 'BEGIN{i=1;while(i<100){if(i%2==0)print i;i++}}'

x=1

do {
动作1
x++
} while (x<5)

awk 'BEGIN{i=5;do{print i;i++}while(i<10)}'
awk 'BEGIN{i=5;do{print i;i++}while(i<1)}'

for(预置;条件;递增) {
动作
}

awk 'BEGIN {for (x=1;x<=4;x++) print x }'

练习:
逆序输出每个字段
	达到这样既可
	/bin/bash
	/root
	root
	0
	0
	x
	root

awk -F: 'BEGIN{printf "%-15s %-15s %-15s\n","用户名","UID","权限"}{if ($3==0) printf "%-18s %-15s %-15s\n",$1,$3,"admin";else if($3<500) printf "%-18s %-15s %-15s\n",$1,$3,"sysuser";else printf "%-18s %-15s %-15s\n",$1,$3,"users"}' /etc/passw

跳转语句

break 跳出循环

continue 在达到循环底部之前终止当前循环从新开始下一次循环

next 读入下一行同时返回脚本顶部这样可以避免对当前行执行其他操作

awk 'BEGIN {for(x=1;x<5;x++) {if (x==3) break;print x }}'

awk 'BEGIN {for(x=1;x<5;x++) {if (x==3) continue;print x }}'

awk -F: 'NR > 5 {next} {print $1} END {print NR}' /etc/passwd
cat -b u.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 news:x:9:13:news:/etc/news:


awk -F: 'BEGIN{i=1}NR>5 {next}{print i++}END{print NR}' u.txt
1
2
3
4
5
10

awk -F: 'BEGIN{i=1}NR>5 {exit}{print i++}END{print NR}' u.txt
1
2
3
4
5
3.数组

自定义数组

awk 'BEGIN {ary[1]="seker";ary[2]="zorro";print ary[1],ary[2]}'
seker zorro
awk 'BEGIN {ary[1]="seker";ary[2]="zorro";for(i in ary) print ary[i]}'
seker
zorro

循环产生数组和取出数组

awk 'BEGIN{n=5;for (i=1;i<=n;i++) ary[i]=i+100;for(m in ary) print m,ary[m]}'
4 104
5 105
1 101
2 102
3 103

awk -F: '{ary[NR]=$1} END {for(i in ary) print i,ary[i]}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
6 sync
7 shutdown
8 halt
9 mail


awk -F: '{ary[$3]=$1} END {for(i in ary) print i,ary[i]}' /etc/passwd
10 uucp
11 operator
12 games
13 gopher
14 ftp
32 rpc
37 rpm

利用数组实现行列互换?

[root@manager ~]# cat aa.txt 
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5

1 1 1 1 1 
2 2 2 2 2 
3 3 3 3 3 
4 4 4 4 4 
5 5 5 5 5

[root@manager test7]# awk '{for(i=1;i<=NF;i++) ary[NR,i]=$i}END{for(j=1;j<=NF;j++){for(l=1;l<=NR;l++)printf ary[l,j]" ";printf "\n"}}' kk.txt
4.awk的内置函数

一.算数函数

sqrt函数(求平方根)
[root@manager ~]# awk 'BEGIN{x=sqrt(100);print x}'
10

[root@manager ~]# awk 'BEGIN{x=int(3.14);print x}'
3
[root@manager ~]# awk 'BEGIN{x=int(3.88);print x}'
3

rand函数(内置函数 srand 使用参数awk 'BEGIN{x=rand();print x}'作为随机数种子。当参数缺省的时候,使用当前时间作为随机数种子。)
[root@manager ~]# awk 'BEGIN{x=rand();print x}'
0.237788

srand函数(内置函数 rand 的伪随机函数,其返回值范围为 0 >= result <= 1。在实际使用时,一般先使用 srand 函数生成随机数种子,然后再使用 rand 函数生成随机数。否则每次得到的值会一样。)
[root@manager ~]# awk 'BEGIN{srand();x=int(100*rand());print x}'
64
[root@manager ~]# awk 'BEGIN{srand();x=int(100*rand());print x}'
32
[root@manager ~]# awk 'BEGIN{srand();x=int(100*rand());print x}'
60

二.字符串函数

sub和gsub函数使用(替换字符)
[root@manager ~]# awk 'BEGIN{info="this is a test2010test2010test!";sub(2010,"!",info);print info}'
this is a test!test2010test!

[root@manager ~]# awk 'BEGIN{info="this is a test2010test2010test!";gsub(2010,"!",info);print info}'
this is a test!test!test!

index函数(查找字符)
[root@manager ~]# awk 'BEGIN{info="this is test2010test!";print index(info,"test")?"ok":"not found";}'
ok

length函数(计算长度)
[root@manager ~]# awk 'BEGIN{info="hello";print length(info)}'
5

substr函数(截取字符串)
[root@manager ~]# awk 'BEGIN{info="hello world";print substr(info,4,10)}'
lo world

match函数(正则匹配查找)
[root@manager ~]# awk 'BEGIN{info="hello 2016world";print match(info,/[0-9]+/)?"ok":"none";}'
ok

split函数(字符分割)
[root@manager ~]# awk 'BEGIN{info="hello world";split(info,test);print length(test);for(k in test){print k,test[k];}}'
2
1 hello
2 world

三.一般函数

函数说明
close( Expression )用同一个带字符串值的 Expression 参数来关闭由 print 或 printf 语句打开的或调用 getline 函数打开的文件或管道。如果文件或管道成功关闭,则返回 0;其它情况下返回非零值。如果打算写一个文件,并稍后在同一个程序中读取文件,则 close 语句是必需的。
system(Command )执行 Command 参数指定的命令,并返回退出状态。等同于 system 子例程。
Expression | getline [ Variable ]从来自 Expression 参数指定的命令的输出中通过管道传送的流中读取一个输入记录,并将该记录的值指定给 Variable 参数指定的变量。如果当前未打开将 Expression 参数的值作为其命令名称的流,则创建流。创建的流等同于调用 popen 子例程,此时 Command 参数取 Expression 参数的值且 Mode 参数设置为一个是 r 的值。只要流保留打开且 Expression 参数求得同一个字符串,则对 getline 函数的每次后续调用读取另一个记录。如果未指定 Variable 参数,则 $0 记录变量和 NF 特殊变量设置为从流读取的记录。
getline [ Variable ] < Expression从 Expression 参数指定的文件读取输入的下一个记录,并将 Variable 参数指定的变量设置为该记录的值。只要流保留打开且 Expression 参数对同一个字符串求值,则对 getline 函数的每次后续调用读取另一个记录。如果未指定 Variable 参数,则 $0 记录变量和 NF 特殊变量设置为从流读取的记录。
getline [ Variable ]将 Variable 参数指定的变量设置为从当前输入文件读取的下一个输入记录。如果未指定 Variable 参数,则 $0 记录变量设置为该记录的值,还将设置 NF、NR 和 FNR 特殊变量。


close函数
[root@manager ~]# awk 'BEGIN{while("head -5 /etc/passwd"|getline){print $0;};close("/etc/passwd");}'
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

getline函数    #输入
[root@manager ~]#  awk 'BEGIN{while(getline < "/etc/passwd"){print $0;};close("/etc/passwd");}'


[root@manager ~]#  awk 'BEGIN{print "Enter your name:";getline name;print name;}'
Enter your name:
robin
robin
[root@manager ~]#  awk 'BEGIN{printf "Enter your name:";getline name;print name;}'
Enter your name:robin
robin

[root@manager test7]# awk -F: 'BEGIN{printf "输入用户名: ";getline name;while(getline < "/etc/passwd"){if($1==name) print $0}}'



system函数    #调用系统指令
[root@manager ~]#  awk 'BEGIN{b=system("ls -l /root");print b;}'
总用量 20
-rw-r--r--  1 root root  135 918 16:14 aa.txt
-rw-------. 1 root root  905 99 06:30 anaconda-ks.cfg
-rw-r--r--. 1 root root 8003 99 06:30 install.log
-rw-r--r--. 1 root root 3384 99 06:30 install.log.syslog
0
[root@manager test7]# awk -F: 'BEGIN{x=system("id robin &>/dev/null");if(x==0){while(getline < "/etc/passwd")if($1=="robin")print $1,$6}}'
robin /home/robin

四.时间函数

函数名说明
mktime( YYYY MM DD HH MM SS[ DST])生成时间格式
strftime([format [, timestamp]])格式化时间输出,将时间戳转为时间字符串 具体格式,见下表
systime()得到时间戳,返回从1970年1月1日开始到当前时间(不计闰年)的整秒数

创建时间格式

[root@manager ~]# awk 'BEGIN{tstamp=mktime("2001 01 01 12 12 12");print strftime("%c",tstamp);}'
2001年01月01日 星期一 12时12分12秒

[root@manager ~]#  awk 'BEGIN{tstamp1=mktime("2001 01 01 12 12 12");tstamp2=mktime("2001 02 01 0 0 0");print tstamp2-tstamp1;}'
2634468


[root@manager ~]# awk 'BEGIN{tstamp1=mktime("2001 01 01 12 12 12");tstamp2=systime();print tstamp2-tstamp1;}' 
495876393

strftime日期和时间格式说明符

格式描述
%a星期几的缩写(Sun)
%A星期几的完整写法(Sunday)
%b月名的缩写(Oct)
%B月名的完整写法(October)
%c本地日期和时间
%d十进制日期
%D日期 08/20/99
%e日期,如果只有一位会补上一个空格
%H用十进制表示24小时格式的小时
%j从1月1日起一年中的第几天
%m十进制表示的月份
%M十进制表示的分钟
%p12小时表示法(AM/PM)
%S十进制表示的秒
%U十进制表示的一年中的第几个星期(星期天作为一个星期的开始)
%w十进制表示的星期几(星期天是0)
%W十进制表示的一年中的第几个星期(星期一作为一个星期的开始)
%x重新设置本地日期(08/20/99)
%X重新设置本地时间(12:00:00)
%y两位数字表示的年(99)
%Y当前月份
%Z时区(PDT)
%%百分号(%)

printf函数
是格式化输出函数, 一般用于向标准输出设备按规定格式输出信息。在编写程序时经常会用到此函数。printf()函数的调用格式为:

%d 十进制有符号整数
%u 十进制无符号整数
%f 浮点数
%s 字符串
%c 单个字符
%p 指针的值
%e 指数形式的浮点数
%x, %X 无符号以十六进制表示的整数
%o 无符号以八进制表示的整数
%g 自动选择合适的表示法
\n 换行
\f 清屏并换页
\r 回车
\t Tab符
\xhh 表示一个ASCII码用16进表示,其中hh是1到2个16进制数

输出样式
%s是字符类型,%d数值类型
printf默认是不输出换行的所以要加\n
10和7是偏移量
默认是右对齐,所有加个- 就是左对齐,就是把不足的位数用空格填充
注意:格式与输出列之间要有逗号

[root@manager ~]# awk ‘BEGIN{a=16;printf “%-10o\n”,a}’
20

1.使用awk打印一个用星号组成金字塔

2.使用awk打印99乘法表

3.输出100-999
1-9 0-9 0-9

4.开两个虚拟机,定时(10分钟)将虚拟机的httpd日志收集到本机,提取,这些httpd日志的 ip 访问时间 浏览器 保存到文件

练习1答案
1. awk -F: '$3>=30&&$3<=40 {print NR, $1}' passwd
2. awk -F: 'NR>=5&&NR<=10 {print NR, $1}' passwd
3. awk -F: 'NR%2!=0 {print}' passwd
4. awk -F: 'NR%2==0 {print}' passwd
5. awk -F: 'NF>5 {print NR}' passwd
6. awk -F: '$3!=$4 {print $3,$4}' passwd
7. seq 100 | awk '$0%7==0 || $0 ~/7/ {print $0}' | head -3
8. awk -F: 'BEGIN{i=0}{sum+=$3;gsum+=$4;i++}END{print i;print sum;print gsum}' /etc/passwd
9. ps aux | awk 'BEGIN{i=0}NR>=2{vsum+=$5;rsum+=$6;i++}END{print vsum/1024"M";print rsum/1024"M";print i}'
练习2:
练习找出普通用户的用户名并统计数量
awk -F: 'BEGIN{i=0} $3>=500 {print $1;i++} END {print i}' /etc/passwd
awk -F: '{if($3==0) print $1"\t"$3"\t""admin";else if($3>=1&&$3<500) print $1,$3,"sysuser";else print $1,$3,"user"}' /etc/passwd
练习打印100以内的偶数
# awk 'BEGIN{i=1;while(i<100) {if (i%2==0) print i;i++}}'

逆序输出每个字段
	达到这样既可
	/bin/bash
	/root
	root
	0
	0
	x
	root


	# awk -F: '{for (x=NF;x>0;x--) print $x}' /etc/passwd

输出样式
	%s是字符类型,%d数值类型
	printf默认是不输出换行的所以要加\n
	10和7是偏移量
	默认是右对齐,所有加个- 就是左对齐,就是把不足的位数用空格填充
	注意:格式与输出列之间要有逗号
	# awk -F: '{printf "%-10s %-10d %s\n",$1,$3,$7}' /etc/passwd

继续解决上一个试做题的格式问题
	# awk -F: '/bash$/{for (x=NF;x>0;x--) printf "%-13s",$x;printf "\n"}' /etc/passwd



使用嵌套的for循环,打印100-999之间的数,个十百位分别用一个for来打印
	# awk 'BEGIN{OFS="";for (i=1;i<=9;i++) {for (j=0;j<=9;j++) {for (n=0;n<=9;n++) print i,j,n}}}'
    打印乘法口诀表
	# cat 99.sh 
	#!/bin/bash
	awk 'BEGIN{
		for(i=1;i<10;i++)
		{
			for(j=1;j<=i;j++)
				printf "%d*%d=%d ",j,i,j*i
			print
		}	

	}'
	# 

    打印金字塔
	# cat jin.sh 
	#!/bin/bash
	awk 'BEGIN{
		num=5
		for(i=1;i<=num;i++)
		{	
			for (n=1;n<=num-i;n++)
				printf "%s"," "
			for (j=1;j<=2*i-1;j++)
		   		printf "%s","*"
			print
		}
	}'



[root@manager ~]# awk '{for(i=1;i<=NF;i++) ary[NR,i]=$i}END{for(j=1;j<=NF;j++){for(k=1;k<=NR;k++)printf ary[k,j]" ";printf "\n"}}' aa.txt 
1 1 1 1 1 
2 2 2 2 2 
3 3 3 3 3 
4 4 4 4 4 
5 5 5 5 5 
[root@manager ~]# cat aa.txt 
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值