# SHELL # 第一章 初始shell ## shell概述 ### 1.计算机编程语言 计算机编程语言是程序设计的最重要的工具,它是指计算机能够接受和处理的、具有一定语法规则的语言 ------ 编译型和解释型语言: **解释型** **​ 解释性语言编写的程序不进行预先编译,以文本方式存储程序代码** **​ 执行时才翻译执行** **​ 程序每执行一次就要翻译一遍** **​ 代表语言:python,JavaScript,Shell等** 优缺点:跨平台能力强,易于调,执行速度慢 ------ 编译型 编译型语言在执行之前要先经过编译过程,编译成为一个可执行的机器语言的文件 因为翻译只做一遍,以后都不需要翻译,所以执行效率高 典型代表:C语言,C++ 优缺点:执行效率高,缺点是跨平台能力弱,不便调试 ------ ### 2.shell能做什么 ``` 自动化批量系统初始化程序 (update,软件安装,时区设置,安全策略...) 自动化批量软件部署程序 (LAMP,LNMP,Tomcat,LVS,Nginx) 应用管理程序(KVM,集群管理扩容,MySQL,DELLR720批量RAID) 日志分析处理程序(PV, UV, 200, !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可以做任何事(一切取决于业务需求) ``` ------ ### 3.shell定义 shell是命令解释器,用于与操作系统交互,用来完成各种任务,如文件操作、系统管理、网络管理等   Shell 是一种程序设计语言,他有变量,有自己的语法结构等,shell程序设计语言可以编写功能很强、代码简短的程序 4.获取当前支持的shell ```shell cat /etc/shells /bin/sh /bin/bash /usr/bin/sh /usr/bin/bash ``` ```shell chsh -l ``` ------ ## GUN Bash特性 ### 1.命令和文件自动补齐  下载bash-completion 补全工具 [root@xingdiancloud ~]# yum install bash-completion -y ------ ### 2.命令历史记忆功能  上下键调用历史命令  获取历史命令的命令:history  获取某一条历史命令:!number 直接执行对应序号的命令  快捷键调用历史命令:Ctrl+r 输入关键字调出之前的命令 ------ ### 3.快捷键  Ctrl+a 切换到命令行开始  Ctrl+e 切换到命令行末尾  Ctrl+u 清除剪切光标之前的内容  Ctrl+k 清除剪切光标之后的内容  Ctrl+y 粘贴刚才锁删除的字符  Ctrl+左右键 快速移动光标 ------ ### 4.前后台作业控制  &:后台运行  nohup:正在运行一个进程,退出帐户时该进程不会结束  ^C:终止  ^Z:暂停  jobs:查看后台进行  bg %工作号:将一个在后台暂停的命令,变成继续执行  fg %工作号:将后台中的命令调至前台继续运行 案例: [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 & ------ ### 5.输入输出重定向 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 ------ ### 6.管道 [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 追加 ------ ### 7.命令排序  ; 无论前面是否执行成功,分号后的命令都会继续执行  && 前面执行成功,后面的才继续执行  || 前面命令不成功,后面的命令也会继续 [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 ------ ### 8.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 ``` ------ ##  三、编写脚本 创建bash脚本: 以.sh结尾的文件 .py结尾的文件是python的脚本 ### 1.创建脚本文件 #!/bin/bash #!/usr/bin/env bash 指定命令解释器:第一行的专门解释命令解释器 注释 :以#开有的都不生效 编写bash指令集合 #!/bin/bash #注释 #author:blackmed #version:0.1 #功能 ------ ### 2.修改权限  注意:改不改权限都可以执行 [root@xingdiancloud ~]#./scripts [root@xingdiancloud ~]#/shelldoc/scripts [root@xingdiancloud ~]#bash scripts ------ ## 四:脚本调试  sh –x script 这将执行该脚本并显示所有变量的值  sh –n script 不执行脚本只是检查语法模式,将返回所有错误语法  sh –v script 执行脚本前把脚本内容显示在屏幕上 ------ # 第二章 shell变量 ## 变量概述 规则: 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头  中间不能有空格,可以使用下划线  不能使用标点符号  不能使用bash里的关键字 ------ ## 变量分类 ### 1.自定义变量 定义:变量名=变量值 引用变量: 查看变量: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前执行一些工作,都可以在此文件中设置 [root@xingdiancloud ~]# vi ~.bash_logout clear 仅执行一个clear命令在你注销的时候 ------ ~/.bash_history  这个文件会记录用户先前使用的历史命令 注意:  在/etc/profile.d建立独立的环境变量配置文件  常用环境变量:USER UID HOME HOSTNAME PWD PATH **​PATH:这个变量存放的是所有命令所在的路径** **修改:PATH=$PATH:+目录** ------ ### 3.位置变量 ```shell $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 ``` xingdian是第一位的位置变量 ```shell vim syx.bash #!/bin/bash echo "hello $3" ``` $3表示 第三位的变量 执行脚本 ```shell bash syx.sh a b c 输出第三位的变量 hello c ``` ------ ### 4.预定义变量 ```shell $0 脚本名 $* 所有的参数 $@ 所有的参数 $# 参数的个数 $$ 当前进程的PID $! 上一个后台进程的PID $? 上一个命令的返回值 0表示成功 ``` ------ ## 变量赋值 ### 显示赋值 变量名=变量值 ```shell [root@xingdiancloud ~]# ip1=192.168.1.251 [root@xingdiancloud ~]# school="BeiJing 1000phone" [root@xingdiancloud ~]# today1=`date +%F` [root@xingdiancloud ~]# today2=$(date +%F) ``` ------ ### 键盘读入 语法格式: ```shell read 变量名 read -p "提示信息: " 变量名 read -t 5 -p "提示信息: " 变量名 -t 后面跟秒数,定义输入字符的等待时间 read -n 2 变量名 -n 后跟一个数字,定义输入文本的长度,很实用。 ``` 案例1: ```shell read -t 3 -n 3 -p"输出:" 变量名//输入字符等待3s,输入3位长度之后会自动截断 ``` ```shell #!/bin/bash read -t 3 -n 3 -p "请输入密码:" pass echo "您输入的密码是:$pass" ``` 案例2: ```shell #!/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 ``` 注意:定义或引用变量时注意事项  " " 弱引用 可以实现变量和命令的替换  ' ' 强引用 不完成变量替换  反引 命令替换 等价于 $() 反引号中的shell命令会被先执行 ```shell [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}'` ``` ------ ## 四:变量运算 #### 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 ``` [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 ``` ------ ## 五:扩展 #### 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 ``` ------ # 第三章:流程控制 ## 一:条件测试 #### 1.语法格式 ```shell 格式1: test 条件表达式 格式2: [ 条件表达式 ] 格式3: [[ 条件表达式 ]] ``` ------ #### 2.文件测试 ```shell [ -e dir|file ] [ -d dir ] [ -f file ] 是否存在,而且是文件 [ -r file ] 当前用户对该文件是否有读权限 [ -x file ] [ -w file ] ``` ------ 案例 ```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......" ``` ```shell [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 ``` ------ ## 二:条件判断 #### 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 ``` ------ ## 三:循环结构 #### 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循环 ​ 不断重复的执行循环体中的语句,直到某个条件成立 ​ 循环次数不一定是固定的 ##### 语法结构 ​ 当条件测试成立(条件测试为真),执行循环体 ``` 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 ``` ------ ## 四:匹配模式 #### 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 while : do echo "+--------------------------------+" echo " 工具箱 " echo "==================================" echo " a.创建文件 " echo " b.创建目录 " echo " c.安装httpd " echo " d.启动httpd " echo " e.QUITE " echo "===================================" read -p "请输入你的选项:" num case $num in a) touch /opt/zhuwenbao ;; b) mkdir /opt/shangyuxian ;; c) yum -y install httpd ;; d) systemctl start httpd ;; e) exit ;; *) printf "请按照上方提示的选项输入!!!\n" ;; esac done ``` ------ ## 五:循环控制 #### 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秒,再继续下一操作 ## 六:格式化打印 ​ printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等 #### 1.语法格式 ```shell [root@xingdiancloud ~]# printf format-string [arguments...] • format-string: 为格式控制字符串 • arguments: 为参数列表 ``` ```shell [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 回车 ------ ## 七:颜色 ​ shell脚本中echo显示内容带颜色显示,需要使用到-e参数 #### 1.语法格式 ```shell 格式1:echo -e "\033[背景颜色;字体颜色m 要输出的字符 \033[0m" 格式2:echo -e "\e[背景颜色;字体颜色m 要输出的字符 \e[0m" ``` #### 2.案例 ``` 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:闪烁 ``` ------ # 第四章:函数 ## 一:函数 #### 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.函数案例 ###### 案例一: ``` #!/usr/bin/bash list(){ echo "=========================" echo "=========百宝箱===========" echo "======1.关闭防火墙===========" echo "======2.关闭selinux=========" echo "======3.创建用户=============" echo "======4.QUITE===============" } guanbi(){ systemctl stop firewalld systemctl disable firewalld } guan(){ setenforce 0 sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config } chuangjian(){ read -p "用户:" name useradd $name num_pass=`echo $RANDOM` echo $num_pass|passwd --stdin $name &>/dev/null if [ $? -eq 0 ];then echo "用户名:$name的密码:$num_pass">> /opt/user.txt else exit fi } while : do list read -p "请输入您的选项:" num case $num in 1) guanbi sleep 3 ;; 2) guan sleep 3 ;; 3) chuangjian sleep 3 ;; 4) exit ;; *) echo "请您按要求输入!!!" ;; esac done ``` ------ # 第五章:数组 ## 一:数组 #### 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 ``` 案例二:利用索引进行遍历 ``` [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 ``` ------ # 第六章:正则 ## 一:正则 #### 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 ``` ------ # 第七章:shell三剑客 ## 一:非交互式编辑器Sed #### 1.sed介绍 ![image-20230408155357663](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20230408155357663.png) ​ sed 是一种在线的、非交互式的编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出;**Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等** ------ #### 2.语法格式 ```shell sed [options] 'command' in_file[s] ``` options部分 ``` -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 ``` ## 二:文本处理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 ``` ```shell [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' ``` ------ #### 3.grep示例 ```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 ``` ------ #### 4.grep参数 ```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 ``` ------ #### 5.正则案例 ```shell #!/bin/bash read -p "please input number:" num if [[ ! "$num" =~ ^[0-9]+$ ]] then echo "error number!" else echo "is number!" fi ``` ------ # 第八章:并发控制 ## 一:FD文件描述符 #### 1.FD简述 ​ FD:文件描述符/文件句柄 ​ 进程使用文件描述符用来管理进程打开的文件 ```shell [root@xingdiancloud ~]# ll /proc/$$/fd 总用量 0 lrwx------. 1 root root 64 4月 16 23:51 0 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 1 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 2 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 255 -> /dev/pts/0 [root@xingdiancloud ~]# touch file1 启用自定义文件描述符打开文件 [root@xingdiancloud ~]# exec 6<> file1 [root@xingdiancloud ~]# ll /proc/$$/fd 总用量 0 lrwx------. 1 root root 64 4月 16 23:51 0 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 1 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 2 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 255 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 6 -> /root/file1 [root@xingdiancloud ~]# echo "xingdian" > /proc/$$/fd/6 [root@xingdiancloud ~]# cat file1 xingdian [root@xingdiancloud ~]# rm -rf file1 [root@xingdiancloud ~]# ll /proc/$$/fd 总用量 0 lrwx------. 1 root root 64 4月 16 23:51 0 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 1 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 2 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 255 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 6 -> '/root/file1 (deleted)' [root@xingdiancloud ~]# cat /proc/$$/fd/6 xingdian 释放文件描述符 [root@xingdiancloud ~]# exec 6<&- [root@xingdiancloud ~]# ll /proc/$$/fd 总用量 0 lrwx------. 1 root root 64 4月 16 23:51 0 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 1 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 2 -> /dev/pts/0 lrwx------. 1 root root 64 4月 16 23:51 255 -> /dev/pts/0 ``` 注意: ​ exec打开一个文件 ​ exec关闭一个文件(释放文件句柄) ​ 当一个文件FD未被释放,删除源文件也不会影响FD #### 2.管道简述 匿名管道 ```shell [root@xingdiancloud ~]# rpm -qa | grep rpm ``` 命令管道 ```shell [root@xingdiancloud ~]# mkfifo /tmp/xingdian [root@xingdiancloud ~]# file /tmp/xingdian /tmp/xingdian: fifo (named pipe) [root@xingdiancloud ~]# tty /dev/pts/0 [root@xingdiancloud ~]# rpm -qa > /tmp/xingdian 另一个终端 [root@xingdiancloud ~]# grep bash /tmp/xingdian bash-5.1.8-6.el9.x86_64 bash-completion-2.11-4.el9.noarch [root@xingdiancloud ~]# tty /dev/pts/1 ``` #### 3.FD案例 ```shell [root@xingdiancloud ~]# cat a.sh #!/bin/bash exec 7<> /etc/hosts exec 8<> /etc/hostname while read -u 7 line do echo $line done while read -u 8 line2 do echo $line2 done exec 7<&- exec 8<&- [root@xingdiancloud ~]# bash a.sh 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 xingdiancloud ``` 4.并发案例 ```shell #!/usr/bin/bash #固定线程 read -p "请输入线程数量:" diange diangefile=/root/diangefile mkfifo $diangefile //虚拟管道 exec 8<>$diangefile //文件描述 rm -rf $diangefile for i in `seq $diange` do echo >&8 //将内容写入到描述文件中,写如了空行 done for i in {1..100} do read -u 8 //read 读取描述文件中的内容 { ip=192.168.101.$i ping -c1 $ip &>/dev/null if [ $? -eq 0 ];then echo "$ip is up" else echo "$ip is down" fi echo >&8 //等到进程执行完之后再往管道里丢内容以支持后面的循环 }& done wait echo "完成" exec 8<&- //释放描述文件 ```