## 三.shell #### 1、初识shell 计算机编程语言 计算机编程语言是程序设计的最重要的工具,它是指计算机能够接受和处理的、具有一定语法规则的语言 阶段: 机器语言 机器语言是用二进制代码表示的、计算机能直接识别和执行的一种机器指令的集合,第一代计算机语言 汇编语言 用与代码指令实际含义相近的英文缩写词、字母和数字等符号来取代指令代码 高级语言 自然语言相近并为计算机所接受和执行的计算机语言称高级语言;独立于机器面向过程或对象的语言 编译型和解释型语言: 解释型 解释性语言编写的程序不进行预先编译,以文本方式存储程序代码 执行时才翻译执行 程序每执行一次就要翻译一遍 代表语言:python,JavaScript,Shell等 优缺点:跨平台能力强,易于调,执行速度慢 编译型 编译型语言在执行之前要先经过编译过程,编译成为一个可执行的机器语言的文件 因为翻译只做一遍,以后都不需要翻译,所以执行效率高 典型代表:C语言,C++ 优缺点:执行效率高,缺点是跨平台能力弱,不便调试 ##### (1)shell能做什么 自动化批量系统初始化程序 (update,软件安装,时区设置,安全策略...) 自动化批量软件部署程序 (LAMP,LNMP,Tomcat,LVS,Nginx) 应用管理程序(KVM,集群管理扩容,MySQL,DELLR720批量RAID) 日志分析处理程序(PV, UV, 200, [!200](https://gitea.xingdiancloud.com/diandian/shell_mysql.md/pulls/200), top 100, grep/awk) 自动化备份恢复程序(MySQL完全备份/增量 + Crond) 自动化管理程序(批量远程修改密码,软件升级,配置更新) 自动化信息采集及监控程序(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL) 配合Zabbix信息采集(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL) 自动化扩容(增加云主机——>业务上线) Shell可以做任何事(一切取决于业务需求) ##### (2)shell定义 Shell 是命令解释器,用于与操作系统交互,用来完成各种任务,如文件操作、系统管理、网络管理等 Shell 是一种程序设计语言,他有变量,有自己的语法结构等,shell程序设计语言可以编写功能很强、代码简短的程序 ##### (3)命令和文件自动补齐 下载bash-completion 补全工具 ```shell [root@xingdiancloud ~]# yum install bash-completion -y ``` ##### (4)命令历史记忆功能 上下键调用历史命令 获取历史命令的命令:history 获取某一条历史命令:!number 直接执行对应序号的命令 快捷键调用历史命令:Ctrl+r 输入关键字调出之前的命令 ##### (5)快捷键 Ctrl+a 切换到命令行开始 Ctrl+e 切换到命令行末尾 Ctrl+u 清除剪切光标之前的内容 Ctrl+k 清除剪切光标之后的内容 Ctrl+y 粘贴刚才锁删除的字符 Ctrl+左右键 快速移动光标 ##### (6)前后台作业控制 &:后台运行 nohup:正在运行一个进程,退出帐户时该进程不会结束 ^C:终止 ^Z:暂停 jobs:查看后台进行 bg %工作号:将一个在后台暂停的命令,变成继续执行 fg %工作号:将后台中的命令调至前台继续运行 案例: ```shell [root@xingdiancloud ~]# sleep 1000 & [1] 7761 [root@xingdiancloud ~]# jobs [1]+ Running sleep 1000 & [root@xingdiancloud ~]# jobs [1]+ Running sleep 1000 & [root@xingdiancloud ~]# kill -19 %1 [1]+ Stopped sleep 1000 [root@xingdiancloud ~]# jobs [1]+ Stopped sleep 1000 [root@xingdiancloud ~]# bg %1 [1]+ sleep 1000 & ``` ##### (7)输入输出重定向 ```shell 0,1,2 > >> 2> 2>> 2>&1 &> cat < /etc/hosts cat < /proc/self/fd/2 lrwxrwxrwx 1 root root 15 Sep 1 2015 /dev/stdin -> /proc/self/fd/0 lrwxrwxrwx 1 root root 15 Sep 1 2015 /dev/stdout -> /proc/self/fd/1 ``` ##### (8)管道 ```shell [root@xingdiancloud ~]# ip addr |grep 'inet ' |grep eth0 [root@xingdiancloud ~]# ip addr |grep 'inet ' |tee test |grep eth0 覆盖 [root@xingdiancloud ~]# ip addr |grep 'inet ' |tee -a test |grep eth0 -a 追加 ``` ##### (9)命令排序 ; 无论前面是否执行成功,分号后的命令都会继续执行 && 前面执行成功,后面的才继续执行 || 前面命令不成功,后面的命令会继续 ```shell [root@xingdiancloud ~]# ./configure && make && make install (命令返回值 echo $?) [root@xingdiancloud ~]# mkdir /var/111/222/333 && echo ok [root@xingdiancloud ~]# mkdir -p /var/111/222/333 && echo ok [root@xingdiancloud ~]# ls /home/111/222/333/444 || mkdir -p /home/111/222/333/444 ``` ##### (10)shell通配符 - 匹配任意多个字符 ls in* ? 匹配任意一个字符 ll l?ve [] 匹配括号中任意一个字符 [a-z] () 在子shell中执行 (umask 077; touch file1000) {} 集合 touch file{1..9} \ 转义符,让元字符回归本意 ```shell [root@xingdiancloud ~]# echo * [root@xingdiancloud ~]# echo \* [root@xingdiancloud ~]# touch xing\ dian ``` #### 2、shell变量 ##### (1)什么是变量 变量来源于数学,是计算机语言中能储存计算结果或能表示值的抽象概念 变量可以通过变量名访问,在指令式语言中,变量通常是可变的;在某些条件下也是不可变的 ##### (2)变量的规则 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头 中间不能有空格,可以使用下划线 不能使用标点符号 不能使用bash里的关键字 ##### (3)变量分类 ###### 1.自定义变量 定义变量:变量名=变量值 例如:xingdian=123 引用变量:变量名 或变量名或{变量名} 查看变量:echo $变量名 取消变量:unset 变量名 作用范围:仅在当前shell中有效 ###### 2.环境变量 定义环境变量: 方法一 export back_dir2=/home/backup 方法二 export back_dir1 将自定义变量转换成环境变量 引用环境变量:变量名 或变量名或{变量名} 查看环境变量:echo $变量名 取消环境变量:unset 变量名 变量作用范围:在当前shell和子shell有效 注意: 环境变量拥有可继承性:export之后就拥有继承性 永久生效:写到环境变量脚本,/etc/profile ~/.baserc ~/.bash_profile /etc/bashrc 案例: ```shell [root@xingdiancloud ~]# vim /etc/profile JAVA_HOME=/usr/local/java PATH=$JAVA_HOME/bin:$PATH export JAVA_HOME PATH [root@xingdiancloud ~]# vim ~/.bash_profile (只显示部分) PATH=$PATH:$HOME/bin:/usr/local/mycat/bin ``` /etc/profile 这是系统最主要的shell设置文件,也是用户登陆时系统最先检查的文件,有关重要的环境变量都定义在此,其中包括PATH,USER,LOGNAME,MAIL,HOSTNAME,HISTSIZE,INPUTRC等。而在文件的最后,它会检查并执行/etc/profile.d/*.sh的脚本 ~/.bash_profile 这个文件是每位用户的bash环境设置文件,它存在与于用户的主目录中,当系统执行/etc/profile 后,就会接着读取此文件内的设置值。在此文件中会定义USERNAME,BASH_ENV和PATH等环境变量,但是此处PATH除了包含系统的$PATH变量外加入用户的“bin”目录路径 ~/.bashrc 接下来系统会检查~.bashrc文件,这个文件和前两个文件(/etc/profile 和~.bash_profile)最大的不同是,每次执行bash时,~.bashrc都会被再次读取,也就是变量会再次地设置,而/etc/profile,~./bash_profile只有在登陆时才读取。就是因为要经常的读取,所以~/.bashrc文件只定义一些终端机设置以及shell提示符号等功能,而不是定义环境变量 ~/.bash_login 如果~/.bash_profile文件不存在,则系统会转而读取~/.bash_login这个文件内容。这是用户的登陆文件,在每次用户登陆系统时,bash都会读此内容,所以通常都会将登陆后必须执行的命令放在这个文件中 .profile 如果~./bash_profile ~./bash_login两个文件都不存在,则会使用这个文件的设置内容,其实它的功能与~/.bash_profile相同 .bash_logout 如果想在注销shell前执行一些工作,都可以在此文件中设置 ```shell [root@xingdiancloud ~]# vi ~.bash_logout clear 仅执行一个clear命令在你注销的时候 ``` ~/.bash_history 这个文件会记录用户先前使用的历史命令 注意: 在/etc/profile.d建立独立的环境变量配置文件 常用环境变量:USER UID HOME HOSTNAME PWD PATH PATH:这个变量存放的是所有命令所在的路径 修改:PATH=$PATH:+目录 ###### 3.位置变量 $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 案例: ```shell [root@xingdiancloud sh]# cat xingdian.sh #!/bin/bash echo "hello $1" [root@xingdiancloud sh]# bash xingdian.sh xingdian hello xingdian ``` ###### 4.预定义变量 ```shell $0 脚本名 $* 所有的参数 $@ 所有的参数 $# 参数的个数 $$ 当前进程的PID $! 上一个后台进程的PID $? 上一个命令的返回值 0表示成功 ``` 案例: ```shell [root@xingdiancloud sh]# cat test.sh #!/bin/bash echo "第2个位置参数是$2" echo "第1个位置参数是$1" echo "第4个位置参数是$4" echo "所有参数是: $*" echo "所有参数是: $@" echo "参数的个数是: $#" echo "当前进程的PID是: $$" echo '$1='$1 echo '$2='$2 echo '$3='$3 echo '$*='$* echo '$@='$@ echo '$#='$# echo '$$='$$ ``` #### 3、变量赋值 ###### (1)显示赋值 变量名=变量值 示例: ```shell [root@xingdiancloud ~]# ip1=192.168.1.251 [root@xingdiancloud ~]# school="BeiJing 1000phone" [root@xingdiancloud ~]# today1=`date +%F` [root@xingdiancloud ~]# today2=$(date +%F) ``` ###### (2)键盘读入 ```shell read 变量名 read -p "提示信息: " 变量名 read -t 5 -p "提示信息: " 变量名 -t 后面跟秒数,定义输入字符的等待时间 read -n 2 变量名 -n 后跟一个数字,定义输入文本的长度,很实用。 ``` 案例1: ```shell [root@xingdiancloud ~]# vim first.sh back_dir1=/var/backup read -p "请输入你的备份目录: " back_dir2 echo $back_dir1 echo $back_dir2 [root@xingdiancloud ~]# sh first.sh ``` 案例2: ```shell [root@xingdiancloud ~]# vim ping2.sh #!/bin/bash read -p "Input IP: " ip ping -c2 $ip &>/dev/null if [ $? = 0 ];then echo "host $ip is ok" else echo "host $ip is fail" fi [root@xingdiancloud ~]# chmod a+x ping2.sh [root@xingdiancloud ~]# ./ping.sh ``` 注意:定义或引用变量时注意事项 " " 弱引用 可以实现变量和命令的替换 ' ' 强引用 不完成变量替换 反引 命令替换 等价于 $() 反引号中的shell命令会被先执行 ```shell [root@xingdiancloud ~]# school=1000phone [root@xingdiancloud ~]# echo "${school} is good" 1000phone is good [root@xingdiancloud ~]# echo '${school} is good' ${school} is good [root@xingdiancloud ~]# touch `date +%F`_file1.txt [root@xingdiancloud ~]# touch $(date +%F)_file2.txt [root@xingdiancloud ~]# disk_free3="df -Ph |grep '/$' |awk '{print $4}'" 错误 [root@xingdiancloud ~]# disk_free4=$(df -Ph |grep '/$' |awk '{print $4}') [root@xingdiancloud ~]# disk_free5=`df -Ph |grep '/$' |awk '{print $4} ``` ###### (3)变量运算 1.整数运算 方法一:expr ```shell [root@xingdiancloud ~]# expr 1 + 2 [root@xingdiancloud ~]# expr $num1 + $num2 + - \* / % ``` 方法二:$(()) ```shell [root@xingdiancloud ~]# echo $(($num1+$num2)) + - * / % [root@xingdiancloud ~]# echo $((num1+num2)) [root@xingdiancloud ~]# echo $((5-3*2)) [root@xingdiancloud ~]# echo $(((5-3)*2)) [root@xingdiancloud ~]# echo $((2**3)) [root@xingdiancloud ~]# sum=$((1+2)); echo $sum ``` 方法三:$[] ```shell [root@xingdiancloud ~]# echo $[5+2] + - * / % [root@xingdiancloud ~]# echo $[5**2] ``` 方法四:let ```text [root@xingdiancloud ~]# let sum=2+3; echo $sum [root@xingdiancloud ~]# let i++; echo $i ``` 2.小数运算 使用bc做小数运算,scale指定小数点位数 加法运算(scale参数无效) ```shell [root@xingdiancloud ~]# echo "5.999 + 5.001"|bc 6.000 [root@xingdiancloud ~]# echo "5.111+ 5.1114"|bc 10.2224 ``` 减法运算(scale参数无效) ```shell [root@xingdiancloud ~]# echo "2.22 - 1.11"|bc 1.11 ``` 乘法运算 ```shell [root@xingdiancloud ~]# echo "5.12 * 5.6000"|bc 28.6720 ``` 注意:乘积小数点位数默认以乘数中小数点位数最多的为准(不指定scale参数) 除法运算 ```shell [root@xingdiancloud ~]# echo "scale=2;9.898 / 1.11"|bc 8.91 [root@xingdiancloud ~]# echo "9.898 / 1.11"|bc 8 ``` ###### (4)扩展 1.内容的删除 案例一 ```shell [root@xingdian ~]# url=www.sina.com.cn [root@xingdian ~]# echo ${#url} 获取变量值的长度 15 [root@xingdian ~]# echo ${url} 标准查看 www.sina.com.cn [root@xingdian ~]# echo ${url#*.} 从前往后,最短匹配 sina.com.cn [root@xingdian ~]# echo ${url##*.} 从前往后,最长匹配 贪婪匹配 cn [root@xingdian ~]# url=www.sina.com.cn [root@xingdian ~]# echo ${url#a.} www.sina.com.cn [root@xingdian ~]# echo ${url#*sina.} com.cn ``` 案例二 ```shell [root@xingdian ~]# url=www.sina.com.cn [root@xingdian ~]# echo ${url} www.sina.com.cn [root@xingdian ~]# echo ${url%.*} 从后往前,最短匹配 www.sina.com [root@xingdian ~]# echo ${url%%.*} 从后往前,最长匹配 贪婪匹配 www [root@xingdian ~]# echo $HOSTNAME xingdian.1000phone.com [root@xingdian ~]# echo ${HOSTNAME%%.*} xingdian ``` 2.索引及切片 ```shell [root@xingdian ~]# echo ${url:0:5} 0:从头开始 5:到第五个 [root@xingdian ~]# echo ${url:5:5} [root@xingdian ~]# echo ${url:5} ``` 3.变量内容替换 ```shell [root@xingdian ~]# url=www.sina.com.cn [root@xingdian ~]# echo ${url/sina/baidu} www.baidu.com.cn [root@xingdian ~]# url=www.sina.com.cn [root@xingdian ~]# echo ${url/n/N} www.siNa.com.cn [root@xingdian ~]# echo ${url//n/N} 贪婪匹配 www.siNa.com.cN ``` 4.自增运算 对变量的值的影响 ```shell [root@xingdian ~]# i=1 [root@xingdian ~]# let i++ [root@xingdian ~]# echo $i 2 [root@xingdian ~]# j=1 [root@xingdian ~]# let ++j [root@xingdian ~]# echo $j 2 ``` 对表达式的值的影响 ```shell [root@xingdian ~]# unset i [root@xingdian ~]# unset j [root@xingdian ~]# [root@xingdian ~]# i=1 [root@xingdian ~]# j=1 [root@xingdian ~]# [root@xingdian ~]# let x=i++ 先赋值,再运算 [root@xingdian ~]# let y=++j 先运算,再赋值 [root@xingdian ~]# [root@xingdian ~]# echo $i 2 [root@xingdian ~]# echo $j 2 [root@xingdian ~]# [root@xingdian ~]# echo $x 1 [root@xingdian ~]# echo $y 2 ``` #### 4、流程控制 ##### (1)条件测试 ###### 1.语法格式 ```shell 格式1: test 条件表达式 格式2: [ 条件表达式 ] 格式3: [[ 条件表达式 ]] ``` ###### 2.文件测试 ```shell [ -e dir|file ] [ -d dir ] [ -f file ] 是否存在,而且是文件 [ -r file ] 当前用户对该文件是否有读权限 [ -x file ] [ -w file ] ``` [![微信图片_20240515214230](file:///C:/%5CUsers%5C33163%5CDesktop%5C%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20240515214230.jpg)](file:///C:/\Users\33163\Desktop\微信图片_20240515214230.jpg) 案例 ```shell root@xingdian ~]# test -d /home [root@xingdian ~]# echo $? 0 [root@xingdian ~]# test -d /home11111 [root@xingdian ~]# echo $? 1 [root@xingdian ~]# [ -d /home ] [root@xingdian ~]# [ ! -d /ccc ] && mkdir /ccc [root@xingdian ~]# [ -d /ccc ] || mkdir /ccc ``` ###### 3.数值比较 ```shell [ 1 -gt 10 ] 大于 [ 1 -lt 10 ] 小于 [ 1 -eq 10 ] 等于 [ 1 -ne 10 ] 不等于 [ 1 -ge 10 ] 大于等于 [ 1 -le 10 ] 小于等于 ``` 案例 ```shell [root@xingdian ~]# disk_use=$(df -P |grep '/$' |awk '{print $5}' |awk -F% '{print $1}') [root@xingdian ~]# [ $disk_use -gt 90 ] && echo "war......" [root@xingdian ~]# [ $disk_use -gt 60 ] && echo "war......" [root@xingdian ~]# [ $(id -u) -eq 0 ] && echo "当前是超级用户" 当前是超级用户 [alice@xingdian ~]$ [ $UID -eq 0 ] && echo "当前是超级用户" || echo "you不是超级用户" you不是超级用户 ``` ###### 4.字符串比较 ```shell 提示:使用双引号 [root@xingdian ~]# [ "$USER" = "root" ];echo $? 0 [root@xingdian ~]# [ "$USER" == "root" ];echo $? 0 ``` 注意: ```shell "":弱引用,可以实现变量和命令的替换 [root@xingdian ~]# x=* [root@xingdian ~]# echo "$x" * '':强引用,不完成变量替换 [root@xingdian ~]# x=* [root@xingdian ~]# echo '$x' $x ``` ###### 5.扩展 ```shell [root@xingdian ~]# var1=111 [root@xingdian ~]# var2= [root@xingdian ~]# //var3变量没有定义 [root@xingdian ~]# echo ${#var1} #显示变量中字符串的个数 3 [root@xingdian ~]# echo ${#var2} 0 [root@xingdian ~]# echo ${#var3} 0 [root@xingdian ~]# [ -z "$var1" ];echo $? //-z 变量字符串为空 1 [root@xingdian ~]# [ -z "$var2" ];echo $? 0 [root@xingdian ~]# [ -z "$var3" ];echo $? 0 [root@xingdian ~]# [ -n "$var1" ];echo $? //-n 变量字符串不为空 0 [root@xingdian ~]# [ -n "$var2" ];echo $? 1 [root@xingdian ~]# [ -n "$var3" ];echo $? 1 ``` 案例 ```shell [root@xingdian ~]# [ "$USER" = "root" ];echo $? 0 [root@xingdian ~]# [ "$USER" = "alice" ];echo $? 1 [root@xingdian ~]# [ "$USER" != "alice" ];echo $? 0 [root@xingdian ~]# [ 1 -lt 2 -a 5 -gt 10 ];echo $? 1 [root@xingdian ~]# [ 1 -lt 2 -o 5 -gt 10 ];echo $? 0 [root@xingdian ~]# [[ 1 -lt 2 && 5 -gt 10 ]];echo $? 1 [root@xingdian ~]# [[ 1 -lt 2 || 5 -gt 10 ]];echo $? 0 [root@xingdian ~]# [ "$USER" = "root" ];echo $? 0 ``` ##### (2)条件判断 ###### 1.流程控制 在一个shell脚本中的命令执行顺序称作脚本的流;大多数脚本会根据一个或多个条件来改变它们的流 流控制命令:能让脚本的流根据条件而改变的命令称为条件流控制命令 exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出 条件判断:if代码返回0表示真,非0为假 ###### 2.语法结构 单分支结构 ```shell if 条件测试 then 命令序列 fi ``` 双分支结构 ```shell if 条件测试 then 命令序列 else 命令序列 fi ``` 多分支结构 ```shell if 条件测试1 then 命令序列 [elif 条件测试2 then 命令序列 elif 条件测试3 then 命令序列]... else 命令序列 fi ``` 案例 ```shell if [ "$USER" = "root" ] then if [ $UID -eq 0 ] then echo "the user is root" fi elif echo "……" elif echo "……" else echo "the user is not root" echo "正在给用户授权" fi ``` ##### (3)循环结构 ###### 1.for循环 语法结构 ```shell for i in (取值) 范围{1..20} zhangsan lisi wanger mazi ${array[*]} for 变量名 [ in 取值列表 ] do 循环体 done ``` 案例一 ```shell #!/bin/bash # ping主机测试 for i in {70..100} do ping -c1 10.30.161.$i &> /dev/null if [ $? -eq 0 ] then echo "10.30.161.$i is up" |tee -a ipup.txt else echo "10.30.161.$i is down" |tee -a ipdown.txt fi done ``` 案例二 ```shell #!/usr/bin/env bash #批量添加账户 for i in {30..40} do useradd user$i && echo "123" | passwd --stdin user$i &>/dev/null if [ $? -eq 0 ];then echo "账户 user$i 添加成功" else echo "账户 user$i 添加失败" fi done ``` 案例三 ```shell #!/usr/bin/env bash #批量删除账户 for i in {20..30} do userdel -r user$i &>/dev/null [ $? -eq 0 ] && echo "账户 user$i 删除成功" || echo "账户 user$i 删除失败" done ``` ###### 2.while循环 不断重复的执行循环体中的语句,直到某个条件成立 循环次数不一定是固定的 语法结构 当条件测试成立(条件测试为真),执行循环体 ```text while 条件测试 do 循环体 done ``` 案例一 ```shell echo "please enter a number between 5 and 10 :" read var while [[ $var != 0 ]] do if [ "$var" -lt 5 ] then echo "Too smail , Try again" read var elif [ "$var" -gt 10 ] then echo "Too big , Try again" read var else echo "Congratulation! You are right." exit 0; fi done ``` ###### 3.until循环 当条件测试成立(条件测试为假),执行循环体 语法结构 ```shell until 条件测试 do 循环体 done ``` 案例 ```shell #!/bin/bash i=1 until [ "$i" -eq 21 ] do userdel -r user$i let i++ done ``` ##### (4)case选择语句 ###### 1.语法模式 ```shell read -p "请输入你的选项:" num case num in 1) 选项 命令序列1 命令/if语句/for循环…… ;; 模式2) 命令序列2 ;; 模式3) 命令序列3 ;; *) 无匹配后命令序列 esac ``` 案例一 确定要继续删除吗 yes/no: " yes 案例二 系统管理工具箱 ```shell Command action h 显示命令帮助 f 显示磁盘分区 d 显示磁盘挂载 m 查看内存使用 u 查看系统负载 q 退出程序 Command (h for help): m total used free shared buffers cached Mem: 7628 840 6788 0 29 378 Swap: 2047 0 2047 ``` ###### 2.工具箱 ```shell #!/usr/bin/env bash cat <<-EOF +-------------------------------------------------------------------------+ | System_tools V1.0 | +-------------------------------------------------------------------------+ | a. Stop And Disabled Firewalld. | | b. Disabled SELinux Secure System. | | c. Install Apache Service. | | d. Quit | +-------------------------------------------------------------------------+ EOF printf "\e[1;31m Please input your select: \e[0m" && read var case "$var" in "a") systemctl stop firewalld && systemctl disable firewalld ;; "b") sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config ;; "c") yum -y install httpd httpd-tools ;; "d") exit ;; *) printf "请按照上方提供的选项输入!!!\n" ;; esac ``` ##### (5)循环控制 ###### 1.break 关键字用于跳出循环,执行循环后面的语句 案例 ```shell for i in {1..10} do if [ $i -eq 5 ] then break fi echo $i done # 输出结果为: # 1 # 2 # 3 # 4 ``` ###### 2.continue 跳过当前循环中的剩余语句,直接进入下一次循环 案例 ```shell for i in {1..10} do if [ $i -eq 5 ] then continue fi echo $i done # 输出结果为: # 1 # 2 # 3 # 4 # 6 # 7 # 8 # 9 # 10 ``` ###### 3.sleep sleep 10 等待10秒,再继续下一操作 ##### (6)格式化打印 printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等 语法格式 ```shell [root@xingdiancloud ~]# printf format-string [arguments...] • format-string: 为格式控制字符串 • arguments: 为参数列表 [root@xingdiancloud ~]# echo "Hello, Shell" Hello, Shell [root@xingdiancloud ~]# printf "Hello, Shell" [root@xingdiancloud ~]# printf "Hello, Shell\n" Hello, Shell ``` 2.案例 ```shell #!/bin/bash printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg printf "%-10s %-8s %-4.2f\n" 张三 男 66.1234 printf "%-10s %-8s %-4.2f\n" 李四 男 48.6543 printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876 ``` 3.参数解释 %s 字符串 %f 小数 %d 数字 %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐) %-4.2f 指格式化为小数,其中.2指保留2位小数 \f 换页 \n 换行 \r 回车 ##### (7)颜色 shell脚本中echo显示内容带颜色显示,需要使用到-e参数 1.语法格式 ```shell 格式1:echo -e "\033[背景颜色;字体颜色m 要输出的字符 \033[0m" 格式2:echo -e "\e[背景颜色;字体颜色m 要输出的字符 \e[0m" ``` 案例 ```shell 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” 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” 控制选项: \033[0m:关闭所有属性 \033[1m:高亮显示,加粗 \033[5m:闪烁 ``` #### 5、shell函数 ##### (1)函数介绍 Shell 函数的本质是一段可以重复使用的脚本代码,这段代码被提前编写好了,放在了指定的位置,使用时直接调取即可 ##### (2)语法格式 ```shell function name() { statements [return value] } ``` function是 Shell 中的关键字,专门用来定义函数(可以省略) name是函数名 statements是函数要执行的代码,也就是一组命令 return value表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值(可以省略) ##### (3)函数的定义 ```shell myfunc(){ echo "This is a new function" } ``` ##### (4)函数调用 直接用函数名字调用函数 ##### (5)函数的传参 ```shell [root@xingdiancloud ~]# cat hello.sh #!/bin/bash hello(){ echo $1 } hello xingdian [root@xingdiancloud ~]# bash hello.sh xingdian ``` ##### (6)函数变量 ```shell [root@xingdiancloud ~]# cat hello.sh #!/bin/bash i=0 echo "$a" hello(){ a=1 local d=3 echo "$i $a $b $c $d" } hello b=2 echo "$a $d" [root@xingdiancloud ~]# bash hello.sh 0 1 3 1 ``` 注意: 默认,函数里的变量会在函数外面生效 注意脚本中内容按上下文顺序执行 local定义的变量只在函数内生效 ##### (7)调用函数 创建功能函数 ```shell [root@xingdiancloud ~]# cat hello.sh #!/bin/bash hello(){ echo "This is one" } ``` 另一个脚本调用该脚本中函数 ```shell [root@xingdiancloud ~]# cat xingdian.sh #!/bin/bash source ./hello.sh hello [root@xingdiancloud ~]# bash xingdian.sh This is one ``` ##### (8)函数的案例 ```shell #!/bin/bash #v1.24.5.27.1 #by xh list() { echo "+++++++++++++++++++++++++++++++++" echo "+++++++ 百宝箱 ++++++++" echo "+++++++++++++++++++++++++++++++++" echo "|||||||||||||||||||||||||||||||||" echo "=================================" echo "= 1.yum仓库初始化 =" echo "= 2.上课笔记工具安装 =" echo "= 3.kvm虚拟机安装 =" echo "= 4.vmware虚拟机安装 =" echo "= 5.vs code安装 =" echo "= 6.google浏览器安装 =" echo "= 7.vnc-server的安装 =" echo "= 8.一键安装所有 =" echo "= 9.退出 =" echo "=================================" } yum-install(){ echo "====正在执行yum初始化操作请耐心等待!====" rm -rf /etc/yum.repos.d/* curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo > /dev/null yum -y install wget > /dev/null wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo echo "====正在清空缓存,请耐心等待!====" yum clean all echo "====正在重新加载,请耐心等待!====" yum makecache echo "====successed====" } cherrytree(){ echo "====正在进行安装......=====" yum -y install cherrytree > /dev/null if [ $? -eq 0 ];then echo "====successed====" else echo "==== failed ====" echo "====检查网络和yum仓库====" exit fi } kvm-install(){ echo "====正在安装kvm虚拟机====" yum -y install libvirt* virt-manager >> /dev/null && yum -y groupinstall 'Virtualization Host' >> /dev/null echo "==== successed ====" } vmware-install(){ echo "====请将vmware安装包放到当前目录下====" chmod +x echo "......." echo "..........." echo ".................100%" } data=`date | awk '{print $4}'` read -p "当前时间为${data},你是否要进行电脑初始化,继续请按回车(已记录你的初始时间):" ping -c1 www.baidu.com 1> /dev/null if [ $? -eq 0 ];then echo "网络状况良好,请继续~" else echo "网络状况不佳,检查网络~" exit fi echo "xingdian" > user.txt echo "dianye" > password.txt read -p "欢迎使用行癫工具箱,进行安装部署操作:" read -p "请输入用户名:" name username=`cat user.txt | awk '{print $1}'` passwd=`cat password.txt | awk '{print $1}'` if [ "${name}" == "${username}" ];then read -p "请输入密码:" password if [ "${password}" == "${passwd}" ];then echo "登陆成功,进入工具箱" while : do list read -p "请选择你要使用的工具代码:" num case $num in 1) yum-install sleep 3 ;; 2) cherrytree sleep 3 ;; 3) ;; 4) ;; 9) break ;; esac done data2=`date | awk '{print $4}'` echo "结束时间为${data2};感谢您的使用!" else echo "用户名密码错误,请重新执行脚本!" exit fi else echo "用户名输入错误,请重新输入!" exit fis ``` #### 6、shell数组 ##### (1)数组介绍 是若干数据的集合,其中的每一份数据都称为元素 shell不限制数组的大小,理论上可以存放无限量的数据 ##### (2)数组分类 普通数组 只能使用整数作为数组索引/下标(从0开始) 关联数组 可以使用字符串作为数组索引/下标 ##### (3)定义数组 普通数组 方法一: 一次赋一个值 ```shell 数组名[索引]=变量值 [root@xingdiancloud ~]# array1[0]=pear [root@xingdiancloud ~]# array1[1]=apple [root@xingdiancloud ~]# array1[2]=orange [root@xingdiancloud ~]# array1[3]=peach ``` 方法二: 一次赋多个值 ```shell [root@xingdiancloud ~]# array2=(tom jack alice) [root@xingdiancloud ~]# array5=(tom jack alice "bash shell") [root@xingdiancloud ~]# colors=($red $blue $green $recolor) [root@xingdiancloud ~]# array5=(1 2 3 4 5 6 7 "linux shell" [20]=saltstack) [root@xingdiancloud ~]# array8=`cat /etc/passwd` ``` 关联数组 申明该数组为关联数组 ```shell [root@xingdiancloud ~]# declare -A ass_array1 [root@xingdiancloud ~]# declare -A ass_array2 ``` 方法一: 一次赋一个值 ```shell 数组名[索引]=变量值 [root@xingdiancloud ~]# ass_array1[index1]=pear [root@xingdiancloud ~]# ass_array1[index2]=apple [root@xingdiancloud ~]# ass_array1[index3]=orange [root@xingdiancloud ~]# ass_array1[index4]=peach ``` 方法二: 一次赋多个值 ```shell [root@xingdiancloud ~]# ass_array2=([index1]=tom [index2]=jack [index3]=alice [index4]='bash shell') ``` 一步走 ```shell [root@xingdiancloud ~]# declare -A ass_array1='([index4]="peach" [index1]="pear" [index2]="apple" [index3]="orange" )' [root@xingdiancloud ~]# declare -A ass_array2='([index4]="bash shell" [index1]="tom" [index2]="jack" [index3]="alice" )' ``` ##### (4)访问数组元素 普通数组 ```shell [root@xingdiancloud ~]# echo ${array1[0]} 访问数组中的第一个元数 [root@xingdiancloud ~]# echo ${array1[@]} 访问数组中所有元数 等同于 echo ${array1[*]} [root@xingdiancloud ~]# echo ${#array1[@]} 统计数组元数的个数 [root@xingdiancloud ~]# echo ${!array2[@]} 获取数组元数的索引 [root@xingdiancloud ~]# echo ${array1[@]:1} 从数组下标1开始 [root@xingdiancloud ~]# echo ${array1[@]:1:2} 从数组下标1开始,访问两个元素 ``` 关联数组 ```shell [root@xingdiancloud ~]# echo ${ass_array2[index2]} 访问数组中的第二个元数 [root@xingdiancloud ~]# echo ${ass_array2[@]} 访问数组中所有元数 等同于 echo ${array1[*]} [root@xingdiancloud ~]# echo ${#ass_array2[@]} 获得数组元数的个数 [root@xingdiancloud ~]# echo ${!ass_array2[@]} 获得数组元数的索引 ``` ##### (5)数组遍历 方法一: 通过数组元数的个数进行遍历 方法二: 通过数组元数的索引进行遍历 案例一:利用元素进行遍历 ```shell #!/bin/bash #定义数组 array=(Mon Tue Wed Thur Fir Sat Sun) #数组遍历 for day in ${array[*]} do echo $day done ``` 案例二:利用索引进行遍历 ```text [root@xingdiancloud ~]# cat hello.sh #!/bin/bash for line in `cat /etc/hosts` do hosts[++j]=$line done for i in ${!hosts[@]} do echo "$i : ${hosts[i]}" done [root@xingdiancloud ~]# bash hello.sh 1 : 127.0.0.1 2 : localhost 3 : localhost.localdomain 4 : localhost4 5 : localhost4.localdomain4 6 : ::1 7 : localhost 8 : localhost.localdomain 9 : localhost6 10 : localhost6.localdomain6 ``` 定义换行符 ```shell [root@xingdiancloud ~]# cat hello.sh #!/bin/bash IFS=$'\n' for line in `cat /etc/hosts` do hosts[++j]=$line done for i in ${!hosts[@]} do echo "$i : ${hosts[i]}" done [root@xingdiancloud ~]# bash hello.sh 1 : 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 2 : ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 ``` ##### (6)项目案例 通过数组统计性别:把要统计的对象作为数组的索引 ```shell [root@xingdiancloud ~]# cat sex.txt zhangsan f lisi m [root@xingdiancloud ~]# cat sex.sh #!/usr/bin/bash declare -A sex while read line do type=`echo $line | awk '{print $2}'` let sex[$type]++ done < sex.txt #m作为数组的索引 for i in ${!sex[@]} do echo "$i : ${sex[$i]}" done ``` #### 7、shell正则 ##### (1)正则介绍 正则表达式(regular expression, RE)是一种字符模式,用于在查找过程中匹配指定的字符 在大多数程序里,正则表达式都被置于两个正斜杠之间;例如/l[oO]ve/就是由正斜杠界定的正则表达式 它将匹配被查找的行中任何位置出现的相同模式。在正则表达式中,元字符是最重要的概念 重要的文本处理工具:vim、sed、awk、grep 重要的应用软件:mysql、oracle、php、python、Apache、Nginx ... 案例: ```shell 匹配数字: ^[0-9]+$ 123 456 +表示前面的内容出现多次 匹配Mail: [a-z0-9_]+@[a-z0-9]+\.[a-z]+ xingdian131420@126.com 匹配IP: [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} {1,3}数字出现1-3次 [root@xingdian ~]# egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/sysconfig/network-scripts/ifcfg-eth0 IPADDR=172.16.100.1 NETMASK=255.255.255.0 GATEWAY=172.16.100.25 ``` ##### (2)元字符 元字符是这样一类字符,它们表达的是不同于字面本身的含义 shell元字符(也称为通配符) 正则表达式元字符 案例: ```shell [root@xingdian ~]# rm -rf *.pdf [root@xingdian ~]# grep 'abc*' /etc/passwd abrt:x:173:173::/etc/abrt:/sbin/nologin ``` ##### (3)正则表达式元字符 基本正则表达式元字符: ```shell 元字符 功能 示例 ^ 行首定位符 ^love $ 行尾定位符 love$ . 匹配单个字符 l..e * 匹配前导符0到多次 ab*love .* 任意多个字符 [] 匹配指定范围内的一个字符 [lL]ove [ - ] 匹配指定范围内的一个字符 [a-z0-9]ove [^] 匹配不在指定组内的字符 [^a-z0-9]ove \ 用来转义元字符 love\. \< 词首定位符 \ 词尾定位符 love\> \(..\) 匹配稍后使用的字符的标签 :% s/172.16.130.1/172.16.130.5/ :% s/\(172.16.130.\)1/\15/ :% s/\(172.\)\(16.\)\(130.\)1/\1\2\35/ :3,9 s/\(.*\)/#\1/ x\{m\} 字符x重复出现m次 o\{5\} x\{m,\} 字符x重复出现m次以上 o\{5,\} x\{m,n\} 字符x重复出现m到n次 o\{5,10\} ``` 扩展正则表达式元字符 ```shell + 匹配一个或多个前导字符 [a-z]+ove ? 匹配零个或一个前导字符 lo?ve a|b 匹配a或b love|hate () 组字符 loveable|rs love(able|rs) ov+ (ov)+ (..)(..)\1\2 标签匹配字符 (love)able\1er x{m} 字符x重复m次 o{5} x{m,} 字符x重复至少m次 o{5,} x{m,n} 字符x重复m到n次 o{5,10} ``` ##### (4)正则匹配示例 ```shell /love/ /^love/ /love$/ /l.ve/ /lo*ve/ /[Ll]ove/ /love[a-z]/ /love[^a-zA-Z0-9]/ /.*/ /^$/ /^[A-Z]..$/ /^[A-Z][a-z ]*3[0-5]/ /[a-z]*\./ /^[A-Z][a-z][a-z]$/ /^[A-Za-z]*[^,][A-Za-z]*$/ /\/ /\/ /5{2}2{3}\./ 5{2} 空行 /^$/ ``` ##### (5)正则案例 ```shell #!/bin/bash read -p "please input number:" num if [[ ! "$num" =~ ^[0-9]+$ ]] then echo "error number!" else echo "is number!" fi ``` #### 8、shell三剑客 ##### (1)非交互式编辑器sed sed 是一种在线的、非交互式的编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出;Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等 2语法格式 ```shell sed [options] 'command' in_file[s] ``` options部分 ```text -n 静默输出(不打印默认输出) sed -n '1p' a.txt 想显示第几行就显示第几行 -e 给予sed多个命令的时候需要-e选项 #sed -e 's/root/haha/g' -e 's/bash/wwwww/g' passwd > passwd.bak 如果不用-e选项也可以用分号“;”把多个命令隔开。 #sed 's/haha/ro/g ; s/wwwww/kkkk/g' passwd | less 这个是-e的结果 -i -i后面没有扩展名的话直接修改文件,如果有扩展名备份源文件,产生以扩展名结尾的新文件 #sed -iback1 -e 's/root/rottt/g' -e 's/bash/wwwww/g' passwd //选项-i后面没有空格 [root@localhost 桌面]# ls manifest.txt passwdback1 -f 当有多个要编辑的项目时,可以将编辑命令放进一个脚本里,再使用sed搭配-f选项 [root@localhost 桌面]# cat s.sed s/bin/a/g s/ftp/b/g s/mail/c/g [root@localhost 桌面]# sed -f s.sed passwd | less ``` 注意: 基本正则 sed 扩展正则 sed -r 无论是扩展正则还是基本正则,全部加r参数 command部分 ```shell p 打印行 1p 输出再打印一遍第一行 1~2 打印奇数 0~2打印偶数 d 删除文本 #sed '1 d' passwd #sed '$ d' passwd #sed '1,3 d' passwd #sed '1,/^dian/ d' passwd a 追加文本(后) #sed '2 a nihao' passwd #sed '/^dian/ a nihao' passwd i 前插 # sed -i '1 i nihao' passwd c 替换 sed '/zhong/c abc' 将zhong这一行替换成abc #sed -i '1 c no' passwd ``` 3.sed案例 ```shell 1. sed可以从stdin中读取内容 $ cat filename | sed 's/pattern/replace_string/' 2. 选项-i会使得sed用修改后的数据替换原文件 $ sed -i 's/pattern/replace_string/' filename 3. g标记可以使sed执行全局替换 $ sed 's/pattern/replace_string/g' filename 4. g标记可以使sed匹配第N次以后的字符被替换 $ echo "thisthisthisthis" | sed 's/this/THIS/2g' 5. sed中的分隔符可以替换成别的字符, 因为s标识会认为后面的字符为分隔符 $ sed 's:text:replace_text:' $ sed 's|text|replace_text|' 6. sed可以利用指令来删除文件中的空行 $ sed '/^$/d' filename 7. 替换指定的字符串或数字 $ cat sed_data.txt 11 abc 111 this 9 file contains 111 11 99 numbers 0000 $ sed -i 's/\b[0-9]\{3\}\b/NUMBER/g' sed_data.txt $ cat sed_data.txt 11 abc NUMBER this 9 file contains NUMBER 11 99 numbers 0000 8. 由于在使用-i参数时比较危险, 所以我们在使用i参数时在后面加上.bak就会产生一个备份的文件,以防后悔 $ sed -i.bak 's/pattern/replace_string/' filename ``` ##### (2)文本处理awk 1.awk介绍 awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展 2.语法格式 ```shell awk [options] 'commands' filenames ``` options部分 ```shell POSIX options: GNU long options: (standard) -f progfile --file=progfile 指定awk脚本文件 -F fs --field-separator=fs 定义输入字段分隔符,默认的分隔符是空格或制表符(tab) -v var=val --assign=var=val 定义变量并赋值 ``` command部分 ```shell awk BEGIN{} {} END{} 文件 BEGIN{} {} END{} 行处理前 行处理 行处理后 ``` BEGIN{} 所有文本内容读入之前要执行的命令 可以不需要后面跟文件,因为他是在读入文件之前的操作 {} 主输入循环 读入一行命令执行一次循环 END{} 所有文本都读入完成之后执行的命令 必须要读入文件,因为他是在读入文件之后的操作 案例: ```shell # awk 'BEGIN{print 1/2} {print "ok"} END{print "-----------"}' /etc/hosts 0.5 ok ok ok ----------- BEGIN{} 通常用于定义一些变量,例如BEGIN{FS=":";OFS="---"} ``` 常用案例: ```shell awk 'pattern' filename 示例:awk -F: '/root/' /etc/passwd awk '{action}' filename 示例:awk -F: '{print $1}' /etc/passwd awk 'pattern {action}' filename 示例:awk -F: '/root/{print $1,$3}' /etc/passwd 示例:awk 'BEGIN{FS=":"} /root/{print $1,$3}' /etc/passwd command |awk 'pattern {action}' 示例:df -P| grep '/' |awk '$4 > 25000 {print $4}' ``` 3.工作原理 ```shell [root@xingdiancloud ~]# awk -F: '{print $1,$3}' /etc/passwd (1)awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束 root : x : 0 : 0 : root : /root : /bin/bash 1 2 3 4 5 6 7 (2)然后,行被:(默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始,最多达100个字段 (3)awk如何知道用空格来分隔字段的呢? 因为有一个内部变量FS来确定字段分隔符。初始时,FS赋为空格 (4)awk打印字段时,将以设置的方法使用print函数打印,awk在打印的字段间加上空格,因为$1,$3之间有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格 (5)awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕 ``` 4内建变量 ```shell $0: awk变量$0保存当前记录的内容 [root@xingdiancloud ~]# awk -F: '{print $0}' /etc/passwd NR: The total number of input records seen so far. [root@xingdiancloud ~]# awk -F: '{print NR, $0}' /etc/passwd /etc/hosts FNR: The input record number in the current input file [root@xingdiancloud ~]# awk -F: '{print FNR, $0}' /etc/passwd /etc/hosts NF: 保存记录的字段数,$1,$2...$100 [root@xingdiancloud ~]# awk -F: '{print $0,NF}' /etc/passwd FS: 输入字段分隔符,默认空格 [root@xingdiancloud ~]# awk -F: '/alice/{print $1, $3}' /etc/passwd [root@xingdiancloud ~]# awk -F'[ :\t]' '{print $1,$2,$3}' /etc/passwd [root@xingdiancloud ~]# awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd OFS: 输出字段分隔符 [root@xingdiancloud ~]# awk -F: '/alice/{print $1,$2,$3,$4}' /etc/passwd [root@xingdiancloud ~]# awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2,$3,$4}' passwd RS The input record separator, by default a newline. 默认是回车 [root@xingdiancloud ~]# awk -F: 'BEGIN{RS=" "} {print $0}' a.txt ORS The output record separator, by default a newline. [root@xingdiancloud ~]# awk -F: 'BEGIN{ORS=""} {print $0}' passwd ``` 注意: 字段分隔符: FS OFS 默认空格或制表符 记录分隔符: RS ORS 默认换行符 案例: ```shell [root@xingdiancloud ~]# awk 'BEGIN{ORS=" "} {print $0}' /etc/passwd #将文件每一行合并为一行 ORS默认输出一条记录应该回车,加了一个空格 [root@xingdiancloud ~]# head -1 /etc/passwd > passwd1 [root@xingdiancloud ~]# cat passwd1 root:x:0:0:root:/root:/bin/bash [root@xingdiancloud ~]# [root@xingdiancloud ~]# awk 'BEGIN{RS=":"} {print $0}' passwd1 root x 0 0 root /root /bin/bash [root@xingdiancloud ~]# awk 'BEGIN{RS=":"} {print $0}' passwd1 |grep -v '^$' > passwd2 ``` 5.格式化输出 print函数 ```shell [root@xingdiancloud ~]# date |awk '{print "Month: " $2 "\nYear: " $NF}' [root@xingdiancloud ~]# awk -F: '{print "username is: " $1 "\t uid is: " $3}' /etc/passwd [root@xingdiancloud ~]# awk -F: '{print "\tusername and uid: " $1,$3 "!"}' /etc/passwd ``` printf函数 ```shell [root@xingdiancloud ~]# awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd [root@xingdiancloud ~]# awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd %s 字符类型 %d 数值类型 %f 浮点类型 占15字符 - 表示左对齐,默认是右对齐 printf默认不会在行尾自动换行,加\n ``` 6.awk模式和动作 任何awk语句都由模式和动作组成。模式部分决定动作语句何时触发及触发事件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保持执行状态。模式可以是任何条件语句或复合语句或正则表达式。模式包括两个特殊字段 BEGIN和END。使用BEGIN语句设置计数和打印头。BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文本开始执行。END语句用来在awk完成文本浏览动作后打印输出文本总数和结尾状态 模式 正则表达式 ```shell 匹配记录(整行):~匹配 [root@xingdiancloud ~]# awk '/^alice/' /etc/passwd [root@xingdiancloud ~]# awk '$0 ~ /^alice/' /etc/passwd [root@xingdiancloud ~]# awk '!/alice/' passwd [root@xingdiancloud ~]# awk '$0 !~ /^alice/' /etc/passwd 匹配字段:匹配操作符(~ !~) [root@xingdiancloud ~]# awk -F: '$1 ~ /^alice/' /etc/passwd [root@xingdiancloud ~]# awk -F: '$NF !~ /bash$/' /etc/passw ``` 比较表达式 比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。比较表达式使用关系运算符,用于比较数字与字符串 ```shell 运算符 含义 示例 < 小于 x= 大于等于 x>=y > 大于 x>y [root@xingdiancloud ~]# awk -F: '$3 == 0' /etc/passwd [root@xingdiancloud ~]# awk -F: '$3 < 10' /etc/passwd [root@xingdiancloud ~]# awk -F: '$NF == "/bin/bash"' /etc/passwd [root@xingdiancloud ~]# awk -F: '$1 == "alice"' /etc/passwd [root@xingdiancloud ~]# awk -F: '$1 ~ /alic/ ' /etc/passwd [root@xingdiancloud ~]# awk -F: '$1 !~ /alic/ ' /etc/passwd [root@xingdiancloud ~]# df -P | grep '/' |awk '$4 > 25000' ``` 条件表达式 ```shell [root@xingdiancloud ~]# awk -F: '$3>300 {print $0}' /etc/passwd [root@xingdiancloud ~]# awk -F: '{ if($3>300) {print $0} }' /etc/passwd [root@xingdiancloud ~]# awk -F: '{ if($3>300) {print $3} else{print $1} }' /etc/passwd ``` 算术运算 ```shell + - * / %(模) ^(幂2^3) [root@xingdiancloud ~]# awk -F: '$3 * 10 > 500' /etc/passwd [root@xingdiancloud ~]# awk -F: '{ if($3*10>500){print $0} }' /etc/passwd ``` 逻辑操作符和复合模式 ```shell && 逻辑与 a&&b || 逻辑或 a||b ! 逻辑非 !a 除了这个以外的 [root@xingdiancloud ~]# awk -F: '$1~/root/ && $3<=15' /etc/passwd [root@xingdiancloud ~]# awk -F: '$1~/root/ || $3<=15' /etc/passwd [root@xingdiancloud ~]# awk -F: '!($1~/root/ || $3<=15)' /etc/passwd ``` 示例 ```ini [root@xingdiancloud ~]# awk '/west/' datafile [root@xingdiancloud ~]# awk '/^north/' datafile [root@xingdiancloud ~]# awk '$3 ~ /^north/' datafile [root@xingdiancloud ~]# awk '/^(no|so)/' datafile [root@xingdiancloud ~]# awk '{print $3,$2}' datafile [root@xingdiancloud ~]# awk '{print $3 $2}' datafile [root@xingdiancloud ~]# awk '{print $0}' datafile [root@xingdiancloud ~]# awk '/northeast/{print $3,$2}' datafile [root@xingdiancloud ~]# awk '/E/' datafile [root@xingdiancloud ~]# awk '/^[ns]/{print $1}' datafile [root@xingdiancloud ~]# awk '$5 ~ /\.[7-9]+/' datafile [root@xingdiancloud ~]# awk '$2 !~ /E/{print $1,$2}' datafile [root@xingdiancloud ~]# awk '$3 ~ /^Joel/{print $3 " is a nice boy."}' datafile [root@xingdiancloud ~]# awk '$8 ~ /[0-9][0-9]$/{print $8}' datafile [root@xingdiancloud ~]# awk '$4 ~ /Chin$/{print "The price is $" $8 "."}' datafile [root@xingdiancloud ~]# awk '/Tj/{print $0}' datafile [root@xingdiancloud ~]# awk '{print $1}' /etc/passwd [root@xingdiancloud ~]# awk -F: '{print $1}' /etc/passwd [root@xingdiancloud ~]# awk '{print "Number of fields: "NF}' /etc/passwd [root@xingdiancloud ~]# awk -F: '{print "Number of fields: "NF}' /etc/passwd [root@xingdiancloud ~]# awk -F"[ :]" '{print NF}' /etc/passwd [root@xingdiancloud ~]# awk -F"[ :]+" '{print NF}' /etc/passwd [root@xingdiancloud ~]# awk '$7 == 5' datafile [root@xingdiancloud ~]# awk '$2 == "CT" {print $1, $2}' datafile [root@xingdiancloud ~]# awk '$7 != 5' datafile [root@xingdiancloud ~]# cat b.txt xingdian sheng:is a::good boy! [root@xingdiancloud ~]# awk '{print NF}' b.txt 4 [root@xingdiancloud ~]# awk -F: '{print NF}' b.txt 4 [root@xingdiancloud ~]# awk -F"[ :]" '{print NF}' b.txt 7 [root@xingdiancloud ~]# awk -F"[ :]+" '{print NF}' b.txt 6 [root@xingdiancloud ~]# awk '$7 < 5 {print $4, $7}' datafile #{if($7<5){print $4,$7}} [root@xingdiancloud ~]# awk '$6 > 9 {print $1,$6}' datafile [root@xingdiancloud ~]# awk '$8 <= 17 {print $8}' datafile [root@xingdiancloud ~]# awk '$8 >= 17 {print $8}' datafile [root@xingdiancloud ~]# awk '$8 > 10 && $8 < 17' datafile [root@xingdiancloud ~]# awk '$2 == "NW" || $1 ~ /south/ {print $1, $2}' datafile [root@xingdiancloud ~]# awk '!($8 == 13){print $8}' datafile #$8 != 13 [root@xingdiancloud ~]# awk '/southem/{print $5 + 10}' datafile [root@xingdiancloud ~]# awk '/southem/{print $8 + 10}' datafile [root@xingdiancloud ~]# awk '/southem/{print $5 + 10.56}' datafile [root@xingdiancloud ~]# awk '/southem/{print $8 - 10}' datafile [root@xingdiancloud ~]# awk '/southem/{print $8 / 2 }' datafile [root@xingdiancloud ~]# awk '/southem/{print $8 / 3 }' datafile [root@xingdiancloud ~]# awk '/southem/{print $8 * 2 }' datafile ``` 7.脚本编程-条件判断 if语句 ```shell {if(表达式){语句;语句;...}} [root@xingdiancloud ~]# awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd [root@xingdiancloud ~]# awk -F: '{if($3>0 && $3<1000){count++;}} END{print count}' /etc/passwd ``` if...else语句 ```shell {if(表达式){语句;语句;...}else{语句;语句;...}} [root@xingdiancloud ~]# awk -F: '{if($3==0){print $1} else {print $7}}' /etc/passwd [root@xingdiancloud ~]# awk -F: '{if($3==0) {count++} else{i++} }' /etc/passwd [root@xingdiancloud ~]# awk -F: '{if($3==0){count++} else{i++}} END{print "管理员个数: "count ; print "系统用户数: "i}' /etc/passwd ``` if...else if...else语句 ```shell {if(表达式1){语句;语句;...}else if(表达式2){语句;语句;...}else if(表达式3){语句;语句;...}else{语句;语句;...} [root@xingdiancloud ~]# awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print i; print k; print j}' /etc/passwd [root@xingdiancloud ~]# awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理员个数: "i; print "普通用个数: "k; print "系统用户: "j}' /etc/passwd ``` 8脚本编程-循环 while: ```shell [root@xingdiancloud ~]# awk 'BEGIN{ i=1; while(i<=10){print i; i++} }' [root@xingdiancloud ~]# awk -F: '/^root/{i=1; while(i<=7){print $i; i++}}' passwd [root@xingdiancloud ~]# awk '{i=1; while(i<=NF){print $i; i++}}' /etc/hosts [root@xingdiancloud ~]# awk -F: '{i=1; while(i<=10) {print $0; i++}}' /etc/passwd //将每行打印10次 [root@xingdiancloud ~]# cat b.txt 111 222 333 444 555 666 777 888 999 [root@xingdiancloud ~]# awk '{i=1; while(i<=NF){print $i; i++}}' b.txt //分别打印每行的每列 111 222 333 444 555 666 777 888 999 ``` for: ```she [root@xingdiancloud ~]# awk 'BEGIN{for(i=1;i<=5;i++){print i} }' //C风格for 1 2 3 4 5 [root@xingdiancloud ~]# awk -F: '{ for(i=1;i<=10;i++) {print $0} }' /etc/passwd //将每行打印10次 [root@xingdiancloud ~]# awk -F: '{ for(i=1;i<=NF;i++) {print $i} }' passwd //分别打印每行的每列 root x 0 0 root /root /bin/bash bin x 1 1 bin /bin /sbin/nologin ``` 9.脚本变成-数组 案例一: ```shell [root@xingdiancloud ~]# awk -F: '{username[i++]=$1} END{print username[1]}' /etc/passwd bin [root@xingdiancloud ~]# awk -F: '{username[i++]=$1} END{print username[0]}' /etc/passwd root ``` 数组遍历: 按元素个数遍历 ```shell [root@xingdiancloud ~]# awk -F: '{username[x++]=$1} END{for(i=0;i,\(\),\{\}, \+, \| egrep(或grep -E): 使用扩展元字符集 ?, +, { }, |, ( ) 注:grep也可以使用扩展集中的元字符,仅需要对这些元字符前置一个反斜线 \w 所有字母与数字,称为字符[a-zA-Z0-9] 'l[a-zA-Z0-9]*ve' 'l\w*ve' \W 所有字母与数字之外的字符,称为非字符 'love[^a-zA-Z0-9]+' 'love\W+' \b 词边界 '\' '\blove\b' ``` 3grep示例 ```shell [root@xingdiancloud ~]# egrep 'N\W' datafile [root@xingdiancloud ~]# egrep '^n' datafile [root@xingdiancloud ~]# egrep '4$' datafile [root@xingdiancloud ~]# egrep '5\..' datafile [root@xingdiancloud ~]# egrep '\.5' datafile [root@xingdiancloud ~]# egrep '^[we]' datafile [root@xingdiancloud ~]# egrep '[^0-9]' datafile [root@xingdiancloud ~]# egrep '[A-Z][A-Z] [A-Z]' datafile [root@xingdiancloud ~]# egrep 'ss* ' datafile [root@xingdiancloud ~]# egrep '[a-z]{9}' datafile [root@xingdiancloud ~]# egrep '\' datafile [root@xingdiancloud ~]# egrep '\<[a-r].*n\>' datafile [root@xingdiancloud ~]# egrep '^n\w*\W' datafile [root@xingdiancloud ~]# egrep '\bnorth\b' datafile [root@xingdiancloud ~]# egrep '3+' datafile [root@xingdiancloud ~]# egrep '2\.?[0-9]' datafile [root@xingdiancloud ~]# egrep '(no)+' datafile [root@xingdiancloud ~]# egrep 'S(h|u)' datafile ``` 4grep参数 ```shell -i, --ignore-case 忽略大小写 -l, --files-with-matches 只列出匹配行所在的文件名 -n, --line-number 在每一行前面加上它在文件中的相对行号 -c, --count 显示成功匹配的行数 -s, --no-messages 禁止显示文件不存在或文件不可读的错误信息 -q, --quiet, --silent 静默--quiet, --silent -v, --invert-match 反向查找,只显示不匹配的行 -R, -r, --recursive 递归针对目录 --color 颜色 -o, --only-matching 只显示匹配的内容 ``` 示例: ```shell [root@xingdian ~]# grep -R 'ifcfg' /etc 目录 [root@xingdian ~]# egrep 'root' /etc/passwd /etc/shadow /etc/hosts /etc/passwd:root:x:0:0:root:/root:/bin/bash /etc/passwd:operator:x:11:0:operator:/root:/sbin/nologin /etc/shadow:root:$6$gcO6Vp4t$OX9LmVgpjtur67UQdUYfw7vJW.78.uRXCLIxw4mBk82Z99:7::: [root@xingdian ~]# egrep -l 'root' /etc/passwd /etc/shadow /etc/hosts /etc/passwd /etc/shadow [root@xingdian ~]# egrep -n 'root' /etc/passwd /etc/shadow /etc/hosts /etc/passwd:1:root:x:0:0:root:/root:/bin/bash /etc/passwd:11:operator:x:11:0:operator:/root:/sbin/nologin /etc/shadow:1:root:$6$gcO6Vp4t$OX9LmVgpjtur67UQdUy8.M78.uRXCLIxw4mBk82ZrNlxyf54 [root@xingdian ~]# egrep -R '54:04:A6:CE:C2:1F' /etc/sysconfig/ [root@xingdian ~]# egrep '^IPADDR' /etc/sysconfig/network-scripts/ifcfg-eth0 |egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' 192.168.2.254 [root@xingdian ~]# egrep '^IPADDR' /etc/sysconfig/network-scripts/ifcfg-eth0 |egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}' 192.168.2.254 ```