小工具用法 grep 用法 语法:行过滤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 OPTIONS: -i : 不区分大小写 -v : 查找不包含指定内容的行,反向选择(取反) -w : 按单词搜索 -o : 打印匹配关键字 -c : 统计匹配到的行数 -n : 显示行号(注:区分大小写。不区分大小写的话 -ni ) -r : 逐层遍历目录查找 -A : 显示匹配行及后面多少行 -B : 显示匹配行及前面多少行 -C : 显示匹配行前后多少行 -l :只列出匹配的文件名 -L :列出不匹配的文件名 -e : 使用正则匹配 -E :使用扩展正则匹配 ^key:以关键字开头 key$ :以关键字结尾 ^$ :匹配空行 --color =auto :可以将找到的关键词部分加上颜色的显示
1.1 对 grep 起个别名 每次输入 grep 达到 grep —color=auto 效果
1 2 3 4 5 6 7 8 9 10 11 12 13 1 .临时生效alias grep='grep --color=auto' 2 .永久生效,全局,修改配置文件/etc/bashrc 文末添加此文件是用户在登陆时被读取 vim /etc/bashrc alias grep='grep --color=auto' 重新加载文件 source /etc/bashrc
1.2 需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 前置:cp /etc/passwd . 1 .过滤文件内容包含 root 的grep 'root' passwd 2 .过滤文件内容包含 root 的行显示行号(区分大小写)grep -n 'root' passwd 3 .过滤文件内容包含 root 的行显示行号(不区分大小写)grep -ni 'root' passwd 4 .过滤文件内容以 root 开头的行grep '^root' passwd 5 .过滤文件内容以 bash 结尾的行grep 'bash$' passwd 6 .过滤不以 root 开头的行grep -iv '^root' passwd 7 .过滤包含 root 的后5 行,显示行号grep -nA 5 'root' passwd 8 .过滤包含 mail 的前5 行,显示行号grep -nB 'mail' passwd 9 .过滤包含 mail 的前后3 行,显示行号grep -nC 3 'mail' passwd 10 .统计 passwd 文件中 root 出现的次数grep -c 'root' passwd
cut 用法 语法:列截取的工具
2.1 常见选项:
1 2 3 -c : 以字符为单位进行分割,截取-d : 自定义分隔符,默认为制表符\t-f : 与-d 一起使用,指定截取哪个区域
2.2 需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 .分割以 :为分隔符第一列的内容cut -d ':' -f 1 passwd 2 .以 :为分割符 第1 列和第5 列的内容,显示前10 行cut -d ':' -f 1 ,5 passwd | head 3 .截取第2 到第5 个字符cut -c 2 -5 passwd 4 .截取第1 个字符cut -c 1 passwd 5 .截取第10 个字符以后的内容cut -c 10 - passwd 6 .显示当前系统的运行级别runlevel | cut -d ' ' -f 2 runlevel | cut -c 3
tee 用法 语法:tee工具是从标准输入读取并写入到标准输出和文件,即:双向覆盖重定向(屏幕输出|文本输入)
3.1 常见选项
1 2 3 选项: -a :双向追加重定向注:不加 -a 默认是覆盖的
3.2 需求
1 2 3 4 5 6 7 8 1 .查看主机的网络配置文件并保存到 network_config.txtgrep -v '^#' /etc/sysconfig/network-scripts /ifcfg-ens33 | grep -v '^$' | tee network_config.txt 2 .查看主机的广播地址并保存到 broadcast.txtifconfig | grep 'broadcast' | cut -d ' ' -f 15 ,16 | tee broadcast.txt 3 .查看主机的子网掩码并追加到 broadcast.txtifconfig | grep 'broadcast' | cut -d ' ' -f 12 ,13 | tee -a broadcast.txt
sort 用法 语法:sort工具用于排序;它将文件的每一行作为一个单位,从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。
4.1 常见选项
1 2 3 4 5 6 7 8 -u :去除重复行-r :降序排列,默认是升序-o : 将排序结果输出到文件中,类似重定向符号>-n :以数字排序,默认是按字符排序-t :分隔符-k :第N列-b :忽略前导空格。-R :随机排序,每次运行的结果均不同
4.2 需求
1 2 3 4 5 1 . 按照用户的uid进行升序排列sort -n -t ':' -k 3 passwd2 .按照用户的uid进行降序排列sort -nr -t ':' -k 3 passwd
uniq 用法 语法:uniq用于去除 连续的 重复行
5.1 常见选项
1 2 3 -i : 忽略大小写-c : 统计重复行次数-d :只显示重复行
diff 用法 语法:逐行比较两个文件的不同,即怎样改变第一个文件才能和第二个文件相同。
两个文件的内容相同的时候就不出结果
6.1 常见选项
1 2 3 4 5 6 7 -b :不检查空格-B :不检查空白行-i :不检查大小写-w :忽略所有的空格--normal :正常格式显示(默认)-c :上下文格式显示-u :合并格式显示
6.2 举例说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 vim file1 添加内容: aaaa 111 hello world 222 333 bbb vim file2 添加内容: aaa hello 111 222 bbb 333 world
6.2.1 diff 正常格式显示即 —normal (默认)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 结果显示: (base) [root @master test ] 1 c1,2 第一个文件(file1)的第1 行需改变(c=change)和第二个文件(file2)的 1 至2 行匹配< aaaa “<” 即左边的文件(file1)内容 aaaa --- 分割线> aaa “>” 即右边文件(file2)的内容 > hello 3 d3 第一个文件的第3 行需删除(d=delete)和第二个文件(file2)的第 3 行匹配< hello world 文件左边的内容 即删除 hello world 5 d4 第一个文件的第5 行需删除(d=delete)和第二个文件(file2)的第 4 行匹配< 333 文件左边的内容 即删除 333 6 a6,7 第一个文件的第6 行需添加(a=add)和第二个文件(file2)的第 6 ,7 行匹配> 333 添加的内容是第二个文件6 ,7 行的 333 和 world > world
6.2.2 diff 上下文格式显示 -c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 结果显示: (base) [root @master test ] 下面两行显示了文件名,时间戳; *** 表示前一个文件即file1 --- 表示后一个文件即 file2 下面两个部分都是在 file1的角度,指如何改变file1 才能和file2 一样 *** file1 2022 -07-03 09 :51 :21.046292165 +0800 --- file2 2022 -07-03 09 :51 :39.631278483 +0800 *************** 分隔符 *** 1 ,6 **** 以***开头的表示file1 数字1 ,6 表示1 到6 行 ! aaaa ! 表示此行需要修改才可以与file2 匹配 111 - hello world - 表示此行需要删除才可以与file2 匹配 222 - 333 - 表示此行需要删除才可以与file2 匹配 bbb --- 1 ,7 ---- 以--- 开头的表示file2 数字1 ,7 表示1 到7 列! aaa 表示第一个文件需要修改才与第二个文件匹配 ! hello 表示第一个文件需要修改才与第二个文件匹配 111 222 bbb + 333 表示第一个文件需要加上该行才与第二个文件匹配 + world 表示第一个文件需要加上该行才与第二个文件匹配
6.2.3 diff 合并模式 -u
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 结果显示: (base) [root @master test ] --- 指的是第一个文件即 file1 +++ 指的是第二个文件即file2 数字是时间戳 --- file1 2022 -07-03 09 :51 :21.046292165 +0800 +++ file2 2022 -07-03 09 :51 :39.631278483 +0800 @ @ -1 ,6 +1 ,7 @ @ -aaaa 第一个文件需要 删除 aaaa+aaa 第一个文件 添加 aaa +hello 第一个文件 添加 hello 111 111 不变 -hello world 第一个文件 删除 hello world 222 222 不变 -333 第一个文件 删除 333 bbb bbb不变 +333 第一个文件 添加 333 +world 第一个文件 添加 world
6.3 diff 工具比较两个目录的不同(注:此功能较常用)
数据准备:
1 2 3 4 5 6 7 8 9 10 11 12 (base) [root @master test ] (base) [root @master test ] (base) [root @master test ] (base) [root @master test ] (base) [root @master test ] dir1: file1 file2 file3 file4 file5 dir2: file1 file2 file3 test1 test2 (base) [root @master test ] 文件里面随便添加一些数据
6.3.1 比较两个文件目录的不同,也会比较两个文件里面文件内容的不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (base) [root @master test ] diff dir1/file1 dir2/file11 c1< aa --- > fja diff dir1/file2 dir2/file21 d0< aa diff dir1/file3 dir2/file31 d0< afdas Only in dir1: file4 Only in dir1: file5 Only in dir2: test1 Only in dir2: test2
6.3.1 只比较文件目录的不同不比较文件内容不同 -q
1 2 3 4 5 6 7 8 (base) [root @master test ] Files dir1/file1 and dir2/file1 differ Files dir1/file2 and dir2/file2 differ Files dir1/file3 and dir2/file3 differ Only in dir1: file4 Only in dir1: file5 Only in dir2: test1 Only in dir2: test2
6.4 打补丁
diff 找出文件的不同的目的是变相同,以一个文件为标准,去修改其他文件,并且修改的地方较多时,我们可以通过打补丁的方式完成。
1 2 3 4 5 6 7 8 9 10 1 .先找出两个文件的不同输出到另一个文件中diff -uN file1 file2 | tee file.patch-u :合并模式-N :将不存在的文件当作空文件2 .将不同的内容打补丁到 file1patch file1 file.patch 3 .测试验证(注:内容为空则修改完成)diff file1 file2
paste用法 用法:paste工具用于合并文件行,并不改变原文件
7.1 常见选项
1 2 -d :自定义间隔符,默认是tab-s :串行处理,非并行
7.2 举例说明
1 2 3 4 5 echo 'file1的第一行内容' > file1echo 'file1的第二行内容' >> file1echo 'file2的第一行内容' > file2echo 'file2的第二行内容' >> file2echo 'file2的第三行内容' >> file2
7.2.1 默认分隔符
1 2 3 4 5 6 结果显示: (base) [root @master test ] file1的第一行内容 file2的第一行内容 file1的第二行内容 file2的第二行内容 file2的第三行内容 (base) [root @master test ]
7.2.1 以 : 作为分隔符
1 paste -d ':' file1 file2
1 2 3 4 5 6 结果显示: (base) [root @master test ] file1的第一行内容:file2的第一行内容 file1的第二行内容:file2的第二行内容 :file2的第三行内容 (base) [root @master test ]
7.2.2 串行合并
1 paste -d ':' -s file1 file2
1 2 3 4 5 结果显示: (base) [root @master test ] file1的第一行内容:file1的第二行内容 file2的第一行内容:file2的第二行内容:file2的第三行内容 (base) [root @master test ]
tr 用法 用法:tr用于字符转换,替换和删除;主要用于 删除文件中控制字符 或 进行字符转换
1 2 3 4 5 6 7 8 1 、命令的执行结果交给tr处理,其中string1用于查询,string2用于转换处理commands|tr 'string1' 'string2' 2 、tr处理的内容来自文件,记住要使用"<" 标准输入tr 'string1' 'string2' < filename 3 、匹配string1进行相应操作,如删除操作tr [options ] 'string1' < filename
8.1 常见选项
1 2 -d :删除字符串1 中所有输入字符。-s :删除所有重复出现字符序列,只保留第一个;即将重复出现字符串压缩为一个字符串
1 2 3 4 5 6 7 8 9 10 a-z 或 [:lower :] : 匹配所有小写字母 A-Z 或 [:upper :] : 匹配所有大写字母 0 -9 或 [:digit :] : 匹配所有数字 [a -zA -Z0 -9 ] : 所有大小写和数字 [:alnum :] : 匹配所有字母和数字 [:alpha :] : 匹配所有字母 [:blank :] : 所有水平空白 [:punct :] : 匹配所有标点符号 [:space :] : 所有水平或垂直的空格 [:cntrl :] : 匹配所有的控制字符如:\f 走行换页 \n 换行 \r 回车 \t tab键
8.2需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 cp /etc/passwd .1 .找到文件中所有的小写字母替换为大写字母cat passwd | tr 'a-z' 'A-Z' tr '[:lower:]' '[:upper:]' < passwd 2 .找到文件中所有的数字替换成为 @ cat passwd | tr '0-9' '@' tr '[:digit:]' '@' < passwd 3 .找到文件中的所有的 : 和 / 替换为 cat passwd | tr ':/' '#' tr ':/' '#' < passwd 4 .删除文件中的所有小写字母cat passwd | tr -d 'a-z' tr -d '[:lower:]' < passwd 6 .查看系统的内置模块,并且按照模块的大小进行升序排列lsmod | tr -s ' ' | sort -n -t ' ' -k 2 7 .查看本机网卡 ens33 的 IP,NETMASK,BROADCAST 并存储到net_confifconfig ens33 | grep 'netmask' | tr -s ' ' | cut -d ' ' -f 2 -7 | tr ' ' '\n' | tee net_conf
bash 特性 命令和文件自动补全 Tab只能补全 命令 和 文件
1.1 常见的快捷键
1 2 3 4 5 6 7 8 9 ^c 终止前台运行的程序 ^z 将前台运行的程序挂起到后台 ^d 退出 等价exit ^l 或 clear 清屏 ^a |home 光标移到命令行的最前端 ^e |end 光标移到命令行的后端 ^u 删除光标前所有字符 ^k 删除光标后所有字符 ^r 搜索历史命令
1.2 常用的通配符(重点)
1 2 3 4 5 6 7 8 9 10 * : 匹配0 或多个任意字符 ? : 匹配任意单个字符 [list ] : 匹配[list ]中的任意单个字符,或者一组单个字符 [a -z ] [!list ] : 匹配除list中的任意单个字符 {string1,string2,...} :匹配string1,string2或更多字符串
1.3 bash中的引号(重点)
双引号”” :会把引号的内容当成整体来看待,允许通过$符号引用其他变量值 单引号’’ :会把引号的内容当成整体来看待,禁止引用其他变量值,shell中特殊符号都被视为普通字符 反撇号`` :反撇号和$()一样,引号或括号里的命令会优先执行,如果存在嵌套,反撇号不能用 1 2 3 4 5 1 .把 passwd 文件做备份,要求看出备份的日期cp /etc/passwd ./passwd_`date +%F_%H :%M:%S`.bakcp /etc/passwd ./passwd_$ (date +%F_%H :%M:%S).bak(base) [root @master test ] passwd_2022-07-04_17 :31 :00 .bak passwd_2022-07-04_17 :31 :28 .bak
shell 初识 第一个shll 脚本 1 2 3 4 5 6 7 8 9 10 11 echo "HELLO WORLD!!!!"
1.1 执行方式
1.1.1 标准执行(必须有可执行权限)
1 2 3 4 5 给执行的脚本添加一个可执行的权限!后面加脚本的相对路径或者绝对路径 chmod +x first_shell.sh ./first_shell.sh /root/test/first_shell.sh
1.1.2 在命令行指定解释器执行(可以没有可执行权限)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 bash first_shell.sh sh first_shell.sh ------------------------------ -x :一般用于排错,查看脚本的执行过程-n :用来查看脚本的语法是否有问题------------------------------ (base) [root @master test ] + echo 'HELLO WORLD!!!!' HELLO WORLD!!!! (base) [root @master test ] (base) [root @master test ]
1.1.3 source 重新读取文件的内容(可以没有可执行权限)
写一个 shell 脚本 需求:拷贝 /etc/passwd 的内容到 /test 目录下,拷贝文件的命名需要显示时间,运行此脚本的时候显示运行脚本的时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 vim cp_passwd.sh cp /etc/passwd /root/test/passwd_$ (date +%F_%H :%M:%S).bakecho -e "\033[32m --------------------------------------------------- \033[0m" echo -e "\033[32m 您运行此脚本的时间是$ (date +%F_%H:%M:%S)!!! \033[0m" echo -e "\033[32m --------------------------------------------------- \033[0m" ls
变量的定义 变量:变量是用来临时保存数据的,该数据是可以变化的数据。
定义变量 1 2 3 4 变量名=变量值 变量名:用来临时保存数据的 变量值:就是临时的可变化的数据
1 2 3 4 5 6 7 8 # 定义变量,调用变量 A=25 echo "${A}" # 取消变量 unset A echo "${A}"
变量名定义规则 2.1 变量名严格区分大小写
1 2 3 4 5 6 7 A=hello a=world (base) [root @master test ] hello (base) [root @master test ] world
2.2 变量名不能有特殊符号
命名要求:命名只能使用英文字母,数字和下划线,首个字符不能以数字开头 。 中间不能有空格 ,可以使用下划线(_)。不能使用标点符号 。 不能使用bash里的关键字 (可用help命令查看保留关键字)。
注:对于有空格的字符串给变量赋值时,要用引号引起来
合法变量 非法变量 A=10 \t=10 a=10 *a=10 A_A=10 5=10 A_=10 A =10 a_=10 A-=10 _a=10 A A=10 _A=10 5A=10 ABC=10 A = 10 a A=hello world A_ =10 ……… ……….
变量名最好做到见名知意
1 2 3 4 5 好的变量名: IPADDR=192.168 .0.0 DIR =/export/serverTMP_FILE=/var/log/1 .log .......
变量名的定义方式 3.1 基本方式
直接赋值
1 2 3 4 5 6 7 8 9 (base) [root @master test ] (base) [root @master test ] 3456 表示从A变量中第3 个字符开始截取,截取4 个字符(base) [root @master test ] 说明: $ 变量名 和 $ {变量名}的异同相同点:都可以调用变量 不同点:$ {变量名}可以只截取变量的一部分,而$ 变量名不可以
3.2 命令执行结果赋值给变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1 .时间(base) [root @master test ] (base) [root @master test ] 2022 -07-04_21 :21 :51 (base) [root @master test ] 2 .主机名(base) [root @master test ] (base) [root @master test ] master (base) [root @master test ] 3 .内核版本(base) [root @master test ] (base) [root @master test ] 3.10 .0 -1160 .el7.x86_64(base) [root @master test ] 4 .系统版本(base) [root @master test ] (base) [root @master test ] CentOS Linux release 7.9 .2009 (Core) (base) [root @master test ]
3.3 交互式变量
目的:让 用户自己 给变量赋值,比较灵活。
语法:
3.3.1 常见选项
1 2 3 4 -p : 定义提示用户的信息 -n : 定义字符数(限制变量值的长度) -s : 不显示(不显示用户输入的内容) -t : 定义超时时间,默认单位为秒(限制用户输入变量值的超时时间)
3.3.2 例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 1 .默认(base) [root @master test ] Jermyn (base) [root @master test ] Jermyn (base) [root @master test ] 2 .提示(base) [root @master test ] 请输入您的姓名:Jermyn (base) [root @master test ] Jermyn (base) [root @master test ] 3 .不显示(base) [root @master test ] (base) [root @master test ] 123456 (base) [root @master test ] 4 .定义时间(base) [root @master test ] 123456 (base) [root @master test ] 123456 (base) [root @master test ] 5 .定义字符数(base) [root @master test ] Please input your name:Jermy(字符超过5 个的时候自动跳转)(base) [root @master test ] 注:read -s -p "" -p 后面跟内容 不可写作 read -p -s ""
变量值还以来自于文件
1 2 3 4 5 6 7 8 (base) [root @master test ] (base) [root @master test ] (base) [root @master test ] (base) [root @master test ] 192.168 .0.0 (base) [root @master test ] 255.255 .255.0 (base) [root @master test ]
3.4 定义有类型的变量
目的: 给变量做一些限制,固定变量的类型,比如:整型、只读
用法:
3.4.1 常用选项
选项 释义 举例 -i 将变量看成整数(只可赋值整数,其他数据类型为0) declare -i A=123 -r 定义只读变量(不可重新赋值,不可 unset ) declare -r B=hello -a 定义普通数组;查看普通数组 -A 定义关联数组;查看关联数组 -x 将变量通过环境导出 declare -x AAA=123456 等于 export AAA=123456
变量的分类 4.1 本地变量
当前用户自定义的变量。当前进程中有效,其他进程及当前进程的子进程无效。 4.2 环境变量
当前进程 有效,并且能够被子进程 调用。
env
查看当前用户的环境变量set
查询当前用户的所有变量(临时变量与环境变量)export 变量名=变量值
或者 变量名=变量值;export 变量名
定义一个环境变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1 .直接定义declare -x NAME=Jermyn 2 .定义一个本地变量,转换为环境变量NAME=Jermyn export NAME export NAME=Jermyn 3 .想要当前登陆的用户生效的话,修改/root/.bashrc 文件内容vim /root/.bashrc export NAME=Jermyn 3 .永久生效,配置 profile 文件vim /etc/profile export NAME=Jermyn 4 .配置 HADOOP 的环境变量export HADOOP_HOME=/export/server/hadoop-3 .3.0 export PATH=$PATH: $HADOOP_HOME /bin:$HADOOP_HOME /sbin
4.3 全局变量
全局所有的用户和程序都能调用,且继承,新建的用户也默认能调用。
4.3.1 配置文件
文件名 说明 备注 $HOME/.bashrc 当前用户的bash信息,用户登录时读取 定义别名、umask、函数等 $HOME/.bash_profile 当前用户的环境变量,用户登录时读取 $HOME/.bash_logout 当前 用户退出 当前shell时最后读取 定义用户退出时执行的程序等 /etc/bashrc 全局的bash信息,所有用户都生效 /etc/profile 全局环境变量信息 系统和所有用户都生效 $HOME/.bash_history 用户的历史命令 history -w 保存历史记录 history -c 清空历史记录
注:修改后都需要 source 执行,重现加载环境变量才可以生效
4.3.2 用户登录系统读取相关文件的顺序
/etc/profile
$HOME/.bash_profile
$HOME/.bashrc
/etc/bashrc
$HOME/.bash_logout
4.4 系统变量
shell本身已经固定好了它的名字和作用.
内置变量 含义 $? 上一条命令执行后返回的状态;状态值为0表示执行正常 ,非0表示执行异常 或错误 $0 当前执行的程序或脚本名 $# 脚本后面接的参数的个数 $* 脚本后面所有参数 ,参数当成一个整体输出,每一个变量参数之间以空格隔开 $@ 脚本后面所有参数,参数是独立的,也是全部输出 $1~$9 脚本后面的位置参数,$1表示第1个位置参数,依次类推 ${10}~${n} 扩展位置参数,第10个位置变量必须用{}大括号括起来(2位数字以上扩起来) $$ 当前所在进程的进程号,如echo $$
$! 后台运行的最后一个进程号 (当前终端) !$ 调用最后一条命令历史中的参数
区分 $* 和 $@ 的区别
$*
:表示将变量看成一个整体$@
:表示变量是独立的
1 2 3 4 5 6 例子: 运行 ./demo.sh 1 2 3 $ * 是指 1 2 3 $ @ 是指 1 2 3
简单的四则运算 算数运算:默认情况下,只支持简单的整数运算
算数内容:加(+),减(-),乘(*),除(/),取余(%)
四则运算符号 表达式 举例 $(()) echo “$((1+1))” $[] echo “$[1+1]”; expr expr 1 + 1 (必须有空格 );expr 10 * 5(* 需要转义 );expr 5 % 10 注:不可求次幂 let n=1;let n+=1;echo “${n}”
默认支持整数运算,特殊支持小数
1 2 3 4 5 6 7 8 9 (base) [root @master shell_scripts ] 2.5 (base) [root @master shell_scripts ] 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+1.5 2.5
i++ 和 ++i 1 2 i++ :先赋值,在计算 ++i :先计算,在赋值
数组定义 3.1 数组分类
3.2 普通数组定义
1 2 3 4 5 6 数组名[索引下标]=值 array[0 ]=v1 array[1 ]=v2 array[2 ]=v3 array[3 ]=v4
1 2 3 4 5 6 7 8 9 10 11 数组名=(值1 值2 值3 ...) array=(var1 var2 var3 var4) array1=(`cat /etc/passwd`) 将文件中每一行赋值给array1数组 array2=(`ls /root`) array3=(harry amy jack "Miss Hou" ) array4=(1 2 3 4 "hello world" [10 ]=linux)
3.3 数组的读取
1 2 3 4 5 6 7 8 9 10 $ {数组名[元素下标]} echo $ {array[0 ]} 获取数组里第一个元素echo $ {array[*]} 获取数组里的所有元素echo $ {echo $ {!array[@ ]} 获取数组元素的索引下标echo $ {array[@ ]:1 :2 } 访问指定的元素;1 代表从下标为1 的元素开始获取;2 代表获取后面几个元素查看普通数组信息: (base) [root @master ~]
3.4 关联数组定义
1 2 3 declare -A asso_array1 declare -A asso_array2 declare -A asso_array3
数组赋值
一次赋一个值
一次赋多个值
1 # asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="Jermyn")
查看关联数组
1 2 3 declare -A asso_array1='([php]="three" [java]="two" [linux]="one" )' declare -A asso_array2='([name3]="amy" [name2]="jack" [name1]="harry" [name4]="Jermyn" )'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 one three three two one php java linux 3 4 name3 name2 name1 name4
3.5 其他变量定义
取出一个目录下的目录和文件:dirname
和 basename
1 2 3 4 5 6 7 8 (base) [root @master shell_scripts ] (base) [root @master shell_scripts ] /root/test/shell_scripts/cp_passwd.sh (base) [root @master shell_scripts ] /root/test/shell_scripts (base) [root @master shell_scripts ] cp_passwd.sh (base) [root @master shell_scripts ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 一个“%”代表从右往左删除 两个“%%”代表从右往左去掉最多 一个“ 两个“ 举例说明: (base) [root @master ~] (base) [root @master ~] 14 (base) [root @master ~] taobao.com (base) [root @master ~] bao.com (base) [root @master ~] com (base) [root @master ~] www.taobao.com (base) [root @master ~] www.taobao (base) [root @master ~] www.taobao.c (base) [root @master ~] www (base) [root @master ~] www.ta (base) [root @master ~]
1 2 3 4 5 替换:/ 和 // (base) [root @master ~] www.tAObao.com (base) [root @master ~] www.tAObAO.com
条件判断语句 条件判断语句 1.1 语法格式
1 2 3 1 . test 条件表达式2 . [ 条件表达式 ] 注:表达式左右两边都有空格3 . [[ 条件表达式 ]] 注:支持正则,表达式左右两边都有空格
条件判断相关参数 判断文件类型,判断文件新旧,判断字符串是否相等,判断权限等等
2.1 判断文件类型
判断参数 含义 -e 判断文件是否存在 (任何类型文件) -f 判断文件是否存在并且是一个普通文件 -d 判断文件是否存在并且是一个目录 -L 判断文件是否存在并且是一个软连接文件 -b 判断文件是否存在并且是一个块设备文件 -S 判断文件是否存在并且是一个套接字文件 -c 判断文件是否存在并且是一个字符设备文件 -p 判断文件是否存在并且是一个命名管道文件 -s 判断文件是否存在并且是一个非空文件(有内容)
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (base) [root @master server ] anaconda3 hbase-1 .2.4 kafka-eagle spark-3 .2.0 -bin-hadoop3 .2 apache-zookeeper-3 .7.0 -bin jdk kafka-eagle-bin-master zookeeper data jdk1.8.0 _241 kafka-eagle-web-2 .0.2 zookeeper.outhadoop-3 .3.0 kafka mysql57-community-release-el7-10 .noarch.rpm hbase kafka_2.11 -2 .0.0 spark (base) [root @master server ] (base) [root @master server ] 0 (base) [root @master server ] (base) [root @master server ] 0 (base) [root @master server ] (base) [root @master server ] 1
2.2 判断文件的权限
判断参数 含义 -r 当前用户对其是否可读 -w 当前用户对其是否可写 -x 当前用户对其是否可执行 -u 是否有suid,高级权限冒险位 -g 是否sgid,高级权限强制位 -k 是否有t位,高级权限粘滞位(只有文件的创建者,管理员,root可以删除)
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 (base) [root @master shell_scripts ] total 8 -rwxr-xr-x 1 root root 768 Jul 4 18 :49 cp_passwd.sh-rw-r--r-- 1 root root 1679 Jul 5 16 :32 font_style.sh(base) [root @master shell_scripts ] 1 (base) [root @master shell_scripts ] (base) [root @master shell_scripts ] total 8 -rwxr-xr-x 1 root root 768 Jul 4 18 :49 cp_passwd.sh-rwxr-xr-x 1 root root 1679 Jul 5 16 :32 font_style.sh(base) [root @master shell_scripts ] 0
2.3 判断文件新旧(修改时间的新旧)**
判断参数 含义 file1 -nt file2 比较file1是否比file2新 file1 -ot file2 比较file1是否比file2旧 file1 -ef file2 比较是否为同一个文件,或者用于判断硬连接,是否指向同一个inode
举例
1 2 3 4 (base) [root @master shell_scripts ] 1 (base) [root @master shell_scripts ] 0
2.4 判断整数
判断参数 含义 -eq 相等 -ne 不等 -gt 大于 -lt 小于 -ge 大于等于 -le 小于等于
举例
1 2 3 4 5 6 (base) [root @master shell_scripts ] 1 (base) [root @master shell_scripts ] 0 (base) [root @master shell_scripts ] 1
2.5 判字符串
判断参数 含义 -z 判断是否 为空 字符串,字符串长度为0则成立 -n 判断是否为 非空 字符串,字符串长度不为0则成立 string1 = string2 判断字符串是否相等 string1 != string2 判断字符串是否相不等
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 (base) [root @master shell_scripts ] 1 (base) [root @master shell_scripts ] 1 (base) [root @master shell_scripts ] 0 (base) [root @master shell_scripts ] (base) [root @master shell_scripts ] 0 (base) [root @master shell_scripts ] 1
2.6 多重条件判断
判断符号 含义 举例 -a 和 && 逻辑与 [ 1 -eq 1 -a 1 -ne 0 ] [ 1 -eq 1 ] && [ 1 -ne 0 ] -o 和 \ \ 逻辑或 [ 1 -eq 1 -o 1 -ne 1 ]
&& 前面的表达式为真,才会执行后面的代码(1&&1 —>TRUE)
|| 前面的表达式为假,才会执行后面的代码(1||0—>TRUE)
; 只用分割 命令或表达式
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 .判断用户是否是 root 用户,是的话打印"THE USER IS ADMIN" (base) [root @master shell_scripts ] THE USER IS ADMIN (base) [root @master shell_scripts ] THE USER IS ADMIN 2 .是 root 的话打印是管理员,不是的话打印不是[ $ (id -u ) -eq 0 ] && echo "THE USER IS ADMIN" || echo "THE USER IS NOT ADMIN" 3 .(base) [root @master ~] BBB CCC (base) [root @master ~] AAA CCC (base) [root @master ~] AAA (base) [root @master ~] AAA BBB (base) [root @master ~] AAA BBB CCC
字符串的比较
1 2 3 4 5 注意:双引号引起来,看作一个整体;= 和 == 在 [ 字符串 ] 比较中都表示判断 (base) [root @master ~] 0 (base) [root @master ~] 1
2.7 [ 条件 ]
和 [[ 条件 ]]
的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 []和test 两者是一样的,在命令行里test expr和[ expr ]的效果相同。 test的三个基本作用是判断文件、判断字符串、判断整数。支持使用 ”与或非“ 将表达式连接起来。 test中可用的比较运算符只有==和!=,两者都是用于字符串比较的,不可用于整数比较,整数比较只能使用-eq, -gt这种形式。 无论是字符串比较还是整数比较都千万不要使用大于号小于号。当然,如果你实在想用也是可以的,对于字符串比较可以使用尖括号的转义形式, 如果比较"ab"和"bc":[ ab \< bc ],结果为真,也就是返回状态为0. [[ ]] 这是内置在shell中的一个命令,它就比刚才说的test强大的多了。支持字符串的模式匹配(使用=~操作符时甚至支持shell的正则表达 式)。逻辑组合可以不使用test的-a,-o而使用&& ||。 字符串比较时可以把右边的作为一个模式(这是右边的字符串不加双引号的情况下。如果右边的字符串加了双引号,则认为是一个文本字符串。),而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真。 注意:使用[]和[[]]的时候不要吝啬空格,每一项两边都要有空格,[[ 1 == 2 ]]的结果为“假”,但[[ 1==2 ]]的结果为“真”! let和(()) 两者也是一样的(或者说基本上是一样的,双括号比let稍弱一些)。主要进行算术运算(上面的两个都不行),也比较适合进 行整数比较,可以直接使用熟悉的<,>等比较运算符。可以直接使用变量名如var而不需要$var这样的形式。支持分号隔开的多个表达式 1. 首先,尽管很相似,但是从概念上讲,二者是不同层次的东西。 "[[",是关键字,许多shell(如ash bsh)并不支持这种方式。ksh, bash(据说从2.02起引入对[[的支持)等支持。 "["是一条命令, 与test等价,大多数shell都支持。在现代的大多数sh实现中,"["与"test"是内部(builtin)命令,换句话说执行"test"/"["时不会调用/some/path/to/test这样的外部命令(如果有这样的命令的话)。 2.[[]]结构比Bash版本的[]更通用。在[[和]]之间的所有的字符都不会被文件扩展或是标记分割,但是会有参数引用和命令替换。 用[[ ... ]]测试结构比用[ ... ]更能防止脚本里的许多逻辑错误。比如说,&&,||,<和>操作符能在一个[[]]测试里通过,但在[]结构会发生错误。 3.(( ))结构扩展并计算一个算术表达式的值。如果表达式值为0,会返回1或假作为退出状态码。一个非零值的表达式返回一个0或真作为退出状态码。这个结构和先前test命令及[]结构的讨论刚好相反。 4.[ ... ]为shell命令,所以在其中的表达式应是它的命令行参数,所以串比较操作符">" 与"<"必须转义,否则就变成IO改向操作符了(请参看上面2中的例子)。在[[中"<"与">"不需转义; 由于"[["是关键字,不会做命令行扩展,因而相对的语法就稍严格些。例如 在[ ... ]中可以用引号括起操作符,因为在做命令行扩展时会去掉这些引号,而在[[ ... ]]则不允许这样做。 5.[[ ... ]]进行算术扩展,而[ ... ]不做 6.[[ ... && ... && ... ]] 和 [ ... -a ... -a ...] 不一样,[[ ]] 是逻辑短路操作,而 [ ] 不会进行逻辑短路
1 2 3 4 5 6 7 (base) [root @master ~] -bash : [: missing `]' 2 (base) [root@master ~]# [ 1 -eq 0 -a 1 -ne 0 ];echo $? 1 (base) [root@master ~]# [[ 1 -eq 0 && 1 -ne 0 ]];echo $? 1
流程控制语句(if….) 基本语法结构 1.1 if 结构
语法:只要满足条件就一直执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if [ condition ];then command command fi if test 条件;then 命令 fi if [[ 条件 ]];then 命令 fi [ 条件 ] && command
1.2 if…else 结构
语法:判断是否满足条件,满足走一条,不满足走另一条
1 2 3 4 5 6 7 if [ condition ];then command1 else command2 fi [ 条件 ] && command1 || command2
1.3 if…elif…else 结构
语法:选择的有很多,能走的只有一条
1 2 3 4 5 6 7 8 9 if [ condition1 ];then command1 elif [ condition2 ];then command2 else command3 fi 注释: 如果条件1 满足,执行命令1 后结束;如果条件1 不满足,再看条件2 ,如果条件2 满足执行命令2 后结束;如果条件1 和条件2 都不满足执行命令3 结束.
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 1 .判断当前主机是否可以连接远程主机read -p "请输入您的 IP 或者 域名:" IP ping $ {IP} -c1 >/dev/null if [[ $ ? -eq 0 ]];then echo -e "\033[32m 您的主机网络可以与$ {IP}联通. \033[0m" else echo -e "\033[31m 您的主机网路不可以与$ {IP}联通!!! \033[0m" fi read -p "INPUT YOUR IP :" IP ; ping -c3 $ {IP} > /dev/null ; [[ $ ? -eq 0 ]] && echo "YOUR NETOWRK IS OK" || echo "YOUR NETTWORK IS ERROR" 2 .判断一个门户网站是否正常(直接去访问一下,通过访问成功和失败的返回值来判断) wget www.baidu.com curl www.baidu.comelinks -dump www.baidu.com 脚本: read -p "Please input your IP or Domain Name:" web_server wget $ {web_server} &>/dev/nullif [ $ ? -eq 0 ];then test -d /root/test/index || mkdir /root/test/index mv ./index.html /root/test/index/index_$ (date +%F_%T).html echo -e "\033[32m The current website service is normal \033[0m" else echo -e "\033[31m The network service of the current website is abnormal! \033[0m" fi
条件判断语句(for….) for循环语法结构 1.1 列表循环
语法:用于将一组命令执行已知的次数
1 2 3 4 5 6 7 8 9 10 11 12 for variable in {list} do command command … done 或者 for variable in a b c do command command done
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 1 .打印0 -50 for i in {0 ..50 };do echo "$ {i}" ;done2 .打印0 -50 的偶数for i in {0 ..50 ..2 };do echo "$ {i}" ;done3 .打印 10 -1 for i in {10 ..1 };do echo "$ {i}" ;done4 .打印 1 -10 for i in $ (seq 10 );do echo "$ {i}" ;done5 .打印10 -1 for i in $ (seq 10 -1 1 );do echo "$ {i}" ;done
1.2 不带列表循环
语法:不带列表的for循环执行时由 用户指定参数和参数的个数
1 2 3 4 5 6 for variable do command command … done
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 vim demo.sh for ido echo "$ {i}" done (base) [root @master shell_scripts ] a b c (base) [root @master shell_scripts ] abc 解释: (base) [root @master shell_scripts ] + for i in '"$@"' for i 是 for i in '"$@"' 的省略 + echo a a + for i in '"$@"' + echo b b + for i in '"$@"' + echo c c
1.3 案例
1 2 3 4 5 6 7 8 9 10 11 1 . 1 -100 的奇数和vim sum_odd_number.sh sum=0 for i in {1 ..100 ..2 }do sum=$ [ $sum +$i ] done echo "1-100的奇数和是:$ {sum}"
1.4 循环控制语句
循环体: do….done之间的内容
continue:继续;表示 循环体 内下面的代码不执行,重新开始下一次循环 break:打断;马上停止执行本次循环,执行 循环体 后面的代码 exit:表示直接跳出程序 例子:
1.判断输入整数是否是质数(只能被1和它本身整除=的数叫质数。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 read -p "Please Input A Int Number:" number [ $number -eq 1 ] && echo "$number neither prime number nor composite number" $ $ exit [ $number -eq 2 ] && echo "$number is prime number " $ $ exit for i in $ (seq 2 $ [$number -1 ])do if [[ $number %$i -eq 0 ]];then echo " $number is not a prime number!" exit fi done echo " $number is a prime number!"
批量加5个新用户,以u1到u5命名,并统一加一个新组,组名为 class ,统一改密码为123
添加用户的命令 useradd -G development
判断class组是否存在 grep -w ^development /etc/group
或者groupadd development
根据题意,判断该脚本循环5次来添加用户 for
给用户设置密码,应该放到循环体里面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 read -p "请输入您创建的用户组:" group read -p "请输入您创建的用户名:" user_name read -s -p "请输入您统一的密码:" password grep -w "^$ {group}" /etc/group &>/dev/null [ $ ? -ne 0 ] && useradd $ {group } for i in {1 ..5 }do useradd -G $ {group } $ {user_name}$ {i} echo "$ {password}" |passwd --stdin $ {user_name}$ {i} done
1 2 3 4 5 6 7 8 9 10 11 12 13 结果测试: [root @Jermyn ~] uid=1001 (employe1) gid=1001 (employe1) groups=1001 (employe1),1000 (development) [root @Jermyn ~] Last login: Thu Jul 7 20 :39 :30 CST 2022 on pts/0 [employe1 @Jermyn ~]$ su - employe2 Password: Last login: Thu Jul 7 20 :39 :42 CST 2022 on pts/0 [employe2 @Jermyn ~]$ exit logout [employe1 @Jermyn ~]$ exit logout [root @Jermyn ~]
写一个脚本,局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里(以10.1.1.1~10.1.1.10 为例) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 read -p "请输入您需要PING的主机的主机位(如:10.1.1):" ip for ((i=1 ;i<=10 ;i++))do ping -c1 $ip .$i &>/dev/null if [ $ ? -eq 0 ];then echo "$ip .$i is ok" >> /tmp/ip_up.txt else echo "$ip .$i is down" >> /tmp/ip_down.txt fi done
1 2 3 4 5 6 7 运行结果: [root @Jermyn shell_scripts ] 请输入您需要PING的主机的主机位(如:10.1 .1 ):192.168 .88 real 0 m28.787 s user 0 m0.012 s sys 0 m0.080 s
因为上述的 检测方式 花费时间太长,所以提供一种并发机制
并行执行:{程序}&表示将程序放到后台并行执行,如果需要等待程序执行完毕再进行下面内容,需要加wait
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 read -p "请输入您需要PING的主机的主机位(如:10.1.1):" ip for ((i=1 ;i<=10 ;i++))do { ping -c1 $ip .$i &>/dev/null if [ $ ? -eq 0 ];then echo "$ip .$i is ok" >> /tmp/ip_up.txt else echo "$ip .$i is down" >> /tmp/ip_down.txt fi }& done wait echo "检测已经完成,可以PING通的主机存放在/tmp/ip_up.txt " echo "检测已经完成,不可以PING通的主机存放在/tmp/ip_down.txt "
1 2 3 4 5 6 7 8 9 结果显示: [root @Jermyn shell_scripts ] 请输入您需要PING的主机的主机位(如:10.1 .1 ):192.168 .88 检测已经完成,可以PING通的主机存放在/tmp/ip_up.txt 检测已经完成,不可以PING通的主机存放在/tmp/ip_down.txt real 0 m6.781 s user 0 m0.016 s sys 0 m0.030 s
条件判断语句(while…&until) while循环 语法:不确定循环换次数;条件为真就进入循环;条件为假就退出循环
1 2 3 4 5 6 7 8 9 10 11 while 表达式 do command... done while [ 1 -eq 1 ] 或者 (( 1 > 2 )) do command command ... done
实例:
1. 脚本同步系统时间
① 具体需求 写一个脚本,30秒 同步一次系统时间,时间同步服务器10.1.1.1 如果同步失败,则进行邮件报警,每次失败都报警 如果同步成功,也进行邮件通知,但是 成功100次 才通知一次 ② 思路 每隔30s同步一次时间,该脚本是一个死循环 while 循环 同步失败发送邮件ntpdate 10.1.1.1 rdate -s 10.1.1.1 同步成功100次发送邮件 定义变量保存成功次数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 NTP=10.1 .1.1 count=0 while truedo ntpdate $NTP &>/dev/null if [ $ ? -ne 0 ];then echo "system date failed" |mail -s "check system date" root@localhost else let count++ if [ $count -eq 100 ];then echo "systemc date success" |mail -s "check system date" root@localhost && count=0 fi fi sleep 30 done count=0 ntp_server=10.1 .1.1 while truedo rdate -s $ntp -server &>/dev/null if [ $ ? -ne 0 ];then echo "system date failed" |mail -s 'check system date' root@localhost else let count++ if [ $ [$count %100 ] -eq 0 ];then dte successfull" |mail -s 'check system date' root@localhost && count=0 fi fi sleep 3 done 以上脚本还有更多的写法,课后自己完成
until 循环 语法:条件为假就进入循环;条件为真就退出循环
1 2 3 4 5 6 until expression [ 1 -eq 1 ] (( 1 >= 1 )) do command command ... done
打印 1-5
1 2 3 4 5 6 7 8 9 10 11 12 13 i=1 while [ $i -le 5 ]do echo $i let i++ done i=1 until [ $i -gt 5 ]do echo $i let i++ done
实例
使用until语句批量创建10个用户,要求stu1—stu5用户的UID分别为1001—1005; stu6~stu10用户的家目录分别在/rhome/stu6—/rhome/stu10 创建用户语句 useradd -u|useradd -d
使用循环语句(until)批量创建用户 until循环语句结构
判断用户前5个和后5个 条件判断语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 if [ -d /rhome ];then echo "/rhome目录已存在" else mkdir /rhome echo "/rhome不存在,已完成创建" fi i=1 until [ $i -gt 10 ]do if [ $i -le 5 ];then useradd -u $ [1000 +$i ] stu$i echo 123 |passwd --stdin stu$i else useradd -d /rhome/stu$i stu$i echo 123 |passwd --stdin stu$i fi let i++ done ================================================== i=1 until [ $i -gt 10 ]do if [ $i -le 5 ];then useradd -u $ [1000 +$i ] stu$i && echo 123 |passwd --stdin stu$i else [ ! -d /rhome ] && mkdir /rhome useradd -d /rhome/stu$i stu$i && echo 123 |passwd --stdin stu$i fi let i++ done
随机数 生成随机数 系统变量 :RANDOM,默认会产生0~32767的随机整数
前言: 要想调用变量,必须加 $
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 1 .随机产生 0 -32767 之间的数字(注:RAMDOM必须大写)echo $RANDOM 2 .随机产生 0 -2 之间的数字(注:产生 0 -X 之间的数字 宗旨就是随机数除以 X+1 后取余数)echo "$ [ $ {RANDOM}%3 ]" 3 .随机产生 0 -99 之间的数字 echo "$ [ $ {RANDOM}%100 ]" 4 .随机产生 50 -99 之间的数字(注:产生 A-B 之间的数字,宗旨就是,产生随机数除以 A+1 后加上 B-A )例如:随机数字为 756 除以 51 商 14 余 42 加上 49 得 91 例如:随机数字为 764 除以 51 商 14 余 50 加上 49 得 99 例如:随机数字为 52 除以 51 商 1 余 1 加上 49 得 50 echo "$ [ $ {RANDOM}%51+49 ]" 5 .随机产生一个一位数echo "$ [ $ {RANDOM}%10 ]" 随机产生一个两位数 echo "$ [ $ {RANDOM}%90+10 ]" 随机产生一个三位数字(注:三位数字 100 -999 ) echo "$ [ $ {RANDOM}%900+100 ]" 随机产生一个四位数字 echo "$ [ $ {RANDOM}%9000+1000 ]"
示例 2.1 具体需求 :写一个脚本,产生一个phone_num.txt文件,随机产生以139开头的手机号1000个,每个一行。
思路:可以每次产生四个,两次产生或者其他,但是如果每个数字单独产生的话,电话容易出现重复的
产生1000个电话号码,脚本需要循环1000次 FOR WHILE UNTIL
139+8位,后8位随机产生,可以让每一位数字都随机产生 echo $[$RANDOM%10]
将随机产生的数字分别保存到变量里,然后加上139保存到文件里 1、for 循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 touch ./phone_number.txt for ((i=0 ;i<=1000 ;i++))do n4=$ [ $RANDOM %10 ] n5=$ [ $RANDOM %10 ] n6=$ [ $RANDOM %10 ] n7=$ [ $RANDOM %10 ] n8=$ [ $RANDOM %10 ] n9=$ [ $RANDOM %10 ] n3=$ [ $RANDOM %10 ] n11=$ [ $RANDOM %10 ] echo "139$ {n4}$ {n5}$ {n6}$ {n7}$ {n8}$ {n9}$ {n10}$ {n11}" >>./phone_number.txt done
2、while 循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 touch ./phone_number.txt count=0 while [ $count -lt 1000 ]do n4=$ [ $RANDOM %10 ] n5=$ [ $RANDOM %10 ] n6=$ [ $RANDOM %10 ] n7=$ [ $RANDOM %10 ] n8=$ [ $RANDOM %10 ] n9=$ [ $RANDOM %10 ] n3=$ [ $RANDOM %10 ] n11=$ [ $RANDOM %10 ] echo "139$ {n4}$ {n5}$ {n6}$ {n7}$ {n8}$ {n9}$ {n10}$ {n11}" >>./phone_number.txt let count++ done
3、while 死循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 touch ./phone_number.txt count=0 while truedo n4=$ [ $RANDOM %10 ] n5=$ [ $RANDOM %10 ] n6=$ [ $RANDOM %10 ] n7=$ [ $RANDOM %10 ] n8=$ [ $RANDOM %10 ] n9=$ [ $RANDOM %10 ] n3=$ [ $RANDOM %10 ] n11=$ [ $RANDOM %10 ] echo "139$ {n4}$ {n5}$ {n6}$ {n7}$ {n8}$ {n9}$ {n10}$ {n11}" >>./phone_number.txt let count++ if [ $count -eq 1000 ];then exit ;fi done
4、until 循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 touch ./phone_number.txt count=1 until [ $count -gt 1000 ]do n4=$ [ $RANDOM %10 ] n5=$ [ $RANDOM %10 ] n6=$ [ $RANDOM %10 ] n7=$ [ $RANDOM %10 ] n8=$ [ $RANDOM %10 ] n9=$ [ $RANDOM %10 ] n3=$ [ $RANDOM %10 ] n11=$ [ $RANDOM %10 ] echo "139$ {n4}$ {n5}$ {n6}$ {n7}$ {n8}$ {n9}$ {n10}$ {n11}" >>./phone_number.txt let count++ done
2.2 具体需求 :随机抽出5位幸运观众
具体需求:
在上面的1000个手机号里抽奖 5个 幸运观众,显示出这5个幸运观众。 但只显示头3个数和尾号的4个数,中间的都用*代替 思路:
确定幸运观众所在的行 0-1000 随机找出一个数字 $[$RANDOM%1000+1]
将电话号码提取出来 head -随机产生行号 phonenum.txt |tail -1
显示 前3个和后4个数到屏幕 echo 139****
1 2 3 4 5 6 7 8 9 >luck_phone_num.txt for ((i=1 ;i<=5 ;i++))do all_user=`wc -l phone_number.txt | cut -d ' ' -f1 ` random_num=$ [ $RANDOM %$all_user +1 ] random_num_phone=$ (head -$random_num phone_number.txt | tail -1 |cut -c '7-' ) echo "139****$random_num_phone " | tee -a luck_phone_num.txt done
2.3 批量创建用户(密码随机产生)
需求: 批量创建5个用户,每个用户的密码为一个随机数
思路: 1.循环5次创建用户
2.产生一个密码文件来保存用户的随机密码
3.从密码文件中取出随机密码赋值给用户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 echo user0{1 ..5 }:jermyn$ [ $RANDOM %9000 +1000 ]@ .. | tr ' ' '\n' | tee -a user_passwd.txtfor ((i=1 ;i<=5 ;i++))do user=`head -$i uesr_passwd.txt | tail -1 | cut -d ':' -f 1 ` passwd=`head -$i uesr_passwd.txt | tail -1 | cut -d ':' -f 2 ` useradd $user echo $pass |passwd --stdin $user done
循环嵌套+shift 位移+补充expect 语法: 一个循环体内又包含另一个完整 的循环结构,称为循环的嵌套。
每次外部循环都会触发内部循环,直至内部循环完成,才接着执行下一次的外部循环。
for循环、while循环和until循环可以相互 嵌套。
1.1 打印九九乘法表
1 2 3 4 5 6 7 8 9 for ((i=1 ;i<=9 ;i++))do for ((j=1 ;j<=$i ;j++)) do echo -ne "$j *$i =$ [ $i *$j ]\t" done echo done
1 2 3 4 5 6 7 8 9 10 11 12 13 i=1 while [ $i -le 9 ]do j=1 while [ $j -le $i ] do echo -ne "$j *$i =$ [ $i *$j ]\t" let j++ done echo let i++ done
1 2 3 4 5 6 7 8 9 10 11 12 13 i=1 until [ $i -gt 9 ]do j=1 until [ $j -gt $i ] do echo -ne "$j *$i =$ [ $i *$j ]\t" let j++ done echo let i++ done
shift 位移的用法 1 2 3 4 5 6 7 8 9 exit 退出整个程序break 结束当前循环,或跳出本层循环continue 忽略本次循环剩余的代码,直接进行下一次循环shift 使位置参数向左移动,默认移动1 位,可以使用shift 2 : true false
2.1 实现用户自定义输入数字,然后脚本计算和:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 1 .常规 for 循环sum=0 for i do let sum=$sum +$i done echo sum=$sum 结果显示 [root @Jermyn shell_scripts ] + sum=0 + for i in '"$@"' + let sum=0 +1 + for i in '"$@"' + let sum=1 +2 + for i in '"$@"' + let sum=3 +3 + echo sum=6 sum=6 2 .使用 shiftsum=0 while [ $ do let sum =$sum +$1 shift done echo sum =$sum 结果显示: [root @Jermyn shell_scripts ] + sum =0 + '[' 3 -ne 0 ']' + let sum =0 +1 + shift + '[' 2 -ne 0 ']' + let sum =1 +2 + shift + '[' 1 -ne 0 ']' + let sum =3 +3 + shift + '[' 0 -ne 0 ']' + echo sum =6 sum =6
补充扩展expect 语法:expect 自动应答 tcl语言
3.1 需求:A远程登录到server上什么都不做
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 spawn ssh root@192.168 .88.139 expect { "(yes/no)?" { send "yes\r" ;exp_continue } "password:" { send "123456\r" } } interact //交互 脚本执行方式: 1 )定义变量set ip 192.168 .88.139 set pass 123456 set timeout 5 spawn ssh root@ $ip expect { "yes/no" { send "yes\r" ;exp_continue } "password:" { send "$pass \r" } } interact 2 )使用位置参数 类比 bash 的$ * $1 等等set ip [ lindex $argv 0 ]set pass [ lindex $argv 1 ]set timeout 5 spawn ssh root@ $ip expect { "yes/no" { send "yes\r" ;exp_continue } "password:" { send "$pass \r" } } interact
3.2 需求:A远程登录到server上操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 set ip 10.1 .1.1 set pass 123456 set timeout 5 spawn ssh root@ $ip expect { "yes/no" { send "yes\r" ;exp_continue } "password:" { send "$pass \r" } } expect "#" send "rm -rf /tmp/*\r" send "touch /tmp/file{1..3}\r" send "date\r" send "exit\r" expect eof
3.3 shell 脚本和 expect 结合
需求:shell脚本和expect结合使用,在多台服务器上创建1个用户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 [root @server shell04 ] 10.1 .1.1 123456 10.1 .1.2 123456 1 . 循环2 . 登录远程主机——>ssh——>从ip.txt文件里获取IP和密码分别赋值给两个变量3 . 使用expect程序来解决交互问题while read ip passdo /usr/bin/expect <<-END &>/dev/null spawn ssh root@ $ip expect { "yes/no" { send "yes\r" ;exp_continue } "password:" { send "$pass \r" } } expect "#" { send "useradd yy1;rm -rf /tmp/*;exit\r" } expect eof END done < ip.txt cat ip.txt|while read ip passdo { /usr/bin/expect <<-EOF spawn ssh root@ $ip expect { "yes/no" { send "yes\r" ;exp_continue } "password:" { send "$pass \r" } } expect "#" send "hostname\r" send "exit\r" expect eof EOF }& done wait echo "user is ok...." 或者 while read ip passdo { /usr/bin/expect <<-EOF spawn ssh root@ $ip expect { "yes/no" { send "yes\r" ;exp_continue } "password:" { send "$pass \r" } } expect "#" send "hostname\r" send "exit\r" expect eof EOF }& done<ip.txt wait echo "user is ok...."
实战示例 1、写一个脚本,将跳板机上yunwei用户的公钥推送到局域网内可以ping通的所有机器上
说明:主机和密码文件已经提供
10.1.1.1:123456
10.1.1.2:123456
案例分析
关闭防火墙和selinux 判断ssh服务是否开启(默认ok) 循环判断给定密码文件里的哪些IP是可以ping通 判断IP是否可以ping通——>$?—>流程控制语句 密码文件里获取主机的IP和密码保存变量 判断公钥是否存在—>不存在创建它 ssh-copy-id 将跳板机上的yunwei用户的公钥推送到远程主机—>expect解决交互 将ping通的主机IP单独保存到一个文件 测试验证 落地实现
代码拆分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 1 .判断yunwei用户的公钥是否存在[ ! -f /hoem /yunwei /.ssh /id_rsa ] && ssh-keygen -P '' -f ./id_rsa 2 .获取IP并且判断是否可以 ping 通2.1 主机密码文件 ip.txt 10.1 .1.1 :123456 10.1 .1.2 :123456 2.2 循环判断主机是否ping通 tr ':' ' ' < ip.txt|while read ip pass do ping -c1 $ip &>/dev/null if [ $ ? -eq 0 ];then 推送公钥 fi done 3 .非交互式推送公钥/usr/bin/expect <<-END &>/dev/null spawn ssh-copy-id root@ $ip expect { "yes/no" { send "yes\r" ;exp_continue } "password:" { send "$pass \r" } } expect eof END
最终实现
环境准备 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 jumper-server 有yunwei用户 yunwei用户 sudo 授权: visudo root ALL=(ALL) ALL yunwei ALL=(root) NOPASSWD:ALL,!/sbin/shutdown,!/sbin/init,!/bin/rm -rf / 解释说明: 1 )第一个字段 yunwei 指定的是用户:可以是用户名,也可以是别名。每个用户设置一行,多个用户设置多行,也可以将多个用户设置成一个别名后再进行设置。2 )第二个字段ALL指定的是用户所在的主机:可以是ip,也可以是主机名,表示该sudo设置只在该主机上生效,ALL表示在所有主机上都生效!限制的一般都是本机,也就是限制使用这个文件的主机;一般都指定为"ALL" 表示所有的主机,不管文件拷到那里都可以用。比如:10.1 .1.1 =...则表示只在当前主机生效。3 )第三个字段(root)括号里指定的也是用户:指定以什么用户身份执行sudo,即使用sudo后可以享有所有root账号下的权限。如果要排除个别用户,可以在括号内设置,比如ALL=(ALL,!oracle,!pos)。4 )第四个字段ALL指定的是执行的命令:即使用sudo后可以执行所有的命令。除了关机和删除根内容以外;也可以设置别名。NOPASSWD: ALL表示使用sudo的不需要输入密码。5 )也可以授权给一个用户组 %admin ALL=(ALL) ALL 表示admin组里的所有成员可以在任何主机上以任何用户身份执行任何命令
脚本实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [ ! -f /home /yunwei /.ssh /id_rsa ] && ssh-keygen -P '' -f ~/.ssh/id_rsa tr ':' ' ' < /shell04/ip.txt|while read ip pass do { ping -c1 $ip &>/dev/null if [ $ ? -eq 0 ];then echo $ip >> ~/ip_up.txt /usr/bin/expect <<-END &>/dev/null spawn ssh-copy-id root@ $ip expect { "yes/no" { send "yes\r" ;exp_continue } "password:" { send "$pass \r" } } expect eof END fi }& done wait echo "公钥已经推送完毕,正在测试...." remote_ip=`tail -1 ~/ip_up.txt` ssh root@ $remote_ip hostname &>/dev/null test $ ? -eq 0 && echo "公钥成功推送完毕"
写一个脚本,统计web服务的不同连接状态个数 找出查看网站连接状态的命令 ss -natp|grep :22
如何统计==不同的==状态 循环去统计,需要计算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 declare -A array1 states=`ss -ant |grep 22 |cut -d ' ' -f1 ` for i in $states do let array1[$i ]++ done for j in $ {!array1[@ ]}do echo $j: $ {array1[$j ]} done
case语句+函数 case语句 语法:case语句为多重匹配语句;如果匹配成功,执行相匹配的命令。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 说明:pattern表示需要匹配的模式 case var in 定义变量;var代表是变量名 pattern 1 ) 模式1 ;用 | 分割多个模式,相当于or command1 需要执行的语句 ;; 两个分号代表命令结束 pattern 2 ) command2 ;; pattern 3 ) command3 ;; *) default,不满足以上模式,默认执行*)下面的语句 command4 ;; esac esac表示case语句结束
需求:
1.1当给程序传入start、stop、restart三个不同参数时分别执行相应命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 case $1 in start |S) service apache start &>/dev/null && echo "apache 启动成功" || echo "apache 启动失败" ;; stop|T) service apache stop &>/dev/null && echo "apache 停止成功" || echo "apache 停止失败" ;; restart|R ) service apache restart &>/dev/null && echo "apache 重启完毕" || echo "apache 重启失败" ;; *) echo -e "\033[31m 此脚本需要传入位置参数 \033[0m" echo -e "\033[41;30m 请选择 start|S || stop|T || restart|R\033[0m" ;; esac
1.2 脚本提示让用户输入需要管理的服务名,然后提示用户需要对服务做什么操作,如启动,关闭等操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 read -p "请输入你要管理的服务名称(vsftpd):" service case $service in vsftpd|ftp) read -p "请选择你需要做的事情(restart|stop):" action case $action in stop|S) service vsftpd stop &>/dev/null && echo "该$serivce 服务已经停止成功" ;; start ) service vsftpd start &>/dev/null && echo "该$serivce 服务已经成功启动" ;; esac ;; httpd|apache) echo "apache hello world" ;; *) echo "请输入你要管理的服务名称(vsftpd):" ;; esac
1.3 菜单打印出来;交互式让用户输入操作编号,然后做出相应处理
落地实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 meue(){ echo -e "\033[32m ################## 菜单栏 ######################### # f 显示磁盘分区 # # d 显示磁盘挂载 # # m 查看内存使用 # # u 查看系统负载 # # q 退出程序 # ################################################### \033[0m" } meue while truedo read -p "请选择您需要查询的内容:" action case $action in f) clear echo -e "\033[36m########################### 显示分区 ######################\033[0m" fdisk -l meue ;; d) clear echo -e "\033[36m########################### 显示磁盘挂载 ######################\033[0m" df -h meue ;; m) clear echo -e "\033[36m########################### 查看内存使用 ######################\033[0m" free -m meue ;; u) clear echo -e "\033[36m########################### 查看系统负载区 ######################\033[0m" uptime meue ;; q) echo -e "\033[36m########################### 退出程序 ######################\033[0m" exit ;; esac done
函数 语法 :
1 2 3 4 5 6 方法一: 函数名() { 函数体(一堆命令的集合,来实现某个功能) }
1 2 3 4 5 6 7 8 方法二: function 函数名() { 函数体(一堆命令的集合,来实现某个功能) echo hello echo world }
函数中return说明:
return可以结束一个函数。类似于循环控制语句break(结束当前循环,执行循环体后面的代码)。 return默认返回函数中最后一个命令状态值,也可以给定参数值,范围是0-256之间。 如果没有return命令,函数将返回最后一个指令的退出状态值。 2.1 当前命令行调用
1 2 3 4 5 6 7 8 fun_1(){ echo "调用fun_1函数就打印了我!!!" } fun_2(){ echo "调用fun_2函数就打印了我!!!" }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root @Jermyn shell_scripts ] fun_1(){ echo "调用fun_1函数就打印了我!!!" } fun_2(){ echo "调用fun_2函数就打印了我!!!" } [root @Jermyn shell_scripts ] [root @Jermyn shell_scripts ] 调用fun_1函数就打印了我!!! [root @Jermyn shell_scripts ] 调用fun_2函数就打印了我!!! [root @Jermyn shell_scripts ]
2.2 定义到用户的环境变量中
1 2 3 4 5 6 7 8 9 10 11 12 添加编写的函数: fun_1(){ echo "调用fun_1函数就打印了我!!!" } fun_2(){ echo "调用fun_2函数就打印了我!!!" } 注:当用户打开 bash 的时候会读取该文件
2.3 脚本中调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 meue(){ echo -e "\033[32m ################## 菜单栏 ######################### # f 显示磁盘分区 # # d 显示磁盘挂载 # # m 查看内存使用 # # u 查看系统负载 # # q 退出程序 # ################################################### \033[0m" } meue
2.4 需求
写一个脚本收集用户输入的基本信息(姓名,性别,年龄),如不输入一直提示输入 最后根据用户的信息输出相对应的内容 思路:
交互式定义多个变量来保存用户信息 姓名、性别、年龄 如果不输一直提示输入
循环==直到输入字符串不为空 while 判断输入字符串是否为空 每个信息都必须不能为空,该功能可以定义为一个函数,方便下面脚本调用 根据用户输入信息做出匹配判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 input_fun() { input_info="" output_info=$1 while [ -z $input_info ] do read -p "$output_info " input_info done echo $input_info } while truedo name=$ (input_fun 请输入您的姓名:) if [[ $name = "exit" ]] ; then exit fi gender=$ (input_fun 请输入您的性别:) age=$ (input_fun 请输入您的年龄:) echo "name $name gender $gender age $age " | tee -a stu_info.txt done
2.5 需求
只允许yunwei用户通过跳板机远程连接后台的应用服务器做一些维护操作 公司运维人员远程通过yunwei用户连接跳板机时,跳出以下菜单供选择: 1 2 3 4 5 6 7 欢迎使用 Jumper-server ,请选择你要操作的主机: 1 . DB1-Master 2 . DB2-Slave 3 . Web14 . Web2h . helpq. exit
当用户选择相应主机后,直接免密码登录 成功 如果用户不输入一直提示用户输入,直到用户选择退出 综合分析
将脚本放到 yunwei 用户家目录里的.bashrc文件里(/root/yunwei/jump_server.sh) 将菜单定义为一个函数[打印菜单],方便后面调用 用case语句来实现用户的选择【交互式定义变量】 当用户选择了某一台服务器后,进一步询问用户需要做的事情 case…esac 交互式定义变量 使用循环来实现用户不选择一直让其选择 限制用户退出后直接关闭终端 exit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 menu() { cat <<-EOF 欢迎使用Jumper-server ,请选择你要操作的主机: 1 . DB1-Master 2 . DB2-Slave 3 . Web14 . Web2h . helpq. exit EOF } trap '' 1 2 3 19 menu while truedo read -p "请选择你要访问的主机:" host case $host in 1 ) ssh root@10.1 .1.1 ;; 2 ) ssh root@10.1 .1.2 ;; 3 ) ssh root@10.1 .1.3 ;; h ) clear ;menu ;; q) exit ;; esac done 将脚本放到yunwei 用户家目录里的.bashrc里执行:每次启动体跳板机即运行脚本 bash ~/jumper-server .sh exit
进一步完善需求
为了进一步增强跳板机的安全性,工作人员通过跳板机访问生产环境,但是不能在跳板机上停留。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 trap '' 1 2 3 19 menu(){ cat <<-EOF 欢迎使用Jumper-server ,请选择你要操作的主机: 1 . DB1-Master 2 . DB2-Slave 3 . Web14 . Web2h . helpq. exit EOF } menu while truedo read -p "请输入你要选择的主机[h for help]:" host case $host in 1 |DB1) ssh root@10.1 .1.1 ;; 2 |DB2) ssh root@10.1 .1.2 ;; 3 |web1) ssh root@10.1 .1.250 ;; h |help) clear ;menu ;; q|quit) exit ;; esac done 自己完善功能: 1 . 用户选择主机后,需要事先推送公钥;如何判断公钥是否已推2 . 比如选择web1时,再次提示需要做的操作,比如:clean log 重启服务 kill 某个进程
回顾信号:
1 2 3 4 5 6 7 8 1 ) SIGHUP 重新加载配置 2 ) SIGINT 键盘中断^C3 ) SIGQUIT 键盘退出9 ) SIGKILL 强制终止15 ) SIGTERM 终止(正常结束),缺省信号18 ) SIGCONT 继续19 ) SIGSTOP 停止20 ) SIGTSTP 暂停^Z
正则表达式 语法 :正则表达式(Regular Expression、regex 或 regexp,缩写为RE),也译为正规表示法、常规表示法,是一种字符模式,用于在查找过程中匹配指定的字符。许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。支持正则表达式的程序如:locate |find| vim| grep| sed |awk
作用: 匹配邮箱、匹配身份证号码、手机号、银行卡号等匹配某些特定字符串,做特定处理等等
正则当中名词解释 :
1.1 正则中普通常用的元字符 元字符 功能 备注 . 匹配除了换行符以外的任意单个字符 * 前导字符出现0次或连续多次 .* 任意长度字符 ab.* ^ 行首(以…开头) ^root $ 行尾(以…结尾) bash$ ^$ 空行 [] 匹配括号里任意单个字符或一组单个字符 [abc] 匹配不包含括号里任一单个字符或一组单个字符 abc ^[] 匹配以括号里任意单个字符或一组单个字符开头 ^[abc] \^[\^] 匹配不以括号里任意单个字符或一组单个字符开头 \^[^abc]
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ggle gogle google gooogle goooooogle gooooooogle taobao.com taotaobaobao.com jingdong.com dingdingdongdong.com 10.1 .1.1 Adfjd8789JHfdsdf/ a87fdjfkdLKJK 7 kdjfd989KJK;bSKJjkksdjf878. cidufKJHJ6576, hello world helloworld yourself
1 2 3 4 5 6 7 8 9 10 1 .前导字符 g 后面任意字符出现[root @Jermyn shell_scripts ] ggle gogle google gooogle goooooogle gooooooogle jingdong.com dingdingdongdong.com
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 2 .前导字符 g 出现零次或多次[root @Jermyn shell_scripts ] ggle gogle google gooogle goooooogle gooooooogle taobao.com taotaobaobao.com jingdong.com dingdingdongdong.com 10.1 .1.1 Adfjd8789JHfdsdf/ a87fdjfkdLKJK 7 kdjfd989KJK;bSKJjkksdjf878. cidufKJHJ6576, hello world helloworld yourself
1 2 3 4 5 6 7 8 9 10 3 . g 确定 前导字符 o 出现零次或多次[root @Jermyn shell_scripts ] ggle gogle google gooogle goooooogle gooooooogle jingdong.com dingdingdongdong.com
1 2 3 4 5 6 7 8 4 . 以 g 开头的行[root @Jermyn shell_scripts ] ggle gogle google gooogle goooooogle gooooooogle
1 2 3 4 5 6 7 8 5 . 以 g 开头的后面的有一个字符[root @Jermyn shell_scripts ] ggle gogle google gooogle goooooogle gooooooogle
1 2 3 4 5 6 7 8 6 .以 g 开头的字符 前导字符是任意的后面一个字符 任意字符 出现一次或者多次[root @Jermyn shell_scripts ] ggle gogle google gooogle goooooogle gooooooogle
1 2 3 4 5 6 7 8 7 .前导字符为 空 后面有任意字符,这个任意字符出现一次或者多次,以 gle 结尾[root @Jermyn shell_scripts ] ggle gogle google gooogle goooooogle gooooooogle
1 2 3 4 8 .查看空行[root @Jermyn shell_scripts ] 11 :20 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 9 .匹配 [ogl ] 中的 o 或 g 或 l[root @Jermyn shell_scripts ] g gle ggle gogle google gooogle goooooogle gooooooogle taobao.com taotaobaobao.com jingdong.com dingdingdongdong.com hello world helloworld yourself
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 .匹配除了 o g l 以外的字符 不包含 空[root @Jermyn shell_scripts ] gle ggle gogle google gooogle goooooogle gooooooogle taobao.com taotaobaobao.com jingdong.com dingdingdongdong.com 10.1 .1.1 Adfjd8789JHfdsdf/ a87fdjfkdLKJK 7 kdjfd989KJK;bSKJjkksdjf878. cidufKJHJ6576, hello world helloworld yourself
1 2 3 4 5 6 7 8 9 10 11 12 11 .匹配 [gh ] 任意单个字符开头的[root @Jermyn shell_scripts ] g gle ggle gogle google gooogle goooooogle gooooooogle hello world helloworld yourself
1 2 3 4 5 6 7 8 9 10 11 12 12 .查看 profile 的前20 行,行过滤 不以 [root @Jermyn shell_scripts ] pathmunge () { case ":$ {PATH}:" in *:"$1 " :*) ;; *) if [ "$2 " = "after" ] ; then PATH=$PATH: $1 else PATH=$1: $PATH fi
1 2 3 4 5 6 7 13 .查看 RegExpTest_02.txt 的前 10 行 行过滤不以 go 开头的[root @Jermyn shell_scripts ] g gle ggle taobao.com taotaobaobao.com
1.2 正则中其他常用元字符 元字符 功能 备注 \< 取单词的头 \> 取单词的尾 \< \> 精确匹配 \{n\} 匹配前导字符 连续出现n次 \{n,\} 匹配前导字符 至少出现n次 \{n,m\} 匹配前导字符出现 n次与m次之间 \( \) 保存被匹配的字符 \d 匹配数字(grep -P ) [0-9] \w 匹配字母数字下划线(grep -P ) [a-zA-Z0-9_] \s 匹配空格、制表符、换页符(grep -P ) [\t\r\n]
举例说明:
1 2 3 4 5 6 7 8 1 .匹配单词的头以 hel 开头的[root @Jermyn shell_scripts ] hello world helloworld yourself
1 2 3 2 .精确匹配文件中 只是 g 的行[root @Jermyn shell_scripts ] g
1 2 3 4 5 6 3 . g 确定,前导字符 o 出现两次[root @Jermyn shell_scripts ] google gooogle goooooogle gooooooogle
1 2 3 4 5 6 7 8 4 .前导字符 o 出现两次[root @Jermyn shell_scripts ] google gooogle goooooogle gooooooogle 注:此语法可看作 oo 作为整体出现的次数,gogle 则匹配不到,gooole 则只有前两个oo 高亮
1 2 3 4 5 6 5 . 前导字符 o 出现至少 2 次[root @Jermyn shell_scripts ] google gooogle goooooogle gooooooogle
1 2 3 6 .匹配 ip 10 即[0 -9 ]出现两次; . 需要转义;1 即[0 -9 ] 出现一次[root @Jermyn shell_scripts ] 10.1 .1.1
1 2 3 4 5 7 .改10.1 .1.1 为10.1 .1.254 vim 的末行模式 输入 :%s/\(10.1 .1 \).1 /\1.254 /g 解释:%s 全局搜索 (10.1 .1 )将此打包起来 1.254 1 是标签名
1 2 3 4 8 .修改 helloworld yourself 为 hiworld myselfvim 的末行模式 :%s/hello\(world \)your\(self\)/hi\1 my\2 /g
1.3 扩展类正则常用元字符 语法:
grep你要用我,必须加 grep -E
或者 让使用egrep
sed你要用我,必须加 sed -r
扩展元字符 功能 备注 + 匹配一个或多个前导字符 bo+ 匹配boo、 bo ? 匹配零个或一个前导字符 bo? 匹配b、 bo \ 或 匹配a或b () 组字符(看成整体) (my\ your)self:表示匹配myself或匹配yourself {n} 前导字符重复n次 {n,} 前导字符重复至少n次 {n,m} 前导字符重复n到m次
举例说明:
1 2 3 4 5 6 7 1 . g 固定,前导字符 o 出现一次或者多次,和 go* 区别 后者可以出现零次[root @Jermyn shell_scripts ] gogle google gooogle goooooogle gooooooogle
1 2 3 4 5 6 7 8 9 10 11 12 2 . g 固定,前导字符 o 出现零次或者一次[root @Jermyn shell_scripts ] g gle ggle gogle google gooogle goooooogle gooooooogle jingdong.com dingdingdongdong.com
1 2 3 4 5 6 7 8 9 10 11 12 3 . g 是固定的,前导字符是后面任意一个字符 出现零次或者一次(注:就是匹配 g 后面是任意一个字符,如 go 和 gl 是相同的)[root @Jermyn shell_scripts ] g gle ggle gogle google gooogle goooooogle gooooooogle jingdong.com dingdingdongdong.com
1 2 3 4 4 .匹配以 a 或者 以 b 的行 [root @Jermyn shell_scripts ] a87fdjfkdLKJK bSKJjkksdjf878.
1 2 3 4 5 6 5 .匹配以 a 开头的 或者 含有 b 的行[root @Jermyn shell_scripts ] taobao.com taotaobaobao.com a87fdjfkdLKJK bSKJjkksdjf878.
1 2 3 4 5 6 7 6 .过滤 ao 或者 bo[root @Jermyn shell_scripts ] taobao.com taotaobaobao.com ao87fdjfkdLKJK 7 kdjafd9bo89aoKJK;boSKaJjkksdajf878.
1 2 3 4 5 6 7 8 9 10 11 7 .作用与 不加 \ 的效果相同[root @Jermyn shell_scripts ] google gooogle goooooogle gooooooogle [root @Jermyn shell_scripts ] google gooogle goooooogle gooooooogle
1.4 第二类正则 表达式 功能 示例 [:alnum:] 字母与数字字符 [[:alnum:]]+ [:alpha:] 字母字符(包括大小写字母) [[:alpha:]]{4} [:blank:] 空格与制表符 [[:blank:]]* [:digit:] 数字 [[:digit:]]? [:lower:] 小写字母 [[:lower:]]{4,} [:upper:] 大写字母 [[:upper:]]+ [:punct:] 标点符号 [[:punct:]] [:space:] 包括换行符,回车等在内的所有空白 [[:space:]]+
1 2 3 4 1 .数字连续出现4 次 (注:[:lower :] 不可以有空格)[root @Jermyn shell_scripts ] Adfjd8789JHfdsdf/ cidufaKoJHJ6576,
正则字符一览表 元字符 :在正则中,具有特殊意义的专用字符,如: 星号(*)、加号(+)等
前导字符 :元字符前面的字符叫前导字符
元字符 功能 示例 * 前导字符出现0次或者连续多次 ab* abbbb . 除了换行符以外,任意单个字符 ab. ab8 abu .* 任意长度的字符 ab.* adfdfdf [] 括号里的任意单个字符或一组单个字符 [abc][0-9][a-z] 不匹配括号里的任意单个字符或一组单个字符 abc ^[] 匹配以括号里的任意单个字符开头 ^[abc] \^ 不匹配以括号里的任意单个字符开头 ^ 行的开头 ^root $ 行的结尾 bash$ ^$ 空行 \{n\}和{n} 前导字符连续出现n次 [0-9]\{3\} \{n,\}和{n,} 前导字符至少出现n次 [a-z]{4,} \{n,m\}和{n,m} 前导字符连续出现n-m次 go{2,4} \<\> 精确匹配单词 \ \(\) 保留匹配到的字符 \(hello\) + 前导字符出现1次或者多次 [0-9]+ ? 前导字符出现0次或者1次 go? \ 或 \^root\ \^ftp () 组字符 (hello\ world)123 \d perl内置正则 grep -P \d+ \w 匹配字母数字下划线
练习 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 文件准备: vim test.txt Aieur45869Root0000 9 h847RkjfkIIIhellorootHllow88000dfjj 8 ikuioerhfhupliookinghello world 192.168 .0.254 welcome to uplooking. abcderfkdjfkdtest rlllA899kdfkdfj iiiA848890ldkfjdkfj abc 12345678908374 123456 @qq.com123456 @163 .comabcdefg@itcast.com23ed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 1 、查找不以大写字母开头的行(三种写法)。grep '^[^A-Z]' RegExpTest_01.txt grep -v "^[A-Z]" RegExpTest_01.txt grep -v "\<[A-Z]" RegExpTest_01.txt grep -E -v "^[[:upper:]]" RegExpTest_01.txt <--- > '^[^[:upper:]]' 2 、查找有数字的行(两种写法)grep '[0-9]' RegExpTest_01.txt grep -P "\d" RegExpTest_01.txt grep -E "[[:digit:]]" RegExpTest_01.txt 3 、查找一个数字和一个字母连起来的grep -E '[0-9][a-zA-Z]|[a-zA-Z][0-9]' RegExpTest_01.txt 4 、查找不以r 开头的行grep -v '^r' RegExpTest_01.txt grep '^[^r]' RegExpTest_01.txt 5 、查找以数字开头的grep '^[0-9]' RegExpTest_01.txt grep -E '^[[:digit:]]' RegExpTest_01.txt 6 、查找以大写字母开头的grep '^[A-Z]' RegExpTest_01.txt grep -E "^[[:upper:]]" RegExpTest_01.txt 7 、查找以小写字母开头的grep '^[a-z]' RegExpTest_01.txt grep -E "^[[:lower:]]" RegExpTest_01.txt 8 、查找以点结束的grep '\.$' RegExpTest_01.txt 9 、去掉空行grep '^[^$]' RegExpTest_01.txt 10 、查找完全匹配abc的行grep '\<abc\>' RegExpTest_01.txt grep -w 'abc' RegExpTest_01.txt 11 、查找A后有三个数字的行grep 'A[0-9][0-9][0-9]' RegExpTest_01.txt grep 'A[0-9]\{3\}' RegExpTest_01.txt grep -E 'A([0-9]){3}' RegExpTest_01.txt 12 、统计root在/etc/passwd里出现了几次grep -o 'root' /etc/passwd | wc -l 13 、用正则表达式找出自己的IP地址、广播地址、子网掩码ifconfig ens33 | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' ifconfig | grep "broadcast" | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}' ifconfig ens33 | grep -o -P '(\d{1,3}\.){3}\d{1,3}' ifconfig ens33 | grep -o -P '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' 14 、找出文件中的ip地址cat /etc/sysconfig/network-scripts /ifcfg-ens33 | grep "IPADDR" | cut -d "=" -f2 15 、找出全部是数字的行grep -E '^[0-9]+$' RegExpTest_01.txt grep -E -v "[[:alpha:]|[:punct:]|[:space:]]" RegExpTest_01.txt | grep -v "^$ " grep -E -v "[[:alpha:]|[:punct:]|[:space:]]" RegExpTest_01.txt | grep "[0-9]." 16 、找出邮箱地址grep -E '^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$' RegExpTest_01.txt grep -E "([0-9]|[A-Z]|[a-z]|[._-])+@([0-9]|[A-Z]|[a-z]|[._-])+\.([A-Z]|[a-z])+$ " RegExpTest_01.txt grep --help : 匹配模式选择: Regexp selection and interpretation: -E , --extended-regexp 扩展正则 -G , --basic-regexp 基本正则 -P , --perl-regexp 调用perl的正则 -e , --regexp =PATTERN use PATTERN for matching -f , --file =FILE obtain PATTERN from FILE -i , --ignore-case 忽略大小写 -w , --word-regexp 匹配整个单词
脚本搭建Web服务 脚本搭建web服务 要求如下 :
用户输入web服务器的IP、域名以及数据根目录 如果用户不输入则一直提示输入,直到输入为止 当访问www.test.cc时可以访问到数据根目录里的首页文件“this is test page” 参考脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 参考: conf=/etc/httpd/conf/httpd.conf input_fun() { input_var="" output_var=$1 while [ -z $input_var ] do read -p "$output_var " input_var done echo $input_var } ipaddr=$ (input_fun "Input Host ip[192.168.0.1]:" ) web_host_name=$ (input_fun "Input VirtualHostName [www.test.cc]:" ) root_dir=$ (input_fun "Input host Documentroot dir:[/var/www/html]:" ) [ ! -d $root_dir ] && mkdir -p $root_dir chown apache.apache $root_dir && chmod 755 $root_dir echo this is $web_host_name > $root_dir /index.htmlecho "$ipaddr $web_host_name " >> /etc/hosts[ -f $conf ] && cat >> $conf <<end NameVirtualHost $ipaddr:80 <VirtualHost $ipaddr:80 > ServerAdmin webmaster@ $web_host_name DocumentRoot $root_dir ServerName $web_host_name ErrorLog logs/$web_host_name -error_log CustomLog logs/$web_host_name -access_loh common </VirtualHost> end
sed 用法 sed 是 Stream Editor(流编辑器)的缩写,简称流编辑器;用来 处理文件 的。
sed 是一行一行读取文件内容并按照要求进行处理,把处理后的结果输出到屏幕。
总结:
由于sed把每一行都存在临时缓冲区中,对这个副本 进行编辑,所以不会直接修改原文件 Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作,对文件进行过滤和转换操作 命令行格式 语法:sed [options] ‘ 处理动作‘ 文件名 (注:一定是单引号 )
sed [options] ‘处理动作’ 文件名
选项 说明 备注 -e 进行多项(多次)编辑 -n 取消默认输出 不自动打印模式空间 -r 使用扩展正则表达式 -i 原地编辑(修改源文件) -f 指定sed脚本的文件名 sed [options] ==‘ ==处理动作==’== 文件名
以下所有的动作都要在单引号 里
动作 说明 备注 ‘p’ 打印 ‘i’ 在指定行之前插入内容 类似vim里的大写O ‘a’ 在指定行之后插入内容 类似vim里的小写o ‘c’ 替换指定行所有内容 ‘d’ 删除指定行
1.1 例子
1 2 3 4 5 6 7 8 9 10 vim sed_test.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/nologin298374837483 172.16 .0.254 10.1 .1.1
1.1.1 对文件进行增、删、改、查操作
语法:sed 选项 ‘定位+命令’ 需要处理的文件
1 2 3 4 5 6 7 8 9 10 1 . 不加处理动作,打印文件的内容[root @Jermyn shell_scripts ] 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/nologin298374837483 172.16 .0.254 10.1 .1.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 2 .按照处理流程,会一行一行执行代码,第一次的打印指运行此行时打印出来与代码要求无关,第二次打印指代码此行要求打印(注:简单理解为第一行时sed处理的内容,第二行为处理的结果)[root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash root:x:0 :0 :root:/root:/bin/bash bin:x:1 :1 :bin:/bin:/sbin/nologin bin:x:1 :1 :bin:/bin:/sbin/nologin daemon:x:2 :2 :daemon:/sbin:/sbin/nologin daemon:x:2 :2 :daemon:/sbin:/sbin/nologin adm:x:3 :4 :adm:/var/adm:/sbin/nologin adm:x:3 :4 :adm:/var/adm:/sbin/nologin lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologinlp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologin298374837483 298374837483 172.16 .0.254 172.16 .0.254 10.1 .1.1 10.1 .1.1
1 2 3 4 5 6 7 8 9 10 3 . -n 指的是取消默认输出,可以理解为,每次 sed 处理文件的某行时都会打印一次此行的内容,-n 就取消了这次打印,而下面的结果就是,sed 对文件的处理的结果 (注: -n 和 'p' 经常一起使用)[root @Jermyn shell_scripts ] 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/nologin298374837483 172.16 .0.254 10.1 .1.1
1 2 3 4 5 6 7 8 9 10 11 4 .此部分可以理解为,对第二行进行 sed 打印的操作,其他行都是默认打印的内容,第二行第一遍就是默认打印的结果[root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash bin:x:1 :1 :bin:/bin:/sbin/nologin 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/nologin298374837483 172.16 .0.254 10.1 .1.1
1 2 3 5 .此部分理解为,sed 只对第二行进行了打印的操作,-n 值得默认打印内容都不输出[root @Jermyn shell_scripts ] bin:x:1 :1 :bin:/bin:/sbin/nologin
1 2 6 .打印 1 -5 行 理解上方几点,此无特别之处[root @Jermyn shell_scripts ]
1 2 3 7 . $p 指打印最后一行 无 ^p 用法,打印第一行 1 p[root @Jermyn shell_scripts ] 10.1 .1.1
1.1.2 增加文件内容
i 定位符上一行插入内容
a 定位符下一行插入内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 . 在文件的第二行插入 hello world[root @Jermyn shell_scripts ] hello world [root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash hello world 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/nologin298374837483 172.16 .0.254 10.1 .1.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 2 .不加定位符的话,就是每行的上一行都插入 hello world ,可以理解为 sed 对每行进行传入的操作,命令的结果就是内容 hello world 命令执行的对象就是每一行,所以每一行都会打印一次[root @Jermyn shell_scripts ] hello world root:x:0 :0 :root:/root:/bin/bash hello world bin:x:1 :1 :bin:/bin:/sbin/nologin hello world daemon:x:2 :2 :daemon:/sbin:/sbin/nologin hello world adm:x:3 :4 :adm:/var/adm:/sbin/nologin hello world lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologinhello world 298374837483 hello world 172.16 .0.254 hello world 10.1 .1.1
1 2 3 .文件的第二行的上一行插入 hello 换行 world 。[root @Jermyn shell_scripts ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 4 .先输入左侧单引号,添加的内容添加完成后在输入右侧的单引号。第一行输入 sed '3i\ 后敲击回车,第二行 \ 后敲击回车 [root@Jermyn shell_scripts]# sed ' 3 i\> hello\ > world' sed_test.txt root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin hello world 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 298374837483 172.16.0.254 10.1.1.1
注:示例 3 和 4 示例 3 更好操作些,示例4更直观些
1 2 3 4 5 . a 类比 i [root @Jermyn shell_scripts ] [root @Jermyn shell_scripts ]
1.1.3 修改文件的内容
c 替换指定的整行内容
1 2 3 4 5 6 7 8 9 10 1 .将以 adm 开头的行替换为 hello world[root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash bin:x:1 :1 :bin:/bin:/sbin/nologin daemon:x:2 :2 :daemon:/sbin:/sbin/nologin hello world lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologin298374837483 172.16 .0.254 10.1 .1.1
1 2 2 .每一行都替换为 hello world[root @Jermyn shell_scripts ]
1 2 3 4 5 6 3 .此命令较之前特殊,是将 1 -5 总共5 行 替换为一行 hello world[root @Jermyn shell_scripts ] hello world 298374837483 172.16 .0.254 10.1 .1.1
1.1.4 删除文件的内容
1 2 3 4 5 6 7 8 9 10 11 1 .删除文件的某行[root @Jermyn shell_scripts ] 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/nologin298374837483 172.16 .0.254 10.1 .1.1 [root @Jermyn shell_scripts ] 1 root:x:0 :0 :root:/root:/bin/bash
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 2 .删除文件的 1 -3 行[root @Jermyn shell_scripts ] 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 298374837483 7 172.16 .0.254 8 10.1 .1.1 [root @Jermyn shell_scripts ] adm:x:3 :4 :adm:/var/adm:/sbin/nologin lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologin298374837483 172.16 .0.254 10.1 .1.1
1 2 3 4 5 6 7 8 9 10 3 .删除文件的最后一行[root @Jermyn shell_scripts ] 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/nologin298374837483 172.16 .0.254 [root @Jermyn shell_scripts ]
1 2 3 4 .不加定位就是全删[root @Jermyn shell_scripts ] [root @Jermyn shell_scripts ]
1 2 3 5 .正则删除文件中含有数字的,结果就是全删除了,每行都带有数字[root @Jermyn shell_scripts ] [root @Jermyn shell_scripts ]
1 2 3 6 .只删除 ip 地址的sed '/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/d' sed_test.txt sed -r '([0-9]{1,3}\.){3}[0-9]{1,3}' sed_test.txt
1.2 其他命令
命令 解释 备注 r 从另外文件中读取内容 w 内容另存为 & 保存查找串以便在替换串中引用 和\(\)相同 = 打印行号 ! 对所选行以外的所有行应用命令,放到行数之后 ‘1,5!’ q 退出
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 r 从文件中读取输入行w 将所选的行写入文件 [root @server ~] [root @server ~] [root @server ~] [root @server ~] [root @server ~] ! 对所选行以外的所有行应用命令,放到行数之后 [root @server ~] [root @server ~] [root @server ~] [root @server ~] [root @server ~] [root @server ~] & 保存查找串以便在替换串中引用 \(\) [root @server ~] root:x:0 :0 :root:/root:/bin/bash [root @server ~] 或者 sed -n '1,5s/^/#/p' passwd 以空开头的加上 sed -n '1,5s/^#//p' passwd 以 [root @server ~] [root @server ~] [root @server ~] [root @server ~] [root @server ~] = 打印行号 q 退出 [root @server ~] ROOT:x:0 :0 :root:/root:/bin/bash 综合运用: [root @server ~] [root @server ~]
1.3 其他选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 -e 多项编辑-r 扩展正则-i 修改原文件[root @server ~] root:x:0 :0 :root:/root:/bin/bash 1 [root @server ~] 1 root:x:0 :0 :root:/root:/bin/bash [root @server ~] sed -n '1,5p' 1 .txt sed -ne '1p' -ne '5p' 1 .txt sed -ne '1p;5p' 1 .txt [root @server ~] [root @server ~] [root @server ~] [root @server ~] sed -e '/^#/d' -e '/^;/d' -e '/^$/d' -e '/^\t$/d' -e '/^\t#/d' smb.conf sed -r '/^(#|$|;|\t#|\t$)/d' smb.conf sed -e '/^#/d' -e '/^;/d' -e '/^$/d' -e '/^\t$/d' -e '/^\t#/' smb.conf [root @server ~] [root @server ~] [root @server ~] 192.168 .0.254 [root @server ~] 192.168 .0.254 [root @server ~] 10.1 .1.1 10.1 .1.255 255.255 .255.0 [root @server ~] 10.1 .1.1 10.1 .1.255 255.255 .255.0 [root @server shell06 ] 10.1 .1.1 255.255 .255.0 10.1 .1.254 [root @server shell06 ] 10.1 .1.1 255.255 .255.0 10.1 .1.254 [root @server shell06 ] 10.1 .1.1 255.255 .255.0 10.1 .1.254 [root @server shell06 ] 10.1 .1.1 10.1 .1.255 255.255 .255.0 [root @server shell06 ] [root @server shell06 ] 10.1 .1.1 10.1 .1.255 255.255 .255.0
-i 选项 直接修改原文件
注意: -ni 不要一起使用 p 命令 不要再使用 -i 时使用
1.4 sed 结合正则使用
sed 选项 ‘sed命令 或者 正则表达式 或者 地址定位’ 文件名
定址用于决定对哪些行进行编辑。地址的形式可以是数字、正则表达式、或二者的结合。 如果没有指定地址,sed将处理输入文件的所有行。 正则 说明 备注 /key/ 查询包含关键字的行 sed -n ‘/root/p’ 1.txt /key1/,/key2/ 匹配包含两个关键字之间的行 sed -n ‘/\^adm/,/^mysql/p’ 1.txt /key/,x 从匹配关键字的行开始到==文件第x行==之间的行(包含关键字所在行) sed -n ‘/^ftp/,7p’ x,/key/ 从文件的第x行开始到与关键字的匹配行之间的行 x,y! 不包含x到y行 /key/! 不包括关键字的行 sed -n ‘/bash$/!p’ 1.txt
示例:
1 2 3 4 5 6 7 1 .匹配 bin 到 lpd 之间的行[root @Jermyn shell_scripts ] 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
1 2 3 4 2 .匹配 adm 开头的行或者 lp 开头的行[root @Jermyn shell_scripts ] adm:x:3 :4 :adm:/var/adm:/sbin/nologin lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologin
1 2 3 4 5 6 3 .匹配以 lp 开头的行到 第 8 行[root @Jermyn shell_scripts ] lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologin298374837483
1 2 3 4 5 4 .匹配第 8 行到以 10 开头的行[root @Jermyn shell_scripts ] 172.16 .0.254 10.1 .1.1
1 2 3 4 5 5 .匹配从第 2 行开始 到以 adm 或者 lp 开头的行[root @Jermyn shell_scripts ] 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
脚本模式 用法:
1 2 3 4 5 6 7 8 9 10 11 建议使用 ./sed.sh file 脚本的第一行写上 1 ,5 ds/root/hello/g 3 i7775 i888a999 p
注意事项:
1 2 3 4 5 1. 脚本文件是一个sed的命令行清单。'commands' 2. 在每行的末尾不能有任何空格、制表符(tab)或其它文本。 3. 如果在一行中有多个命令,应该用分号分隔。 4. 不需要且不可用引号保护命令 5.
awk用法 awk 介绍 1.1 awk 概述
awk是一种==编程语言==,主要用于在linux/unix下对==文本和数据==进行处理,是linux/unix下的一个工具。数据可以来自标准输入、一个或多个文件,或其它命令的输出。 awk的处理文本和数据的方式:逐行扫描文件 ,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。 awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。 gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展。 下面介绍的awk是以GNU的gawk为例的,在linux系统中已把awk链接到gawk,所以下面全部以awk进行介绍。 1.2 awk 作用
awk用来处理文件和数据的,是类unix下的一个工具,也是一种编程语言 可以用来统计数据,比如网站的访问量,访问的IP量等等 支持条件判断,支持for和while循环 awk 使用方式 2.1 命令行模式的使用
语法:
1 2 3 4 5 awk 选项 '命令部分' 文件名 特别说明: 引用shell变量需用双引号引起
命名部分说明:
1 2 3 '/root/{awk语句}' sed中: '/root/p' 'NR==1,NR==5{awk语句}' sed中: '1,5p' '/^root/,/^ftp/{awk语句}' sed中:'/^root/,/^ftp/p'
1 2 3 '{print $0;print $1}' sed中:'p' 'NR==5{print $0}' sed中:'5p' 注:awk命令语句间用分号间隔
1 2 3 'BEGIN{awk语句};{处理中};END{awk语句}' 'BEGIN{awk语句};{处理中}' '{处理中};END{awk语句}'
2.2 常用选项介绍
1 2 -F 定义字段分割符号,默认的分隔符是 空格-v 定义变量并赋值
脚本模式使用 3.1 脚本编写
1 2 3 4 5 以下是awk引号里的命令清单,不要用引号保护命令,多个命令用分号间隔 BEGIN {FS=":" }NR==1 ,NR==3 {print $1 "\t" $NF } ...
3.2 脚本执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 方法1 : awk 选项 -f awk的脚本文件 要处理的文本文件 awk -f awk.sh filename sed -f sed.sh -i filename 方法2 : ./awk的脚本文件(或者绝对路径) 要处理的文本文件 ./awk.sh filename ./sed.sh filename方法1 : awk 选项 -f awk的脚本文件 要处理的文本文件 awk -f awk.sh filename sed -f sed.sh -i filename 方法2 : ./awk的脚本文件(或者绝对路径) 要处理的文本文件 ./awk.sh filename ./sed.sh filename
3.3 awk内部相关变量
变量 变量说明 备注 $0 当前处理行的所有记录 $1,$2,$3…$n 文件中每行以间隔符号分割的不同字段 awk -F: ‘{print $1,$3}’ NF 当前记录的字段数(列数) awk -F: ‘{print NF}’ $NF 最后一列 $(NF-1)表示倒数第二列 FNR/NR 行号 FS 定义间隔符 ‘BEGIN{FS=”:”};{print $1,$3}’ OFS 定义输出字段分隔符,默认空格 ‘BEGIN{OFS=”\t”};{print $1,$3}’ RS 输入记录分割符,默认换行 ‘BEGIN{RS=”\t”};{print $0}’ ORS 输出记录分割符,默认换行 ‘BEGI N{ORS=”\n\n”};{print $1,$3}’ FILENAME 当前输入的文件名
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 vim awk_test.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 games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin ntp:x:38:38::/etc/ntp:/sbin/nologin Jermyn:x:1000:1000::/home/Jermyn:/bin/bash
1 2 3 4 5 6 7 8 1 .awk 执行方式与 sed 类似,即一行一行执行,此命令打印当前处理的行,即打印文件的每行内容[root @Jermyn shell_scripts ] [root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash bin:x:1 :1 :bin:/bin:/sbin/nologin ........ ntp:x:38 :38 ::/etc/ntp:/sbin/nologin Jermyn:x:1000 :1000 ::/home/Jermyn:/bin/bash
1 2 3 4 5 6 7 2 .打印 1 -5 行的内容,定位即是 NR==1 ,NR==5 [root @Jermyn shell_scripts ] 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
1 2 3 4 3 .打印第一行和第五行,此处 || 为逻辑运算符,并非正则, | 为正则[root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologin
1 2 3 4 5 6 7 8 9 4 .打印 3 -5 行,第二格命令是 NR 大于等于 3 并且 NR 小于等于 5 的行[root @Jermyn shell_scripts ] 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 @Jermyn shell_scripts ] 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
1 2 3 4 5 6 7 5 .打印以冒号 : 分割的第一列和最后一列以及倒数第二列[root @Jermyn shell_scripts ] root /bin/bash /root bin /sbin/nologin /bin ....... ntp /sbin/nologin /etc/ntp Jermyn /bin/bash /home/Jermyn
1 2 3 4 5 6 7 8 9 10 6 .两个命令区别就是 $NF 和 NF ,后者可以理解为 NF 为变量值为 NR=7 即直接 print 7 ,前者可以理解为是打印第 $7 列[root @Jermyn shell_scripts ] /bin/bash /sbin/nologin ..... [root @Jermyn shell_scripts ] 7 7 .......
1 2 3 4 5 6 7 8 9 10 11 12 13 14 7 .打印出包含 root 的行,/root/ 为地址定位[root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash operator:x:11 :0 :operator:/root:/sbin/nologin [root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash operator:x:11 :0 :operator:/root:/sbin/nologin [root @Jermyn shell_scripts ] root /bin/bash operator /sbin/nologin
1 2 3 4 5 6 7 8 8 .打印 1 -5 行和以 root 开头的行[root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 9 .以冒号 :为分隔符取出第一行和最后一行,取出内容以 @ 隔开(注:OFS=" " 为双引号,不可为单引号)下面两条命令效果相同,如果不定义分隔符的话,可以直接在打印的时候以 定义"分隔符" [root @Jermyn shell_scripts ] [root @Jermyn shell_scripts ] root@ /bin/bash bin@ /sbin/nologin ........ ntp@ /sbin/nologin Jermyn@ /bin/bash [root @Jermyn shell_scripts ] 用户名:root SHELL解释器:/bin/bash 用户名:bin SHELL解释器:/sbin/nologin 用户名:daemon SHELL解释器:/sbin/nologin 用户名:adm SHELL解释器:/sbin/nologin 用户名:lp SHELL解释器:/sbin/nologin 用户名:sync SHELL解释器:/bin/sync .......
1 2 3 10 .下面两条命令效果相同[root @Jermyn shell_scripts ] [root @Jermyn shell_scripts ]
awk工作原理 awk -F: '{print $1,$3}' /etc/passwd
awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符(RS)结束
每行被间隔符 : (默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始
问:awk如何知道用空格来分隔字段的呢?
答:因为有一个内部变量 FS 来确定字段分隔符。初始时,FS赋为空格
awk使用print函数打印字段,打印出来的字段会以空格分隔,因为$1,$3之间有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为 输出字段分隔符OFS,OFS默认为空格
awk处理完一行后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕
awk使用进阶 5.1 格式化输出print
和printf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 print函数 类似echo "hello world" printf函数 类似echo -n awk 'BEGIN{FS=":"};{printf "%-15s %-15s %-15s\n",$1,$6,$NF}' a.txt %s 字符类型 strings %-20s %d 数值类型 占15 字符 - 表示左对齐,默认是右对齐 printf默认不会在行尾自动换行,加\n
5.2 定义变量
1 2 3 4 5 6 7 1 注意: awk中调用定义的变量不需要加$
5.3 awk中BEGIN…END使用
①BEGIN:表示在程序开始前执行
②END :表示所有文件处理完后执行
③用法:'BEGIN{开始处理之前};{处理中};END{处理结束后}'
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 . 打印最后一列和倒数第二列(登录shell和家目录)awk -F : 'BEGIN{ print "Login_shell\t\tLogin_home\n***************************************"};{print $NF"\t\t"$(NF-1)};END{print "***************************************"}' awk_test.txt awk 'BEGIN{ FS=":";print "Login_shell\tLogin_home\n***************************************"};{print $NF"\t"$(NF-1)};END{print "***************************************"}' awk_test.txt 结果显示: Login_shell Login_home *************************************** /bin/bash /root /sbin/nologin /bin /sbin/nologin /sbin /sbin/nologin /var/adm ............ /sbin/nologin /dev/null /sbin/nologin /var/empty/sshd /sbin/nologin /var/spool/postfix /sbin/nologin /var/lib/chrony /sbin/nologin /etc/ntp /bin/bash /home/Jermyn ***************************************
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 2 .打印/etc/passwd里的用户名、家目录及登录shellawk -F ':' 'BEGIN{print "U_NAME\t\tH_DIR\t\t\tSHELL\n" "*********************************************"} {printf "%-15s %-20s %-10s \n",$1,$(NF-1),$NF}END{print "*********************************************"}' awk_test.txt awk -F : 'BEGIN{OFS="\t\t";print"u_name\t\th_dir\t\t\tshell\n *********************************************"}; {printf "%-15s %-20s %-10s\n",$1,$(NF-1),$NF}; END{print "**********************************************"}' awk_test.txt 结果显示: U_NAME H_DIR SHELL ********************************************* root /root /bin/bash bin /bin /sbin/nologin ......... chrony /var/lib/chrony /sbin/nologin ntp /etc/ntp /sbin/nologin Jermyn /home/Jermyn /bin/bash *********************************************
5.4 awk和正则的综合运用
运算符 说明 == 等于 != 不等于 > 大于 < 小于 >= 大于等于 <= 小于等于 ~ 匹配 !~ 不匹配 ! 逻辑非 && 逻辑与 \ \ 逻辑或
实例:
1 2 3 4 5 1 .从第一行匹配到以 daemon 开头的行[root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash bin:x:1 :1 :bin:/bin:/sbin/nologin daemon:x:2 :2 :daemon:/sbin:/sbin/nologin
1 2 3 4 5 2 .从第一行到第3 行 [root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash bin:x:1 :1 :bin:/bin:/sbin/nologin daemon:x:2 :2 :daemon:/sbin:/sbin/nologin
1 2 3 4 5 3 .从以lp 开头的行匹配到第7 行[root @Jermyn shell_scripts ] lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologinsync:x:5 :0 :sync:/sbin:/bin/sync shutdown:x:6 :0 :shutdown:/sbin:/sbin/shutdown
1 2 3 4 5 6 7 8 4 .打印以root开头或者以lp 开头的行 [root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologin[root @Jermyn shell_scripts ] root:x:0 :0 :root:/root:/bin/bash lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologin
1 2 3 4 5 6 7 8 5 .显示5 -10 行 [root @Jermyn shell_scripts ] lp :x:4 :7 :lp :/var/spool/lpd:/sbin/nologinsync: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
1 2 3 4 5 6 6 .打印5 -10 行以shutdown结尾的内容:[root @Jermyn shell_scripts ] shutdown:x:6 :0 :shutdown:/sbin:/sbin/shutdown [root @Jermyn shell_scripts ] shutdown:x:6 :0 :shutdown:/sbin:/sbin/shutdown
1 2 3 4 5 6 7 8 7 .awk 截取IP[root @Jermyn shell_scripts ] 192.168 .88.139 [root @Jermyn shell_scripts ] 192.168 .88.139 255.255 .255.0 192.168 .88.255
awk的脚本编程 6.1 流程控制语句
6.1.1 if 结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 if 语句:if [ xxx ];thenxxx fi 格式: awk 选项 '正则,地址定位{awk语句}' 文件名 { if (表达式){语句1 ;语句2 ;...}} 例子: 1 .UID >=500 && UID <=60000 [root @Jermyn shell_scripts ] polkitd 999 chrony 998 Jermyn 1000 2 .格式打印[root @Jermyn shell_scripts ] root IS ADMIN 3 .执行命令作为判断条件[root @Jermyn shell_scripts ] THIS USER IS ADMIN
6.1.2 if…else 结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if...else语句: if [ xxx ];then xxxxx else xxx fi 格式: {if (表达式){语句;语句;...}else {语句;语句;...}} 1 .执行命令作为判断条件 if...else...结构[root @Jermyn shell_scripts ] THIS USER IS ADMIN
1 2 3 4 5 6 7 8 9 10 11 12 2.如果 if($3>=500 && $3 !=65534 是普通用户 否则不是普通用户 [root@Jermyn shell_scripts]# awk -F ':' '{ if($3>=500 && $3 !=65534 ) {print $1" Is a common user"} else {print $1 " Not ordinary users"}}' awk_test.txt | tail nobody Not ordinary users systemd-network Not ordinary users dbus Not ordinary users polkitd Is a common user tss Not ordinary users sshd Not ordinary users postfix Not ordinary users chrony Is a common user ntp Not ordinary users Jermyn Is a common user
1 2 3 4 5 6 7 awk -F ':' ' { if($3>=500 && $3 !=65534 ) {print $1" Is a common user"} else {print $1 " Not ordinary users"} }' awk_test.txt | tail
1 2 3 3 .if( '$(id -u)' >=500 && '$(id -u)' !=65534 ) [root @Jermyn shell_scripts ] 不是普通用户
1 2 3 4 5 6 awk 'BEGIN{ if( ' $ (id -u )'>=500 && ' $ (id -u )' !=65534 ) {print "是普通用户"} else {print "不是普通用户"} }'
6.1.3 if…elif…else结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if [xxxx ];then xxxx elif [xxx ];then xxx .... else ... fi if...else if...else语句: 格式: { if (表达式1 ){语句;语句;...}else if (表达式2 ){语句;语句;...}else if (表达式3 ){语句;语句;...}else {语句;语句;...}}
1 2 3 4 5 6 7 8 1 .判断用户是 管理员 还是 系统用户,还是 普通用户[root @Jermyn shell_scripts ] root :是管理员 bin :是系统用户 ....... chrony :是普通用户 ntp :是系统用户 Jermyn :是普通用户
1 2 3 4 5 6 7 8 9 awk -F ':' ' { if($3==0) {print $1,":是管理员"} else if($3>=1 && $3<=499 || $3==65534 ) {print $1,":是系统用户"} else {print $1,":是普通用户"} }' awk_test.txt
1 2 3 4 5 2 .判断管理员 和 系统用户以及普通用户的个数[root @Jermyn shell_scripts ] 管理员个数为:1 系统用户个数为:18 普通用户的个数为:3
1 2 3 4 5 6 7 8 9 10 11 awk -F ':' ' { if ($3==0) {i++} else if ( $3>=1 && $3<=499 || $3==65534 ) {j++} else {k++} }; END{print "管理员个数为:"i "\n系统用户个数为:"j"\n普通用户的个数为:"k}' awk_test.txt
6.2 循环语句
6.2.1 for循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 for ((i=1 ;i<=5 ;i++));do echo $i ;doneawk 'BEGIN { for(i=1;i<=5;i++) {print i} }' for ((i=1 ;i<=10 ;i+=2 ));do echo $i ;done|awk '{sum+=$0};END{print sum}' awk 'BEGIN{ for(i=1;i<=10;i+=2) {print i} }' awk 'BEGIN{ for(i=1;i<=10;i+=2) print i }' awk 'BEGIN{sum=0;for(i=1;i<=5;i++) sum+=i;print sum}' awk 'BEGIN{for(i=1;i<=5;i++) (sum+=i);{print sum}}' awk 'BEGIN{for(i=1;i<=5;i++) (sum+=i);print sum}'
6.2.2 while 循环
1 2 3 4 5 6 7 8 9 10 11 12 i=1 ;while (($i <=5 ));do echo $i ;let i++;done awk 'BEGIN { i=1;while(i<=5) {print i;i++} }' awk 'BEGIN{i=1;while(i<=10) {print i;i+=2} }' awk 'BEGIN{i=1;sum=0;while(i<=5) {sum+=i;i++}; print sum }' awk 'BEGIN {i=1;while(i<=5) {(sum+=i) i++};print sum }'
6.2.3 嵌套循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 嵌套循环: for ((y=1 ;y<=5 ;y++))do for ((x=1 ;x<=$y ;x++)) do echo -n $x done echo done awk 'BEGIN{ for(y=1;y<=5;y++) {for(x=1;x<=y;x++) {printf x} ;print } }' 1 12 123 1234 12345 1 12 123 1234 12345 尝试用三种方法打印99 口诀表: 循环的控制: break 条件满足的时候中断循环continue 条件满足的时候跳过循环1 2 1 2 4 5
6.3 awk算数运算
1 2 3 4 5 6 + - * / %(模) ^(幂2 ^3 ) 可以在模式中执行计算,awk都将按浮点数方式执行算术运算
统计案例 7.1 统计系统中各种类型的shell
1 2 3 4 5 6 [root @Jermyn shell_scripts ] /bin/sync 1 /bin/bash 2 /sbin/nologin 17 /sbin/halt 1 /sbin/shutdown 1
1 2 3 4 5 6 7 8 9 10 解释:此命令目的是打印出 shell 解释器的数量 关键在于创建一个关联数组 shells $NF 是每行的最后一列即是 shell 解释器shells[$NF ] 中 $NF 可以理解为下标,也可以理解为是 KEY 值 每行在执行的时候就是 shells[/bin /sync ] ........ ++ 可以理解为是 KEY 对应的 VALUE 值,每次相同的时候就 ++ 即对相同的解释器进行累加 后面的 for 循环可以理解为 for i in shells.KEY 循环遍历数组内的下标 所以 i 就是下标或者理解为 KEY 所以 shells[i ] 就是 shells[KEY ] 可以理解为 KEY.VALUE
7.2 统计网站访问状态
1 2 3 [root @Jermyn shell_scripts ] LISTEN 2 ESTAB 2
7.3 统计访问网站的每个IP的数量
7.4 统计网站日志中PV量
1 2 3 4 5 6 7 8 统计Apache/Nginx日志中某一天的PV量 <统计日志> 14519 统计Apache/Nginx日志中某一天不同IP的访问量 <统计日志>
名词解释:
网站浏览量(PV) 名词:PV=PageView (网站浏览量) 说明:指页面的浏览次数,用以衡量网站用户访问的网页数量。多次打开同一页面则浏览量累计。用户每打开一个页面便记录1次PV。
名词:VV = Visit View(访问次数) 说明:从访客来到您网站到最终关闭网站的所有页面离开,计为1次访问。若访客连续30分钟没有新开和刷新页面,或者访客关闭了浏览器,则被计算为本次访问结束。
独立访客(UV) 名词:UV= Unique Visitor(独立访客数) 说明:1天内相同的访客多次访问您的网站只计算1个UV。
独立IP(IP) 名词:IP=独立IP数 说明:指1天内使用不同IP地址的用户访问网站的数量。同一IP无论访问了几个页面,独立IP数均为1