shell脚本

Shell是一个命令解释器,它在操作系统的最外层,负责直接与用户进行对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,输出到屏幕反馈给用户。这种对话方式可是交互也可以是非交互式的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1、!/bin/bash 作用:告诉脚本使用的是哪种命令解释器。如不指shell,以当前shell作为执行的shell。
2、在shell中以#表示开头,整个行就被当作一个注释。执行时被忽略。
3、shell程序一般以.sh结尾

创建shell程序的步骤:
 第一步:创建一个包含命令和控制结构的文件。
 第二步:修改这个文件的权限使它可以执行。 使用chmod +x test.sh
 第三步:检测语法错误
 第四步:执行 ./test.sh
shell脚本的执行通常有以下几种方式
1、/root/test.sh 或者 ./test.sh (当前路径下执行脚本的话要有执行权限chmod +x test.sh)
2、bash test.shsh test.sh (这种方式可以不对脚本文件添加执行权限)
3、source test.sh (可以没有执行权限)
4、sh < test.sh 或者 cat test.sh |sh(bash)

SHELL变量及运用

变量是shell 传递数据的一种方法。变量是用来代表每个值的符号名。我们可以把变量当成一个容器,通过变量,可以在内存中存储数据。也可以在脚本执行中进行修改和访问存储的数据

变量的设置规则:
1、 变量名称通常是大写字母,它可以由字母(大小写)、数字和下划线_组成。变量名区分大小写;但是大家要注意变量名称不能以数字开头
2、 等号 = 用于为变量分配值,在使用过程中等号两边不能有空格
3、 变量存储的数据类型是整数值和字符串值
4、 在对变量赋于字符串值时,建议大家用引号将其括起来。因为如果字符串中存在空格隔符号。需要使用单引号或双引号
5、 要对变量进行调用,可以在变量名称前加美元符号$
6、 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含“$变量名”或用${变量名}包含

按照变量的作用可以分成4类
1、用户自定义变量
2、环境变量:这种变量中主要保存的是和系统操作环境相关的数据。
3、位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。
4、预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。
按照变量作用域可以分成2类:全局变量和局部变量。
局部变量是shell 程序内部定义的,其使用范围仅限于定义它的程序,对其它程序不可见。包括:用户自定义变量、位置变量和预定义变量。
全局变量是环境变量,其值不随shell 脚本的执行结束而消失

用户定义变量
变量名命名规则:由字母或下划线打头,不允许数字开头,后面由字母、数字或下划线组成,并且大小写字母意义不同 赋值等号不能有空格 。在使用变量时,在变量名前加$
variable [ˈveəriəbl] 变量

VAR_1=123 #定义一个变量赋值123
echo $VAR_1 #显示调用变量
unset VAR_1#删除变量
变量值的叠加,使用${}
$name是 ${name} 的简化版本,但是在某些情况下,还必须使用花括号引起的方式来消除歧义并避免意外的结果。
var=“hello”
echo $varworld #用法错误。
echo ${var}world

命令的使用替换,使用$()或反引号` `,建议用$()
在命令就调用date命令
date命令是显示或设置系统时间与日期。
-s<字符串>:根据字符串来设置日期与时间。字符串前后必须加上双引号;
<+时间日期格式>:指定显示时,使用特定的日期时间格式。

echo $(date "+%Y%m%d %H:%M:S") #显示时间,调用date命令

echo `date +"%Y-%m-%d"` #显示时间,调用date命令

命令的嵌套使用,使用$( $( )) 此项注意不要和算术运算$ (( ))搞混

VAR6=$(tar zcvf txt.tar.gz $(find /root/ -name *.txt))

echo $VAR6 #查看值, VAR6中存储着tar的标准输出

shell中单引号和双引号区别
‘’ 在单引号中所有的字符包括特殊字符($,’’,`和\)都将解释成字符本身而成为普通字符。
“” 在双引号中,除了$, ‘’, `和\以外所有的字符都解释成字符本身,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义
注:\转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如$将输出“$”符号,而不当做是变量引用
echo "this is $var " #输出this is 123 ,变量可用。
echo ‘this is \$novar ’ #输出this in $novar ,变量不可用。


局部变量: 它只在自己的进程当中使用

var=123 #设置一个局部变量
echo $var #当前环境输出var值是123
echo 'echo $var'>test.sh #写一个输出变量脚本
sh test.sh #执行脚本发现,输出$var变量值为空,因为执行脚本执行了另外一个bash进程。

全局变量:对于shell会话和所有的子shell都是可见的
使用export把这个局部变量输出为全局变量
env #查看全局变量
export var=hello #设置一个全局变量
env |grep var #查看全局变量var
sh test.sh #执行脚本中的$var也能调用

虽然我们设置了export全局变量,但是新开的xshell连接中,还是读不到变量VAR1,怎么办?
让变量永久生效,可以把定义好的变量写入配置文件
当登录系统或新开启一个ssh连接启动bash进程时,一定会加载这4个配置文件:
vim /etc/profile #系统全局环境和登录系统的一些配置
vim /etc/bashrc #shell全局自义配置文件,用于自定义shell
vim /root/.bashrc #用于单独自定义某个用户的bash
vim /root/.bash_profile #用户单独自定义某个用户的系统环境

查看启动文件顺序。

echo 'echo  /etc/profile ' >> /etc/profile
echo 'echo  /etc/bashrc' >> /etc/bashrc
echo 'echo  /root/.bashrc ' >> /root/.bashrc
echo 'echo  /root/.bash_profile ' >> /root/.bash_profile

ssh root@192.168.1.63 #弹出以下信息,就知道有优先级了
/etc/profile
/etc/bashrc
/root/.bashrc
/root/.bash_profile

vim /etc/profile #在文件的最后插入export var=hello

source /etc/profile #重新加载profile文件

echo $var #查看效果


设置PATH环境变量
shell要执行某一个程序,它要在系统中去搜索这个程序的路径,path变量是用来定义命令和查找命令的目录,当我们安装了第三方程序后,可以把第三方程序bin目录添加到这个path路径内,就可以在全局调用这个第三方程序的
echo $PATH #查看默认命令的目录
vim /tmp/command1 #写一个脚本,充当命令
#! /bin/bash
echo “this is test PATHvar,add”

chmod +x command1 #加执行权限

PATH=/tmp/:$PATH #临时添加一个命令目录,永久添加到profile文件中

command1 #直接执行成功


shell位置变量
Shell解释执行用户的命令时,将命令行的第一个字符作为命令名,而其它字符作为参数。
$0 获取当前执行shell脚本的文件文件名,包括脚本路径,命令本身
$n 获取当前脚本的第n个参数 n=1,2…n 当n大于9时 用${10}表示。
vim print.sh #创建一个输入出脚本
!# /bin/bash
echo “$0”
echo “$1”
echo “$2”
echo “$3”
echo “${10}”
sh print.sh 1 2 3 #运行脚本会把脚本后面的参数输入,以空格分界
pring.sh
1
2
3
/etc/init.d/network restart #服务启动参数原理


特殊变量
有些变量是一开始执行Script脚本时就会设定,且不能被修改,我们叫它特殊变量。这些变量当一执行程序时就有了,以下是一些特殊变量:
$* 以一个单字符串显示所有向脚本传递的参数;
如"$*“用【”】括起来的情况、以"$1 $2 … $n"的形式输出所有参数
$# 传递到脚本的参数个数
$$ 当前进程的进程号PID
$? 显示最后命令的退出状态;0表示没有错误,其他任何值表明有错误
$! 后台运行的最后一个进程的进程号pid
在这里插入图片描述
常用的环境变量
vim env.sh #创建一个脚本
#!/bin/bash
echo $HOME
echo $PATH
echo $PWD
echo $USER
[root@localhost tmp]# sh /env.sh #执行
/root
/tmp/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
/tmp
root


数学运算

expr命令
对数值的处理

加减乘除比较><= 需要加转义符\ ,符号两边都需要空格。忽略小数点。
expr 2 \- 1 #输出2-1的值
expr 5 \> 1 #输出 1 为真。0为假 ,忽略小数点。

对字符的处理
expr length “1234567” #统计“”中的字符串个数,输出结果7。
expr substr "hello world" 6 6 #从第六个字符开始,截取6个字符输出,输出结果为world


使用$(( ))
格式:$((表达式1,表达2))
特点:
1、在双括号结构中,所有表达式可以像c语言一样,如:a++,b–等。a++ 等价于 a=a+1
2、在双括号结构中,所有变量可以不加入:“$”符号前缀。
3、双括号可以进行逻辑运算,四则运算
4、双括号结构 扩展了for,while,if条件测试运算
5、支持多个表达式运算,各个表达式之间用“”分开
在这里插入图片描述
b=2 #设置变量b=2
echo $((b++)) #输出变量b,在加1。此时输出的是2。先输出,后加一,echo $b 》b=b+1 。
echo $((++b)) #此时运行结果为3,++b,先执行加1,在输出结果。b=b+1 》echo $b。
echo $((100*(1+100)/2)) #求一到一百的和。
5050


条件测试语句和if流程控制语句的使用

read命令键盘读取变量的值
从键盘读取变量的值,通常用在shell脚本中与用户进行交互的场合。该命令可以一次读取多个变量的值,变量和输入的值都需要使用空格隔开。在read命令后面,如果没有指定变量名,读取的数据将被自动赋值给特定的变量REPLY
read常用见用法及参数
read a b #从键盘输入值给a和b 。
hello world
echo $a $b #查看变量a和b,结果为hello world
读取多个值,从标准输入读取一行,直至遇到第一个空白符或换行符。把用户键入的第一个词存到变量first中,把该行的剩余部分保存到变量last中

read -s passwd 将你输入的东西隐藏起来,值赋给passwd。这个用户隐藏密码信息
输入的时间限制
read -t 2 test #两秒结束。
read -n 2 test #只接受两个字符
read -r line #使用-r参数时,允许让输入中的内容包括:空格、/、\、 ?等特殊字符串。
read -p "please input:"passwd # 输出" "中的提示信息。
echo -n "please input :" ; read passwd #利用echo -n (不换行) 辅助输出提示信息。
在这里插入图片描述


流程控制语句if

单分支语句
语法格式:
if 条件
then
commands
fi

在这里插入图片描述
注:根据我们的命令退出码来进行判断(echo $? =0),如果是0,那么就会执行then后面的命令
vim test.sh #写个简单的if脚本

if ls /mnt &>/dev/null
then
echo "it's ok "
if

双分支if语句
语法格式:

if command  ; then
		commands
else
		commands
fi

在这里插入图片描述

vim test.sh 
#! /bin/bash
if grep aiwen /etc/passwd;then
echo "it's ok"
else echo "it's err"
fi

多分支if语句
语法结构:

if条件测试操作1 ; then
		commands
elif  条件测试操作2  ; then
		commands
elif 条件测试操作3 ; then
		commands
.......
else
		commands
fi

在这里插入图片描述
判断用户在系统中是否存在,是否有家目录

#!/bin/bash
read -p "input a user:" tu
if grep $tu /etc/passwd ; then
        echo "the user $tu exists on this system"&&ls -d /home/$tu &&echo$tu has a home directory”|| echo$tu no home directory”
elif ls -d /home/$tu  ;  then
        echo "the user $tu not exists on this system"
        echo "$tu has a home directory"
else
        echo "the user $tu not exists on this system"
        echo "$tu not has a direcotry"
fi

test测试命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试
格式:test 测试条件
如果结果是对的,也叫结果为真,用$?=0表示,反之为假,用非0表示
在这里插入图片描述

  1 #! /bin/bash 
  2 read -p "输入两个数值比较:" a b
  3 if test $a -eq $b ;then
  4 echo " $a等于$b"      
  5 elif test $a -gt $b ;then echo "$a大于$b"
  6  else echo "$a 小于 $b"
  7 fi

整数字符比较,可以用[ ] 括号代替test
在这里插入图片描述
[ ]括号中的数值必须有空格。


字符的比较(ASCLL码)

== 等同于 = 符号两边要有空格。
在这里插入图片描述
根据用户名判断是否是超级管理员

#!/bin/bash
read -p  "input your name: " name
if [ $name == "root" ] ; then
        echo "you are super administrator"
else
        echo  "You are a general user"
fi

在做字符串大小比较的时候,注意字符串的顺序
 大于号和小于号必须转义,要不然SHELL会把它当成重定向符号
 大于和小于它们的顺序和sort排序是不一样的
 在test比较测试中,它使用的是ASCII顺序,大写字母是小于小写字母的;sort刚好相反

比较字母大小,注意空格。如下
在这里插入图片描述


文件的比较(判断文件存在和权限,比较)
在这里插入图片描述

if [ -e /etc/passwd ];then
 echo ok
else  
 echo err
 fi

实战清空日志目录脚本
vim clear-log.sh #新建一个脚本。需要root运行。

#!/bin/bash
# clear /var/log/messages

    #确定当前是root用户
    if [ $USER != "root" ];then
    	echo "你必须使用root用户才能执行脚本"
    	exit 10    #直接退出,并返回10
    fi
    
    #判断文件是否存在
    if [ ! -f /var/log/messages ];then
    	echo "文件不存在"
    	exit 12
    fi
    
    #保留最近100行的日志内容
    tail -100 /var/log/messages > /var/log/mesg.tmp
    
    #日志清理
    >/var/log/messages
    #cat /dev/null > /var/log/messages
    
    mv /var/log/mesg.tmp /var/log/messages
    echo "Logs clean up"

注:自定义退出码 exit ,取值范围是0-255。


流程控制过程中复杂条件和通配符

判断第一种:两个条件都为真(&&)或有一个为真(||)就执行

if [ 条件判断一 ] && (||) [ 条件判断二 ]; then    
   &emsp; 命令一
elif [ 条件判断三 ] && (||) [ 条件判断四 ]; then
    &emsp;命令二	
  &emsp;else
  &emsp;  执行其它
fi

判断第二种-a(与),-o(或)

if [条件判断一 -a (-o) 条件判断二 -a (-o) 条件判断三]; then
elif [条件判断三  -a (-o) 条件判断四 ]; then
else
   执行其它
fi

判断第三种

if [[条件判断一 && (||) 条件判断二 ]]; then
elif [[ 条件判断三 && (||) 条件判断四 ]]; then
else
   执行其它
fi

查看umask(默认创建的权限,相反码002—目录755,文件644)
vim /etc/profile #查看命令59行
在这里插入图片描述
vim umask.sh 判断uid>199 ,当前用户为是否是root

if [ $UID -gt 199 ] && [ "\`/usr/bin/id -gn\`" = "`/usr/bin/id -un`" ]; then
    echo "umask 002"
else
    echo "i am root :umask 022"
fi

bash umask.sh #执行测试

i am root :umask 022


[[ 。。。 ]]和[ 。。。]的区别
[[… ]] 运算符是[… ]运算符的扩充;[[… ]]能够支持 *,< 、>等符号且不需要转义符

if [[ $USER == r* ]] ; then echo "hello,$USER" ; else echo $USER not ; fi 

hello,root
注: $USER == r对比时, r 表示以r开头的任意长度字符串,这样就包括root

只有一个[ ],对比时r* ,就表示两个字符串 r*

 if [ $USER == r* ] ; then echo "hello,$USER" ; else echo $USER not ; fi

root not

也可以这样写

if [[ $USER == [a-z]oot ]] ; then echo "hello,$USER" ; else echo $USER not ; fi

[[ 。。。 ]]和[ 。。。]的区别汇总:

1、所有的字符与逻辑运算符直接用“空格”分开,不能连到一起。
2、在[… ]表达式中,常见的> 、<需要加转义符\,大小比较
3、进行逻辑运算符&& 、||比较时;如果用的[ ]符号,则用在外面,如[… ] && [… ] || [ …]如果在[…]里面进行逻辑与或的比较,则用-a、-o进行表示,如[ x = y –a x < z –o x > m ]
4、[[… ]] 运算符只是[… ]运算符的扩充;能够支持< 、>符号运算不需要转义符;它还是以字符串比较大小。里面支持逻辑运算符 || 、 && , 不再使用-a 、-o
5、[[…]] 用 && 而不是 -a 表示逻辑“与”;用 || 而不是 -o表示逻辑“或”
6、[[… ]]可以进行算术扩展,而[ … ]不可以
7、[[…]]能用正则,而[…]不行
8、双括号(( ))用于数学表达式
9、双方括号号[[ ]]用于高级字符串处理,比如“模糊匹配”


shell中的通配符
在这里插入图片描述
ls /etc/*.conf #匹配所有
ls /etc/???.conf #匹配三个字符
/etc/nfs.conf /etc/sos.conf /etc/yum.conf
touch /opt/a{1,2,3}.txt #创建文件
ls /opt/a[123].txt #分别适配括号里的字符
/opt/a1.txt /opt/a2.txt /opt/a3.txt
ls /opt/a[1,2,3].txt #分别适配括号里的字符
ls /opt/a[13].txt #适配1到3字符
/opt/a1.txt /opt/a3.txt


实战-3个shell脚本实战

实战1:编写脚本检查服务器运行状态

vim status.sh
#!/bin/bash
if [ $# -ge 1 ] ; then
        systemctl status $1 > /dev/null
   if [ $? -eq 0 ] ; then
        echo "$1 服务正在运行"
   else
        systemctl start $1
   fi
else
          echo "执行脚本的格式"
          echo "sh $0 服务名"
fi

实战2:根据学生的成绩判断 学生的优劣

vim  check_cj.sh  
#!/bin/bash
read  -p   "请输入你的成绩  "   cj
if   [ $cj   -ge  0  ]    &&  [  $cj  -le  59  ]  ;then
     echo   "补考"
elif  [  $cj  -ge  60 ]    &&  [  $cj  -le  70   ]   ;then
    echo  "良好"
elif     [ $cj -ge 71 ]     &&  [ $cj   -le  85 ]   ;then
   echo  "好"
elif [ $cj  -ge 86 ]     &&  [   $cj  -le  100 ]   ;then
    echo  "优秀" 
else
   echo "成绩的有效范围是0-100之间"
fi

实战3:每周一晚上3:00 ,备份数据库服务器上webdb库的所有数据到系统的/mysqlbak目录里,使用系统日期做备份文件名。

vim   mysqlbak.sh
#!/bin/bash
baknamefile=`date +%Y-%m-%d`
bakdir=/mysqlbak
user=root
password=123
dbname=webdb
[  -e  $bakdir   ]   ||    mkdir    $bakdir
mysqldump   -u$user   -p$password  --flush-logs  $dbname   >    $bakdir/${baknamefile}-webdb.sql

因为mysql咱们还没有学,这里以/etc目录来做实验:

vim etcbak.sh
#!/bin/bash
baknamefile=`date +%Y-%m-%d`
bakdir=/etcbak
srcdir=/etc
[  -e  $bakdir   ]   ||    mkdir    $bakdir
tar zcvf ${bakdir}/${baknamefile}-etc.tar.gz /etc/
echo "========================"
ls -lh ${bakdir}/${baknamefile}-etc.tar.gz
echo "back etc is ok!"
rm -rf $(find ${bakdir}  -ctime +4)  #删除四天前的存档

chmod +x etcbak.sh #添加权限

crontab -e #创建任务计划每天3点钟执行

0 3 * * *  /root/etcbak.sh  2>&1 > /dev/null

分 时 日 月 周


结构化命令case和for、while循环

流程控制语句:case
**控制语句:用来实现对程序流程的选择、循环、转向和返回等进行控制。**case是开关语句的一个组成部分;
它是根据变量的不同进行取值比较,然后针对不同的取值分别执行不同的命令操作
适用于多分支,是一个多选择语句

case    变量或表达式    in
        变量或表达式1)
                   命令序列1
                   ;;
        变量或表达式2)
                   命令序列2
                   ;;
                   ……
                   *)  
         默认命令序列
 esac

在这里插入图片描述
执行流程:

  • 1.首先使用“变量或表达式”的值与值1进行比较,若取值相同则执行值1后的命令序列,直到遇见双分号“;; ”后跳转至esac,表示分支结束;
  • 2.若与值1不相匹配,则继续与值2 进行比较,若取值相同则执行值2 后的命令序列,直到遇见双分号“;; ”后跳转至esac,表示结束分支。
  • 3.依次类推,若找不到任何匹配的值,则执行默认模式“ *) ”后的命令序列,直到遇见esac后结束分支
    注意事项:
     “变量或表达式”后面必须为单词in,每一个“变量或表达式”的值必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;
     匹配中的值可以是多个值,通过“|”来分隔

编写一个操作文件的脚本

#!/bin/bash
cat <<eof
****************
** 1. backup  **
** 2. copy    **
** 3. quit    **
****************
eof
read -p "Input a choose: " OP
case $OP in
	1|backup)
		echo "BACKUP......"
		;;
	2|copy)
		echo "COPY....."
		;;
	3|quit)
		exit
		;;
	*)
		echo error
esac

编写一个启动apache服务脚本

 vim  case-2.sh #创建一个脚本
    #!/bin/bash
    case $1 in
    	start)
    		systemctl $1 httpd
    		ps aux|grep httpd
    		echo "httpd start"
    		;;
    	stop)
    		systemctl $1 httpd
    		ps aux|grep httpd
    		echo "httpd stop"
    		;;
    	status)
    		systemctl $1 httpd
    		;;
    	restart)
    		systemctl $1 httpd
    		echo "httpd restart"
    		;;
    	*)
    		echo "USAGE: $0  start|stop|restart"
    esac

循环语句
for命令

for-do-done
语法格式:
for var in list 
do
  commands
done
或:
for var in list ; do
  commands
done

在这里插入图片描述
取值列表有多种取值方式,比如
可以直接读取in 后面的值,默认以空格做分隔

  vim for-1.sh
    #!/bin/bash
    for var in a1 b1 c1 d1
    do
    	echo the text is $var
    done

列表中的复杂值,可以使用 引号或转义字符”/”来加以约束

#!/bin/bash
for var in a1 b\'1 "c1 d1" e2 "hello world" I\'s a22 
do
	echo the text is $var
done

在这里插入图片描述
从变量中取值

#!/bin/bash
list="a1 b1 c1 d1"
for i in $list
do
	echo is a $i
done

从命令中取值

   #!/bin/bash
    for i in `cat  /etc/hosts`
    do
    	echo "$i"
    done

自定义shell分隔符
默认情况下,base shell会以空格、制表符、换行符做为分隔符。通过IFS来自定义为分隔符
指定单个字符做分隔符:
IFS=: #以:冒号做分隔符

可以指定多个
如 IFS=’\n’:;" #这个赋值会将反斜杠、n、冒号、分号和双引号作为字段分隔符。

#!/bin/bash
IFS=$'\n'   #以回车做分割
for i in `cat /etc/hosts`
do
	echo "$i"
done
#!/bin/bash
IFS=: 以冒号做分割
list=`head -1 /etc/passwd`
for i in $list
do
	echo $i
done

C语言风格的for

语法格式:

for ((i=0;i<10;i++))
do
   commmands
done

例1:单个变量。 输出1到10之间的数字

#!/bin/bash
for ((  i=1  ; i<=10  ;  i++  ))
do
	echo num is $i
done

i++ 这一条语句在for循环体中哪个位置执行?

 for ((  i=1  ; i<=10  ;    ))   #i=1 只赋值一次。然后执行 i <= 10
do
		echo num is $i
i=$(($i+1))    # i++在这里执行。 当for循环体中所有命令执行完后,再执行i++
done

例2:多个变量。 同时输出1-9的升序和降序

#!/bin/bash
for ((a=1,b=9 ; a<10 ; a++,b--))
do
	echo num is $a - $b 
done

while /waɪl/ 循环语句和循环嵌套
while-do-done
重复测试指令的条件,只要条件成立就反复执行对应的命令操作,直到命令不成立或为假;
语法格式如下:

   while 测试命令t
    do
    命令
    done

在这里插入图片描述

注意:避免陷入死循环 while true #true 表示一直时真, false为假。( /bin/true)

例1:降序输出10到1的数字

#!/bin/bash
var=10
while [ $var -gt 0 ]  当等式不成立结束循环
do
		echo $var
		var=$[$var-1]
done

例2:输出如下图两数相乘的效果
在这里插入图片描述
自增操作 let var++
自减操作 let var–

#!/bin/bash
num=1
while [ $num -lt 10 ]
do
        sum=$((  $num * $num ))
        echo  "$num * $num = $sum"
        ((num++))
      #  let num++
done

嵌套循环
创建一个批量添加用户的脚本。
编写脚本的思路
1 明确脚本的功能 先判断用户是否存在,添加用户。显示结果。
2 编写脚本时会使用到那些命令 ? useradd passwd for
3 把变化的数据使用变量表示
4 选择适合的流程控制 (选择 、 循环 、分支)

#!/bin/bash
for name in `cat /root/a.txt`
#for name in $(cat /root/a.txt)
do
        id $name &> /dev/null
        if [ $? -ne 0 ];then
                useradd $name
                echo "123456" |passwd --stdin $name &> /dev/null
                echo "user $name created"

        else
                echo "user $name is exist"
        fi
done

&> 是正确和错误的信息都重定向到/dev/null里面

例2 :打印九九乘法表
在这里插入图片描述
规律: 内层循环的变量<=外层循环的变量

for i in  `seq 9`
do
         for j in `seq $i`
         do
              echo  -n  "$i*$j= `echo $(($i*$j))` " #-n不换行输出
         done
        echo "  "
done

外循环执行一次,内循环执行多次。直到输出结束。

实战-3个shell脚本实战

实战-将/opt目录下所有的日志文件全自动打包

#! /bin/bash
SEC_DIR=/var/log/
DES_DIR=/opt/backup/`date +"%Y%m%d"`/
if [ !-d $DEC_DIR ];then

      mkdir -p $DES_DIR

fi
for i in $(find $SEC_DIR -name "*.log")
  do  
tar -czvf ${i}.tgz $i  &> /dev/null
done
mv $(find /var/log/ -name "*.tgz") ${DES_DIR} && ls -lh ${DES_DIR} && echo "ok"

实战-找出192.168.1.1-10网段中,服务器已经关机的IP地址

#!/bin/bash
for (( i=1;i<10;i++ ))
do
  ping  -c  3  192.168.1.$i &> /dev/null
  if  [ $? -ne 0 ];then
     echo 192.168.1.$i is shutdown
  fi
done

&> /dev/null #注意&>中间不能有空格。

批量创建帐号并生成随机密码
pass=`date +%s|md5sum|cut -c 1-8` #批量生成密码。

#!/bin/bash
for i in xuegoda{1..10}
do
      useradd $i
      pass=`date +%s|md5sum|cut -c 1-8`
      sleep 1  #睡眠一秒,停止一秒
      echo "$i:$pass" >> /tmp/passwd.log
      echo $pass |passwd --stdin $i > /dev/null 2>&1
      if [ $? -eq 0 ];then
        echo "create user is successfully!"
      else
        echo "create user is failed!"
      fi

总结
if

  1. 单分支 if else fi
  2. 双分支 if elif else fi

for

  1. for i in 变量 do 命令 done
  2. for ((初始值可以多个i=1;循环控制条件i<10;变量变化 i++ ))do 命令 done

cace

  1. case 变量或表达式 in
    变量或表达式1)
    命令序列1
    ;;
    变量或表达式2)
    命令序列2
    ;;
    ……
    *
    默认命令序列
    esac

while

  1. while [ 条件判断] do 命令 done

跳出循环-shift参数左移-函数的使用

break和continue

Break:跳出整个循环

break概述:跳出当前整个循环或结束当前循环,在for、while等循环语句中,用于跳出当前所在的循环体,执行循环体之后的语句,后面如果什么也不加,表示跳出当前循环等价于break 1,也可以在后面加数字,假设break 3表示跳出第三层循环

Continue: 跳过本次循环,进行下次循环
continue概述:忽略本次循环剩余的代码,直接进行下一次循环;在for、while等循环语句中,用于跳出当前所在的循环体,执行循环体之后的语句,如果后面加的数字是1,表示忽略本次条件循环,如果是2的话,忽略下来2次条件的循环

#! /bin/sh
while true  #设置条件一直为真
do
	 echo "*******************************"
	 echo "Please select your operation:"
	 echo " 1 Copy"
	 echo " 2 Delete"
	 echo " 3 Backup"
	 echo " 4 Quit"
	 echo "*******************************"
	 read op
 case $op in
    1)
	 continue    #这里加了continue后,后面的echo命令就不执行了 ,按1跳回菜单
	 echo "your selection is Copy"
	 ;;
    2)
     	 echo "your selection is Delete"
	 ;;
    3)
	 echo "your selection is Backup"
	 ;;
    4)
	 echo "Exit ..."
	 break    #跳出循环体
	 ;;
     *)
 	echo "invalide selection,please try again"
  esac
done

使用交互式方法批量添加用户

#!/bin/bash
while : #代表永为真
do
	read -p "Please enter prefix & password & num:" pre pass num
	printf "user information:
	*********************
	user prefix:   $pre
	user password: $pass
	user number:   $num
	********************
"
read -p "Are you sure?[y/n] " action
if [ "$action" == "y" ];then
	break
fi
done
for i in $(seq  $num)    # 从i =1开始,取到 $num  。 seq 表示 1-$num  


do
	user=${pre}${i}
	id $user &> /dev/null
	if [ $? -ne 0 ];then
		useradd $user
		echo "$pass"|passwd --stdin $user &> /dev/null
		if [ $? -eq 0 ];then
			echo -e "\033[31m$user\033[0m creat"   #以红色来显示用户名
		fi
	else
		echo "user $user exist"
	fi
done

$(seq $num) 等于 ` seq $num ` ; $( 命令 ) ; ${ 变量 } ; [ 表达式/条件 ]

seq命令: seq命令用于产生从某个数到另外一个数之间的所有整数。
seq 3 #输出 1-3的数字,sep 2 3 #输出2 3
1
2
3


Shift参数左移指令
shift命令用于对参数的移动(左移),通常用于在不知道传入参数个数的情况下依次遍历每个参数然后进行相应处理(常见于Linux中各种程序的启动脚本)
在扫描处理脚本程序的参数时,经常要用到的shift命令,如果你的脚本需要10个或10个以上的参数,你就需要用shift命令来访问第10个及其后面的参数
作用:每执行一次,参数序列顺次左移一个位置,$#的值减1,用于分别处理每个参数,移出去的参数,不再可用
在这里插入图片描述
例子:加法计算器

#!/bin/bash
if [ $# -le 0 ];then
	echo  “没有足够的参数”
	exit
fi

sum=0
while  [  $#  -gt  0  ]  ; do
	#sum=$(expr $sum + $1)  可以用 expr 运算命令
	sum=$[$sum+$1]
	shift
   # shift 2  一次移动2个参数
done
echo result is $sum

测试:

bash a shift.sh  11 2 3 4 

result is 20


函数的使用
函数是一个脚本代码块,你可以对它进行自定义命名,并且可以在脚本中任意位置使用这个函数,要使用这个函数,只要使用这个函数名称就可以了。使用函数的好处:模块化,代码可读性强。

函数创建语法
方法1:

function name {
		commands
}

注意:name是函数唯一的名称

方法2:name后面的括号表示你正在定义一个函数

name(){
		commands
}

调用函数语法:
函数名 参数1 参数2 …
调用函数时,可以传递参数。在函数中用$1、$2…来引用传递的参数

函数的使用
例1:

#!/bin/bash
function fun_1 {    #定义函数
        echo "this is function"
}
fun_1   #调用函数

注意:函数名的使用,如果在一个脚本中定义了重复的函数名,那么以最后一个为准

#!/bin/bash
function fun_1 {
        echo "this is function"
}
function fun_1 {
        echo "this is 2222222"
}
fun_1  #输出 最后一个fun_1的值。

退出函数,并返回值
使用return /rɪ’tɜːn/(返回)命令来退出函数并返回特定的退出码

 #!/bin/bash
function fun_1 {
        echo "this is function"
        ls /etc/passwd
        return 3  #输出返回值$?为3
}
fun_1

注:状态码的确定必需要在函数一结束就运行return返回值;状态码的取值范围(0~255)
exit 数字 和return 数字的区别?
exit整个脚本就直接退出,往回数字 ; return 只是在函数最后添加一行,然后返回数字,只能让函数后面的命令不执行,无法强制退出整个脚本的。


把函数值赋给变量使用

例子: 函数名就相当于一个命令

    #!/bin/bash
    fun1(){
            read -p "Input a value: " va
            echo $[$va*5]
    }
num=$(fun1)
echo "current num is $num"

函数的参数传递

第一种:通过脚本传递参数给函数中的位置参数$1

#!/bin/bash
fun1(){
        rm -rf $1
}
fun1 $1    要通过脚本调用传递

第二种:调用函数时直接传递参数

#!/bin/bash
fun1(){
        rm -rf $1
}
fun1 /tmp/a.txt  #直接传递

第三种:函数中多参数传递和使用方法

#!/bin/bash
fun1(){
        echo $[$1*5]
        echo $[$2*2]
}
fun1 5  2   #直接传两个参数

函数中变量的处理

函数使用的变量类型有两种:

  1. 局部变量
  2. 全局变量1、全局变量,默认情况下,你在脚本中定义的变量都是全局变量,你在函数外面定义的变量在函数内也可以使用全局变量,默认情况下,你在脚本中定义的变量都是全局变量,你在函数外面定义的变量在函数内也可以使用

1、全局变量,默认情况下,你在脚本中定义的变量都是全局变量,你在函数外面定义的变量在函数内也可以使用

#!/bin/bash
function fun1 {
        num1=$[var1*2]
}
read -p "input a num:" var1
fun1
echo the new value is: $num1

实战-自动备份mysql数据库脚本和nginx服务启动脚本
自动备份mysql数据库脚本
从centos7.0开始,系统中自带的mysql数据库包,改为mariadb数据库。MariaDB名称来自Michael Widenius的女儿Maria(玛丽亚)的名字。

安装mariadb数据库:
[root@xuegod63 ~]# yum install -y mariadb mariadb-server -y
# mariadb 是mysql的客户端命令 ;mariadb mariadb-server 是mysql服务端命令
[root@xuegod63 ~]# rpm -qf /usr/bin/mysql

mariadb-5.5.56-2.el7.x86_64

[root@xuegod63 ~]# systemctl start mariadb
登录mysql:
[root@xuegod63 ~]# mysqladmin -u root password "123456" #给root用户配置一个密码123456
[root@xuegod63 ~]# mysql -u root -p123456 #登录mysql数据库

MariaDB [(none)]> show databases;
MariaDB [(none)]> create database xuegod  ;    #创建xuegod数据库
MariaDB [(none)]> use xuegod        #选择数据库
MariaDB [xuegod]> create table user (id int);   #创建user表,只有一个id字段
MariaDB [xuegod]> insert into user values(1);   #插入一条记录,id字段值1
MariaDB [xuegod]> insert into user values(2);   #插入一条记录,id字段值2
MariaDB [xuegod]> select * from user;   #查看表中的数据
+------+
| id   |
+------+
|    1 |
|    2 |
+------+

mysql自动化备份脚本:
思路:
1、检查一下运行环境: 目录是否存在,时间,权限,用户
2、运行要执行的命令:备份,导出数据。。。
3、把命令执行过程中的没有用的文件删除一下
4、弹出命令运行成功的消息

#!/bin/sh 
#auto backup mysql 
#Define PATH定义变量
BAKDIR=/data/backup/mysql/`date +%Y-%m-%d` 
MYSQLDB=xuegod
#MYSQLDB=webapp 
MYSQLUSR=root
#MYSQLUSR=backup 
MYSQLPW=123456
#MYSQLPW=backup     #mysql数据库密码 
#must use root user run scripts 必须使用root用户运行,$UID为系统变量
if 
   [ $UID -ne 0 ];then 
   echo This script must use the root user ! ! ! 
   sleep 2 
   exit 0 
fi 
#Define DIR and mkdir DIR 判断目录是否存在,不存在则新建
if 
   [ ! -d $BAKDIR ];then 
   mkdir -p $BAKDIR 
else 
   echo This is $BAKDIR exists.... 
   exit
fi 
#Use mysqldump backup mysql 使用mysqldump备份数据库
/usr/bin/mysqldump -u$MYSQLUSR  -p$MYSQLPW   $MYSQLDB > $BAKDIR/${MYSQLDB}_db.sql 
cd $BAKDIR ; tar -czf  ${MYSQLDB}_db.tar.gz *.sql 
#查找备份目录下以.sql结尾的文件并删除
find  $BAKDIR  -type f -name *.sql -exec rm -rf {} \;
#或
#如果数据库备份成功,则打印成功,并删除备份目录30天以前的目录
[ $? -eq 0 ] && echo “This `date +%Y-%m-%d` MySQL BACKUP is SUCCESS” 
cd /data/backup/mysql/  &&  find .  -type d  -mtime +30 |xargs rm -rf 
echo "The mysql backup successfully "

nginx服务启动脚本
此nginx脚本中使用了函数功能,让脚本具有更强的可读性

#!/bin/bash
#chkconfig: 2345 80 90
#description:nginx run

# nginx启动脚本
# @author	Devil
# @version	0.0.1
# @date		2018-05-29

PATH=/data/soft/nginx
DESC="nginx daemon"
NAME=nginx
DAEMON=$PATH/sbin/$NAME   #/data/soft/nginx/sbin/nginx
CONFIGFILE=$PATH/$NAME.conf
PIDFILE=$PATH/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
[ -x "$DAEMON" ] || exit 0
do_start()
{
	$DAEMON -c $CONFIGFILE || echo -n "nginx already running"
}
do_stop()
{
	$DAEMON -s stop || echo -n "nginx not running"
}
do_reload()
{
	$DAEMON -s reload || echo -n "nginx can't reload"
}
case "$1" in
	start)
		echo -n "Starting $DESC: $NAME"
		do_start
		echo "."
	;;
	stop)
		echo -n "Stopping $DESC: $NAME"
		do_stop
		echo "."
	;;
	reload|graceful)
		echo -n "Reloading $DESC configuration..."
		do_reload
		echo "."
	;;
	restart)
		echo -n "Restarting $DESC: $NAME"
		do_stop
		do_start
		echo "."
	;;
	*)
		echo "Usage: $SCRIPTNAME {start|stop|reload|restart}" >&2
		exit 3
	;;
esac
exit 0

总结:
22.1 跳出循环
22.2 Shift参数左移指令
22.3 函数的使用
22.4 实战-自动备份mysql数据库和nginx服务启动脚本


**

expect-正则表达式-sed-cut的使用

**

expect实现无交互登录
expect ([ɪkˈspekt] 期待 )是从它发展出来的。如果你想要写一个能够自动处理输入输出的脚本(如向用户提问并且验证密码)又不想面对C或者Perl,那么expect是你的最好的选择。它可以用来做一些linux下无法做到交互的一些命令操作
安装和使用expect

  yum -y install expect  #安装expect命令

expect实现无交互登录
expect ([ɪkˈspekt] 期待 )是从它发展出来的。如果你想要写一个能够自动处理输入输出的脚本(如向用户提问并且验证密码)又不想面对C或者Perl,那么expect是你的最好的选择。它可以用来做一些linux下无法做到交互的一些命令操作
23.1.1 安装和使用expect

1)定义脚本执行的shell
#!/usr/bin/expect
这里定义的是expect可执行文件的链接路径(或真实路径),功能类似于bash等shell功能
2)set timeout 30
设置超时时间,单位是秒,如果设为timeout -1 意为永不超时
3)spawn
spawn 是进入expect环境后才能执行的内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。不能直接在默认的shell环境中进行执行主要功能,它主要的功能是给ssh运行进程加个壳,用来传递交互指令。
4)expect
这里的expect同样是expect的内部命令
主要功能:判断输出结果是否包含某项字符串,没有则立即返回,否则就等待一段时间后返回,等待时间通过timeout进行设置
5)send
执行交互动作,将交互要执行的动作进行输入给交互指令
命令字符串结尾要加上"\r",如果出现异常等待的状态可以进行核查
6)exp_continue
继续执行接下来的交互操作
7)interact
执行完后保持交互状态,把控制权交给控制台;如果不加这一项,交互完成会自动退出
8)$argv
expect 脚本可以接受从bash传递过来的参数,可以使用 [lindex $argv n]获得,n从0开始,分别表示第一个,第二个,第三个……参数
例1:免密码通过SSH登录服务器(了解) 这里不是用密钥
注:运行脚本时,要把#号后面的注释删除,不然无法运行

#!/usr/bin/expect
set ipaddr "192.168.1.63"
set name "root"
set passwd "123456"
set timeout 30    #设置超时时间,单位是秒;expect超时等待的时间。默认timeout为10s。
spawn ssh $name@$ipaddr   # spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在shell下执行是找不到spawn命令的。这个就好比cd是shell的内建命令,离开shell,就无法执行cd一样。 它主要的功能是给ssh运行进程加个壳,用来传递交互指令。  
expect {
"yes/no" { send "yes\r";exp_continue }
"password" { send "$passwd\r" }   #执行交互动作,与手工输入密码的动作等效。
}

expect "#"   #判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,向下执行;否则就一直等待,直到超时时间到
send "touch /root/xuegod1011.txt\r"
send "ls /etc > /root/xuegod1011.txt\r"
send "mkdir /tmp/xuegod1011\r"
send "exit\r"
expect eof   #执行完成上述命令后,退出Expect,把控制权交给控制台,变回手工操作

对服务器批量管理(了解)
cat ip_pass.txt #这里写上要执行的IP地址和root用户密码
192.168.1.63 123456
192.168.1.63 123456
192.168.1.63 123456

#!/usr/bin/expect

set ipaddr [lindex $argv 0]
set passwd [lindex $argv 1]
set timeout 30
spawn ssh root@$ipaddr
expect {
"yes/no" { send "yes\r";exp_continue }
"password" { send "$passwd\r" }
}

expect "#"
send "touch /root/xuegod1011.txt\r"
send "ls /etc > /root/xuegod1011.txt\r"
send "mkdir /tmp/xuegod1011\r"
send "exit\r"
expect eof
[root@xuegod63 ~]# cat login.sh    #开始执行
#!/bin/bash
echo
for ip in `awk '{print $1}' /root/ip_pass.txt`
do
	pass=`grep $ip /root/ip_pass.txt|awk '{print $2}'`
	expect /root/ssh.exp $ip $pass
done

正则表达式的使用
在这里插入图片描述
正则表达式,又称规则表达式。(英语:Regular Expression [ˈreɡjulə] 规则的 [ iksˈpreʃən] 表达 ),在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
正则表达式不只有一种,而且LINUX中不同的程序可能会使用不同的正则表达式,如:
工具:grep sed awk
LINUX中常用的有两种正则表达式引擎
 基础正则表达式:BRE
 扩展正则表达式: ERE

在这里插入图片描述

grep 命令

例:统计/etc/ssh/sshd_config文件中除去空行和#号开头的行的行数
grep -v "^$\|^#" /etc/ssh/sshd_config #使用基础正则表达式,-v表示取反,^$表示空行

grep -E -v "^$|^#" /etc/ssh/sshd_config #扩展正则表达式加E,不需要加转义符。

egrep -v "^$|^#" /etc/ssh/sshd_config #扩展正则表达式,不需要转义符\

点字符
grep .ot /etc/passwd #查找passwd文件包括.ot 的字符,.代表一个单字符
root: x:0:0:root:/root:/bin/bash
operator: x:11:0:operator:/root:/sbin/nologin
setroubleshoot: x:993:990::/var/lib/setroubleshoot:/sbin/nologin

sed 工具命令

sed编辑器是一行一行的处理文件内容的。正在处理的内容存放在模式空间(缓冲区)内,处理完成后按照选项的规定进行输出或文件的修改。
接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;
sed也是支持正则表达式的,如果要使用扩展正则加参数-r
sed的执行过程:
1、 一次读取一行数据
2、 根据我们提供的规则来匹配相关的数据,比如查找root。
3、 按照命令修改数据流中的数据,比如替换
4、 将结果进行输出
5、 重复上面四步

语法格式:sed [options] ‘[commands]’ filename

echo "this is aplle" | sed 's/aplle/dog/'

this is dog

echo "this is aplle" > a.txt

sed 's/aplle/dog/' a.txt  
this is dog

cat a.txt  #发现并没有修改文件
this is aplle  

sed选项|参数
options:
-a #在当前行下面插入文件
-n #读取下一个输入行,用下一个命令处理新的行而不是用第一个命令
-e #执行多个sed指令
-f #运行脚本
-i #编辑文件内容
-i.bak # 编辑的同时创造.bak的备份
-r #使用扩展的正则表达式

命令:
i在当前行上面插入文件
c 把选定的行改为新的指定的文本
p 打印 ***
d 删除 ***
r/R 读取文件/一行
w 另存
s 查找
y替换
h 拷贝模板块的内容到内存中的缓冲区。
H 追加模板块的内容到内存中的缓冲区。
g获得内存缓冲区的内容,并替代当前模板块中的文本。
G 获得内存缓冲区的内容,并追加到当前模板块文本的后面
D 删除\n之前的内容
P 打印\n之前的内容

替换标记:
 数字:表明新文本将替换第几处模式匹配的地方
 g:表示新文本将会替换所有匹配的文本
 \1:子串匹配标记,前面搜索可以用元字符集(…),
 &:保留搜索到的字符用来替换其它字符

sed匹配字符集
^ 匹配行开始,如:/^sed/匹配所有以sed开头的行。
$ 匹配行结束,如:/sed$/匹配所有以sed结尾的行。
. 匹配一个非换行符的任意字符,如:/s.d/匹配s后接一个任意字符,最后是d。
* 匹配0个或多个字符,如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行。

例1:s 查找 只替换第一个匹配到的字符,将passwd中的root用户替换成xuegod

sed 's/root/xuegod/'   /etc/passwd

sed 's/root/xuegod/g' /etc/passwd |more #全部替换/g

将sed中默认的/ 定界符改成#号 ,如果字符中有/,就会用到#作为分节符,否则要转义符。

sed 's#/bin/bash#/sbin/nologin#' /etc/passwd  

sed 's/\/bin\/bash/\/sbin\/nologin/' /etc/passwd #加转义符\

(2)按行查找替换
写法如下:
 用数字表示行范围;$表示行尾
 用文本模式配置来过滤
例1:单行替换,将第2行中bin替换成xuegod

sed '2s/bin/xuegod/' /etc/passwd  | more

root: x:0:0:root:/root:/bin/bash
xuegod: x:1:1:bin:/bin:/sbin/nologin

多行替换,如果涉及到多行处理,用逗号表示行间隔
将第3行到最行尾中bin替换成xuegod
sed ‘2,$s/bin/xuegod/’ /etc/passwd | more
root: x:0:0:root:/root:/bin/bash
xuegod: x:1:1:bin:/bin:/sbin/nologin
daemon: x:2:2:daemon:/sxuegod:/sbin/nologin
adm: x:3:4:adm:/var/adm:/sxuegod/nologin

(3)d 删除第2行到第4行的内容
sed '2,4d' /etc/hosts #删除hosts文件的中第2行到第4行内容。
sed '/192.168/d' /etc/hosts #将包括192.168的行删除

(4)添加行
 命令i(insert插入),在当前行前面插入一行 i
 命令a(append附加),在当前行后面添加一行 a\

例1:插入

 echo "hello world" | sed  'i\ dk '  #可以加转义符\ 
    dk
    hello world 

    echo "hello world"|sed 'a\dk' 
    hello world
    dk

例2:追加

echo "hello world"|sed 'a\xuegod'
hello world
xuegod

例3:在文件最后追加内容

sed '$a\192.168.1.65 xuegod65.cn'   /etc/hosts 
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.1.65 xuegod65.cn

例4:在文件中第2行之后,开始追加内容

 sed '2a\192.168.1.65 xuegod65.cn'   /etc/hosts 

127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.1.65 xuegod65.cn

例5:在文件中第2行到第4行之后分别追加内容

sed '2,4a\192.168.1.65 xuegod65.cn'   /etc/hosts 

(5)修改行命令c (change) c\

例1:将第4行内容改成192.168.1.65 xuegod65.cn

 sed '4c\192.168.1.65 xuegod65.cn'  /etc/hosts 

例2:将第2行到最后全部修改成192.168.1.65 xuegod65.cn

 sed '2,$c\192.168.1.65 xuegod65.cn'  /etc/hosts  

例3:将包括192.168.1.64行的内容修改成192.168.1.65

 sed '/192.168.1.64/c\192.168.1.65' /etc/hosts

(6)打印指定行,-n
sed -n '2p' /etc/hosts #打印第二行

(7)将修改或过滤出来的内容保存到另一个文件中
将passwd中的包括root字样的行保存到 c.txt 中

  sed -n '/root/w c.txt' /etc/passwd

(8)-i 对原文件修改,保存( 必会 ) 使用场景: 替换或修改服务器配置文件

 sed -i 's/root/xuegod/'  /etc/passwd

修改IP地址为192.168.1.65

sed -i 's/IPADDR=192.168.1.63/IPADDR=192.168.1.65/' /etc/sysconfig/network-scripts/ifcfg-ens33

cut命令

cut常用参数
cut命令用来显示行中的指定部分,删除文件中指定字段。
说明:该命令有两项功能,其一是用来显示文件的内容,它依次读取由参数file所指明的文件,将它们的内容输出到标准输出上;
其二是连接两个或多个文件,如cut fl f2 > f3将把文件fl和fn的内容合并起来,然后通过输出重定向符“>”的作用,将它们放入文件f3中。

语法: cut(选项)(参数)
选项
-b:仅显示行中指定范围的字节数;
-c:仅显示行中指定范围的字符;
-d:指定字段的分隔符,默认的字段分隔符为“TAB”;
-f:显示指定字段的内容;

例1:输出系统中所有用户名

使用 -f 选项提取指定字段,使用 -d 选项指定字段分隔符,这里以:冒号做分隔

 cut -f1 -d ":" /etc/passwd

cut命令可以将一串字符作为列来显示,字符字段的记法:
N-:从第N个字节、字符、字段到结尾;
N-M:从第N个字节、字符、字段到第M个(包括M在内)字节、字符、字段;
-M:从第1个字节、字符、字段到第M个(包括M在内)字节、字符、字段。
上面是记法,结合下面选项将摸个范围的字节、字符指定为字段:

-b 表示字节;
-c 表示字符;
-f 表示定义字段。
例1:打印第1个到第3个字符:

 cut -c1-3 /etc/passwd

例2:打印前2个字符:

 cut -c-2 /etc/passwd

例3:打印从第5个字符开始到结尾:

 cut -c5- /etc/passwd

实战-bash脚本语法检查和查看详细的执行过程
检查语法是否有错:
bash -v test.bash #查看bash是否存在语法错误,试运行。
bash -x test.bash #查看bash详细的执行过程,真正运行。

\# Script to show debug of shell
tot=`expr $1 + $2`
secho $tot   #这里故意写错

[root@xuegod63 ~]# bash -v a.sh

\# Script to show debug of shell
tot=`expr $1 + $2`
expr: 语法错误    #语法哪错了?   运行时没有给参数
secho $tot   #这里故意写错
a.sh:行4: secho: 未找到命令

[root@xuegod63 ~]# sed -i 's/secho/echo/' a.sh #修改正确后
[root@xuegod63 ~]# bash -x a.sh 2 3 #查看详细执行过程。 注:这个脚本是真正执行一遍,不是预执行

++ expr 2 + 3
+ tot=5
+ echo 5

例2:查看九九乘法表shell脚本运行过程

#! /bin/bash
      for i in  `seq 9`
    do
             for j in `seq $i`
             do
                  echo  -n  "$i*$j= `echo $(($i*$j))` "
             done
            echo "  "
    done

bash -x 99.sh #查看运行过程
总结:
23.1 expect实现无交互登录
23.2 正则表达式
23.3 sed流编辑器
23.4 cut命令
23.5 实战-bash脚本语法检查和查看详细的执行过程

shell中色彩处理和awk使用技巧
1 Shell中的色彩处理
shell脚本中echo显示内容带颜色显示,echo显示带颜色,需要使用参数-e
格式1: echo -e “\033[背景颜色;文字颜色m 要输出的字符 \033[0m”
格式2:echo -e “\e[背景颜色;文字颜色m要输出的字符**\e[0m**”

例:绿底蓝字

echo -e "\033[42;34m hello world\033[0m"

echo -e "\e[42;34m hello world\e[0m"

注:其中42的位置代表底色,34的位置代表的是字的颜色,0m是清除所有格式
1、字背景颜色和文字颜色之间是英文的分号";"
2、文字颜色后面有个m
3、字符串前后可以没有空格,如果有的话,输出也是同样有空格
4、echo显示带颜色,需要使用参数-e ,-e 允许对下面列出的加反斜线转义的字符进行解释.

控制选项:
\033[0m 关闭所有属性
\033[1m 设置高亮度,加粗
\033[5m 闪烁

echo -e "\e[42;34m hello world\e[5m" #执行后,发现后期所有输出都闪烁状态,如何关闭?在这里插入图片描述
echo -e "\e[42;34m hello world\e[0m" #可以使用\033[0m #关闭所有属性
常见shell输入带颜色文字: 3x代表字的颜色,4x代表背景色
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”


awk基本应用

grep和egrep:文本过滤的
sed:流编辑器,实现编辑的
awk:文本报告生成器,实现格式化文本输出

AWK是一种优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一。这种编程及数据操作语言的最大功能取决于一个人所拥有的知识。awk命名:Alfred Aho Peter 、Weinberger和brian kernighan三个人的姓的缩写。

pattern [ˈpætn] 模式 ; process [ˈprəʊses] 处理
任何awk语句都是由模式和动作组成,一个awk脚本可以有多个语句。模式决定动作语句的触发条件和触发时间。

模式:
正则表达式 : /root/ 匹配含有root的行 /*.root/
关系表达式: < > && || + *
匹配表达式: ~ !~
动作:
变量 命令 内置函数 流控制语句

它的语法结构如下:

awk [options] 'BEGIN{ print "start" } ‘pattern{ commands }’ END{ print "end" }' file

其中:BEGIN 和 END是AWK的关键字部,因此必须大写;这两个部分开始块和结束块是可选的

在这里插入图片描述
特殊模块:
BEGIN语句设置计数和打印头部信息,在任何动作之前进行
END语句输出统计结果,在完成动作之后执行
在这里插入图片描述
通过上面我们可以知道;AWK它工作通过三个步骤
1、读:从文件、管道或标准输入中读入一行然后把它存放到内存中
2、执行:对每一行数据,根据AWK命令按顺序执行。默认情况是处理每一行数据,也可以指定模式
3、重复:一直重复上述两个过程直到文件结束

AWK支持两种不同类型的变量:内建变量,自定义变量
awk内置变量(预定义变量)
 $n 当前记录的第n个字段,比如: $1表示第一个字段,$2表示第二个字段
 $0 这个变量包含执行过程中当前行的文本内容
 FILENAME 当前输入文件的名
FS 字段分隔符(默认是空格)
NF 表示字段数,在执行过程中对应于当前的字段数,NF:列的个数
 FNR 各文件分别计数的行号
NR 表示记录数,在执行过程中对应于当前的行号
 OFS 输出字段分隔符(默认值是一个空格)
 ORS 输出记录分隔符(默认值是一个换行符)
 RS 记录分隔符(默认是一个换行符)
在这里插入图片描述
实例演示
-F fs指定分隔符
-v 赋值一个用户自定义变量
-f 指定脚本文件,从脚本中读取awk命令
(1)分隔符的使用
用法:-Ffs 其中fs是指定输入分隔符,fs可以是字符串或正则表达式;分隔符默认是空格
常见写法:-F: -F, -F[Aa]
echo "AA BB CC DD"|awk '{print $2}' #打印第二个字符。

BB
echo "AA|BB|CC|DD"|awk -F"|" '{print $2}' #F指定分隔符后面不能有空格
BB
echo "AA,BB,CC,DD"|awk -F, '{print $2}' #F指定分隔符,后面不能有空格
BB
awk -F: '{print $1}' /etc/passwd #以:分隔,打印第1列用户名

例2:指定多个分隔符
echo "12AxAbADXaAD52" | awk -F"[aA]" '{print $6}' #有a和b的地方就分割

例3:使用FS指定分隔符
echo "12AxAbADXaAD52" | awk 'BEGIN {FS="aA"} {print $2}' #aA作为分割符
D52

例4:过滤出本系统的IP地址
ifconfig ens33 | grep netmask #用grep过滤出一行
inet 192.168.1.63 netmask 255.255.255.0 broadcast 192.168.1.255
ifconfig ens33 | grep netmask | awk '{print $2}' #默认以空格做分割取第二行
192.168.1.63

(2)关系运算符的使用
例1:

echo "3 2 3 4 5" > a.txt
awk '{print $1+10}'  a.txt 
13

例2:
echo "one two three four" | awk '{print $3}' #打印第三列数
three
echo "one two three four" | awk '{print $NF}’ #打印最后一列数值
four

echo "one two three four" | awk '{print $(NF-2)}' #打印倒数第3列
two
echo "one two three four" | awk '{print $(NF/2-1)}' # $()里可以做运算

参数: $NF 最后一列
例2:打印出passwd文件中用户UID小于10的用户名和它登录使用的shell
awk -F: '$3<10{print $1 $NF}' /etc/passwd #直接输出格式太乱
awk -F: '$3<10{print $1 "<======>" $NF}' /etc/passwd #awk格式化输出

在$1和$NF之间加一下\t tab
awk -F: '$3<10{print $1"\t"$NF}' /etc/passwd #注:awk 最外面使用了单引号’’ ,里面都使用双引号“”

awk -F: '$3<10{print $1,$NF}' /etc/passwd # 输出多个列时,可以加,分隔一下

例2:打印出系统中UID大于1000且登录shell是/bin/bash的用户

awk -F: '$3>=1000 && $NF=="/bin/bash"{print $1"\t"$NF}' /etc/passwd 
aiwen	/bin/bash

(3)在脚本中的一些应用

例:统计当前内存的使用率

  1 #! /bin/bash
  2 total=`free -hm|grep -i mem|awk '{print "总大小:"$2,"已用:"$3,"可用:"$4}'` #-i 忽略大小写
  3 echo "当前系统内存使用情况如下:"
  4 USEFREE=`free -m |grep -i mem|awk '{print $3/$2*100"%"}'`
  5 echo -e "内存使用百分比:\e[31m${USEFREE}\e[0m"
  6 echo  $total

[root@localhost tmp]# sh free.sh
当前系统内存使用百分比为:
内存使用百分比:33.3333%

awk高级应用

命令格式:

awk  [-F | -f | -v ] ‘BEGIN {} / / {command1;command2} END {}’file  
	-f		调用脚本
	-v		定义变量
	‘{}’	引用代码块
     {}		命令代码块,包含一条或多条命令
	BEGIN	初始化代码块
	/ str /		匹配代码块,可以是字符串或正则表达式
	{print A;print B}		多条命令使用分号分隔
	END		结尾代码块

在awk中,pattern有以下几种:
1) empty空模式,这个也是我们常用的
2) /regular expression/ 仅处理能够被这个模式匹配到的行

例:打印以root开头的行

awk -F: '/^root/{print $0}' /etc/passwd

root: x:0:0:root:/root:/bin/bash

3) 行范围匹配 startline,endline
例1:输出行号大于等于3且行号小于等于6的行
awk -F: '(NR>=3&&NR<=6){print NR,$0}' /etc/passwd #注意分割符后面要有空格

内置变量的特殊用法:
$0 表示整个当前行
NF 字段数量 NF(Number 数量 ; field 字段)
NR 每行的记录号,多文件记录递增 Record [ˈrekɔ:d]
\t 制表符
\n 换行符
~ 匹配
!~ 不匹配
-F'[:#/]+' 定义三个分隔符

例1:使用NR行号来定位,然后提取IP地址

 ifconfig ens33 | awk -F " " 'NR==2{print $2} '  #NR==2表示行号

例2:NR与FNR的区别

awk '{print NR"\t" $0}' /etc/hosts /etc/hostname   #多份文件行号会一直加
awk '{print FNR"\t" $0}' /etc/hosts /etc/hostname  #会重新开始统计行号。

注:对于NR来说,在读取不同的文件时,NR是一直加的 ;
对于FNR来说,在读取不同的文件时,它读取下一个文件时,FNR会从1开始重新计算的

例3:使用3种方法去除首行

route -n | grep -v ^Kernel  

方法2:sed 1d #删除第1行的内容

route -n | sed  1d

方法3: awk

route -n | awk 'NR!=1 {print $0}'

例4:匹配,使用awk查出以包括root字符的行 , 有以下3种方法

 awk -F: "/root/{print}" /etc/passwd
 awk -F: "/root/" /etc/passwd
 awk -F: '/root/{print $0}' /etc/passwd

做一个不匹配root行:

awk -F: '!/root/{print $0}' /etc/passwd

以root开头的行:

awk -F: '/^root/{print $0}' /etc/passwd

以bash结尾的行:

awk -F: '/bash$/{print $0}' /etc/passwd
root: x:0:0:root:/root:/bin/bash

=====以下知识,了解----------

例5:条件表达式
表达式?if-true:if-false 问号前面是条件,如果条件为真执行if-true,为假执行if-false

例1:如果passwd中UID小于10,则给变量USER赋值成aaa,否则赋值成bbb

 awk -F: '{$3<10? USER="aaa":USER="bbb";print $1,USER}' /etc/passwd 

在这里插入图片描述

用if(条件){命令1;命令2}elif(条件){命令;}else{命令}中,在比较条件中用( )扩起来,在AWK中,如果条件为1为真,0为假
例:如果UID大于10 ,则输出user=>用户名,否则输出pass=>用户名

 awk -F: '{if($3<10){print "user=>"$1}else{print "pass=>"$1}}' /etc/passwd

~ 匹配
!~ 不匹配

例:查出行号小于等于5且包括bin/bash的行

awk -F: '{if($3<=5 && $NF ~ "bin/bash"){print $1,$NF}}' /etc/passwd
root /bin/bash

例6:变量

1) 用-v指定 var=value 变量名区分大小写的
2) 在程序中直接定义
3) 在awk里,使用变量不用加$符号。

var="test" 

awk 'BEGIN{print "'$var'"}' #引用变量时,使用单引号+双引号括起来

例7:格式化输出

printf命令:格式化输出 printf “FORMAT”,item1,item2…

format使用注意事项:
1、其与print命令的最大不同是,printf需要指定format样式
2、format用于指定后面的每个item的输出格式
3、printf语句不会自动打印换行符;\n
4、format格式的指示符都以%开头,后跟一个字符;如下:
%c: 显示字符的ASCII码

%d, %i:十进制整数
%e, %E:科学计数法显示数值
%f: 显示浮点数
%g, %G: 以科学计数法的格式或浮点数的格式显示数值;
%s: 显示字符串
%u: 无符号整数
%%: 显示%自身

例1:输入passwd文件中的第1列内容,输出时不会换行
awk -F: '{printf "%s",$1}' /etc/passwd ##不会自动换行
在这里插入图片描述

`awk -F: '{printf "%s\n",$1}' /etc/passwd`  #换行输出

例3:在输出的字母前面添加自定义字符串USERNAME:

awk -F: '{printf "USERNAME: %s\n",$1}' /etc/passwd

在这里插入图片描述

例4:对$1和$NF都做格式化输出

 awk -F: '{printf "USERNAME: %s %s\n",$1,$NF}' /etc/passwd 

例5:对$1和$NF都做格式化输出,在 1 和 1和 1NF两者之间添加一串====字符进行输入

 awk -F: '{printf "USERNAME: %s=========%s\n",$1,$NF}' /etc/passwd
USERNAME: root=========/bin/bash
USERNAME: bin=========/sbin/nologin

awk修饰符:
N: 显示宽度;awk
-: 左对齐;
一个字母占一个宽度。默认是右对齐

例1:显示时用10个字符串右对齐显示。如果要显示的字符串不够10个宽度,以字符串的左边自动添加。一个字母占一个宽度。默认是右对齐

awk -F":" '{printf "%10s\n",$1}'  /etc/passwd

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值