shell 脚本基础

什么是shell

  🍒史上最全教程:http://c.biancheng.net/shell/base/

  🍇shell是一个命令解释器,他在操作系统的最外层,负责用户和系统内核的一个交互,把用户输入的命令翻一个系统,然后将输入、输出的结果反馈给系统用户

通俗来讲shell就是把Unix/Linux下的命令放入一个文件内集中执行,它不仅是一个连接用户和Linux内核的程序,又是一个管理Linux的脚本语言的命令解释器,它是利用了shell功能写成的一个程序,将shell语句放入文件然后将结果反馈给用户,shell提供了数组、循环、条件以及逻辑判断等重要功能。

我们所输入的命令计算机是不可以设别的,它需要一种程序来进行翻译成自己识别的二进制程序,同时把结果反馈给我们

  shell 的种类:https://www.cnblogs.com/leilong/p/9206487.html

编程语言的分类

  • 低级语言(面向机器的语言):

   机器语言:二进制语言

   汇编语言:符号语言,使用助记符来代替操作码,也就是用符号代替二进制语言

  • 高级语言(面向人类可读的语言):

     静态语言:编译型语言 c、c++、java

     动态语言:解释型语言 php、shell、python、Perl

         gcc编译器:(解释器)讲人类理解的语言翻译成机器理解的语言

🍐编程语言:                                                                               

     

🥝解释型语言

🍋语言分类

什么是shell脚本

🍈通俗来讲shell脚本就是把Unix/Linux下的命令放入一个文件内集中执行,它不仅是一个连接用户和Linux内核的程序,又是一个管理Linux的脚本语言的命令解释器/bin/bash和/bin/sh,它是利用了shell功能写成的一个程序,将shell语句放入文件然后将结果反馈给用户,shell提供了数组、循环、条件以及逻辑判断等重要功能。

1. 书写脚本

cat /server/scripts/shell.sh
#!/bin/bash   ----> 指定脚本默认使用的命令解释器 第一行
echo "hello word"


#执行结果
[root@m01 /server/scripts]# sh shell.sh 
hello word

标注:

  • 语法正确,文件有+x执行权限chmod +x shell.sh
  • 以 # 开头,整行注释不执行
  • 文件以 .sh 结尾
  • shell 脚本的执行方式

2. shell 脚本执行方式

  • /root/shell.sh 或者 ./shell.sh (要有+x执行权限)usr/bin/bash
  • bash shell.sh 或 sh shell.sh (可以不对脚本文件添加权限)
  • sh < shell.sh (重定向)
  • source 或 . 替你在当前环境中 执行1次脚本 ,source 一般用来实现include 功能 或配置环境变量 别名后生效. /etc/init.d/functions

注:在/etc/shells文件系统中给出系统已知的 shell :/bin/sh、/bin/bash、/usr/bin/sh、/usr/bin/bash

shell 变量及运用

shell 变量

  🍎变量是shell 传递数据的一种方法。变量是用来代表每个值的符号名。就好比我们小时候数学里的 变量和变量值,比如y=x+1 那么 相当与x+1的结果赋值给了y ,当然我么也可以不断给y赋值,之后赋值的y会把之前的值给替换掉。

变量的命名规则:

  1. 命令只能使用英文字母,数学和下划线,首个字母不能以数字开头。

  2. 中间不能有空格,可以使用下划线( _ )

  3. 不能使用标点符号。

  4. 不能使用bash里的关键字( 可用help帮助 )

  5. 等号 = 用于为变量分配值,等号两边不能有空格

  6. 变量存储的数据类型是整数类型和字符串类型

  7. 在赋值时建议用引号括起来。因为如果字符串中存在空格符号,需要使用单引号或双引号。

  8. 要对变量进行调用,在变量前加 $ 符号

变量的分类:

  1. 用户自定义变量

  2. 环境变量:主要保存和系统操作环境相关的数据。

  3. 位置参数变量:主要是用来向脚本当中传递参数或数据,变量名不能自定义,变量作用是固定的。

  4. 预定义变量:是Bash 中已经自定义好的变量,变量名不

  5. 能自定义,变量作用也是自定义好的。

按照变量作用域可以分成 2 类:全局变量和局部变量

局部变量 是 shell 程序内部定义的,其作用仅限于定义它的程序,对其他程序不可见。包括:用户自定义变量、位置变量和预定义变量。

全局变量 是环境变量,其值不随 shell 脚本的执行结束而消失。

内置环境变量

环境变量 含义
PATH 指定命令的搜索路径
HOME 指定用户的主工作目录(即用户登录系统时,默认路径)
HISTSIZE 指保存历史命令记录的条数
LOGNAME 指当前用户的登录名
HOSTNAME 指主机的名称,程序如果要用到主机名,通常从这个环境取得
SHELL 指当前用户时那种Shell
LANG/LAGUGE 语言相关的环境变量,使用多种语言的用户可以修改此环境变量。/etc/sysconfig/i8n
MAIL 指当前用户的邮件存放目录
PS1 命令基本提示符,root用户是#,普通用户是$
PS2 附属提示符,默认是 ">"
UID 记录用户的UID信息
HISTCONTROL 控制history命令是否记录以空格开头的命令

修改和查看环境变量的命令

命令 含义
echo 显示某个环境变量值 echo $PATH
export 设置一个新的环境变量 export HELLO="hello" (可以无引号)
env 显示所有环境变量
set 显示本地定义的shell变量
unset 清空环境变量 unset=HELLO
readonly 设置只读环境变量 readonly

自定义环境变量

  🍌变量名命名规则:由字母或下划线打头,不允许数字开头,后面由字母,数字或下划线组成,并且大小字母意义不同。在使用变量时,在变量名前加 $

1. 例子:自定义环境变量VAR1

[root@m01 /]# VAR2=123
[root@m01 /]# echo $VAR
123

2. 例子:错误的演示,等于号之间的空格和数字开头的变量

[root@m01 /]# VAR = 123
-bash: VAR: command not found
[root@m01 /]# VAR =123
-bash: VAR: command not found
[root@m01 /]# VAR= 123
-bash: 123: command not found
[root@m01 /]# 3VAR=123
-bash: 3VAR=123: command not found

3. 例子:变量值的叠加,${ }

 $name 是${name}的精简版,但是在某些情况下需要使用到,必须使用 { } 才能显示想要的结果

[root@m01 /]# web=nginx 
[root@m01 /]# echo ${web}.org
nginx.org
[root@m01 /]# echo www.${web}.org
www.nginx.org
[root@m01 /]# echo $weborg   #无法显示空白

环境变量

  bash有两个基本的系统级配置文件:/etc/bashrc/etc/profile。这些配置文件包含了两组不同的变量:shell变量环境变量。shell变量是局部的,而环境变量是全局的。环境变量是通过shell命令来设置。设置好的环境变量又可以被所以当前用户的程序使用。

1. 例子:局部变量

[root@m01 ~]# VAR1=123
[root@m01 ~]# echo $VAR1
123
[root@m01 ~]# cat VAR1.sh
#!/bin/bash
echo $VAR1
[root@m01 ~]# echo $VAR1
123
[root@m01 ~]# bash VAR1.sh #或者sh 执行脚本时,会使用另一个bash去执行,就访问不到$VAR1的值

2. 例子:env命令查看全局变量

#用户切换时也可以用此命令判断环境变量是否切换成功

[root@m01 ~]# env | grep PATH  #系统命令路径
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

3. 例子:export把局部变量输出为全局变量

[root@m01 ~]# export  VAR1=123
[root@m01 ~]# echo $VAR1
123
[root@m01 ~]# cat VAR1.sh
#!/bin/bash
echo $VAR1
[root@m01 ~]# bash VAR1.sh   #引用环境变量
123

4. 例子:让环境变量永久生效,可以把定义好的变量写入配置文件

当登录系统或新开启一个ssh连接启动bash进程时,一定会加载这4个配置文件:

配置文件 作用
/etc/profile 系统全局环境和登录系统的一些配置
/etc/bashrc shell 全局自定义配置文件,用于自定义 shell
/root/.bashrc 用于单独自定义某个用户的 bash
/root/.bash_profile 用户单独自定义某个用户的系统环境

四个配置文件的先后顺序

在每个文件追加一条echo输入文件的名字

[root@m01 ~]# echo 'echo  /etc/profile ' >> /etc/profile
[root@m01 ~]# echo 'echo  /etc/bashrc' >> /etc/bashrc
[root@m01 ~]# echo 'echo  /root/.bashrc ' >> /root/.bashrc
[root@m01 ~]# echo 'echo  /root/.bash_profile ' >> /root/.bash_profile
[root@m01 ~]# ssh root@172.16.1.61
/etc/profile
/etc/bashrc
/root/.bashrc
/root/.bash_profile

下面开始插入永久环境变量:

 

#在/etc/profile我们一般会把服务环境放在这里

[root@m01 ~]# vim /etc/profile
export $VAR1=123
[root@m01 ~]# source /etc/profile  #加载文件

shell位置变量

  shell 解释执行用户的命令时,将命令的第一个字符作为命令名,而其他字符作为参数。

    $0获取当前执行 shell 脚本的文件名,包括脚本路径,命令本身.

      $n获取当前脚本的第n个参数 当n大于9时 用${10}表示.

[root@m01 ~]# vim VAR1.sh
#!/bin/bash
echo "本shell脚本的文件名: $0"
echo "第1个参数:  $1"
echo "第2个参数:  $2"
echo "第3个参数:  $3"
echo "第4个参数:  $4"
[root@m01 ~]# chmod +x VAR1.sh
[root@m01 ~]# ./VAR1.sh 11 22 33 44
本shell脚本的文件名:./VAR1.sh
第1个参数: 11
第2个参数: 22
第3个参数:33
第4个参数:44
#使用场景:服务器启动传参数
[root@m01 ~]# /etc/init.d/network restart             #centos6启动方式

链接博文:https://blog.csdn.net/yangyang3_/article/details/80378733

命令的替换,使用$()或反引号

例1:在命令中调用date命令

扩展:date 命令是显示或设置系统时间与日期

-s <字符串>:根据字符串来设置日期与时间。字符串前后必须加上双引号;

例:格式化输出

[root@m01 ~]# date +"%Y-%m-%d"   #今天时间,一般备份数据需要用这个
2019-12-29
[root@m01 ~]# date +"%Y-%m"   #只显示年月
2019-12
[root@m01 ~]# date +"%Y-%m-%d %H:%M.%S"   #日期加时间
2019-12-29 20:31.18
[root@m01 ~]# date +"%Y/%m/%d %H/%M/%S"    #使用/做分隔符
2019/12/29 20/31/29
[root@m01 ~]#  date +"%Y-%m-%d-%H-%M-%S" #使用-做分隔符,一般备份数据需要用这个
2019-12-29-20-31-46

注: %y 年份只显示2位, %Y年份显示4位

date命令加减操作:
date +%Y%m%d                   #显示当天年月日
date -d "+1 day" +%Y%m%d       #显示明天的日期
date -d "-1 day" +%Y%m%d       #显示昨天的日期
date -d "-1 month" +%Y%m%d     #显示上一月的日期
date -d "+1 month" +%Y%m%d     #显示下一月的日期
date -d "-1 year" +%Y%m%d      #显示前一年的日期
date -d "+1 year" +%Y%m%d      #显示下一年的日期

设定时间:

date -s 20180523 #设置成20120523,这样会把具体时间设置成空00:00:00

date -s 01:01:01 #设置具体时间,不会对日期做更改

date -s "2018-05-23 01:01:01" #这样可以设置全部时间

例2:在命令中调用date命令

[root@m01 ~]# echo `date`      #在脚本中优先执行反引号里的命令
Sun Dec 29 20:49:37 CST 2019
[root@m01 ~]# echo $(date)
Sun Dec 29 20:50:31 CST 2019
[root@m01 ~]# echo `date +"%Y-%m-%d"`
2019-12-29

命令的嵌套使用,$($( ))

[root@m01 ~]# find /etc/ -type f 
[root@m01 ~]# VAR1=$( tar zcvfP root.tar.gz $(find /etc/ -type f ))
[root@m01 ~]# echo $VAR1  #查看值, VAR1中存储着tar的标准输出

shell 中单引号与双引号区别

' ' 单引号中所有的字符包括特殊字符($ , ' ' 和 \ )都将解释成字符本身的特殊字符。

" " 在双引号中,除了$ , ' ' ,和 \ 以外所有的字符都解释成字符本身,拥有“调用变量的值”、“引用命令” 和 “转义符” 的特殊含义

注:\ 转义符,跟在 \ 之后的特殊符号将失去特殊含义,变为普通字符。\$ 将输出 “$” 符号,而不当作变量引用。

[root@m01 ~]# echo $VAR1 
110
[root@m01 ~]# echo \$VAR1
$VAR1

例一:给变量赋予多个单词,需要使用单引号和双引号

[root@m01 ~]# VAR1='12306'
[root@m01 ~]# echo $VAR1
12306

例二:赋值时单引号和双引号的区别

[root@m01 ~]# VAR1='12306'
[root@m01 ~]# VAR1="kd $VAR1"
[root@m01 ~]# VAR2='kd $VAR1'
[root@m01 ~]# echo $VAR1
kd 12306
[root@m01 ~]# echo $VAR2    
kd $VAR1

注:单引号之间的内容原封不动赋值给变量,双引号之间的内容如有特殊符号会保留它的特殊含义

删除变量

[root@m01 ~]# unset VAR1
[root@m01 ~]# echo $VAR1

特殊变量

有些变量是一开始执行Script脚本时就会设定,且不能被修改,但我们不叫只读的系统变量,而叫它特殊变量,这些变量当一执行程序就有了

变量 含义
$* 以一个单字符串显示所有向脚本传递的参数
$# 传递到脚本的参数个数
$$ 当前进程的进程号 PID
$? 显示最后命令的推出状态;0表示没有错误,非0代表错误
$! 后台运行的最后一个进程的进程号 pid
$0 脚本的名字
$@ 取出所有参数 加上双引号:每个参数都是独立

例子:

[root@m01 ~]# vim VAR1.sh
#!/bin/bash
echo "$* 表示这个程序的所有参数 "
echo "$#  表示这个程序的参数个数"
echo "$$  表示程序的进程ID "
touch /tmp/b.txt &
echo "$!  执行上一个后台指令的PID"
echo "$$  表示程序的进程ID "
echo "$? 表示上一个程序执行返回结果 "

[root@m01 ~]# sh VAR1.sh 11 22 33 44 55 66 
11 22 33 44 55 66 表示这个程序的所有参数 
6  表示这个程序的参数个数
88800  表示程序的进程ID 
88801  执行上一个后台指令的PID
88800  表示程序的进程ID 
0 表示上一个程序执行返回结果 

变量字串

优秀博客:http://www.zsythink.net/archives/2276/

当我们需要对变量的值进行处理时,我们就会用到以下方法进行处理

  • ${a}:返回变量内容
[root@m01 ~]# a=web
[root@m01 ~]# echo $a
web   #直接显示变量返回值
  • ${#a}:返回变量内容的长度(按字符),也适用与特殊变量
[root@m01 ~]# a=nginx
[root@m01 ~]# echo ${#a}
5           #输出变量的值长度为5(包括空格也算一个字符)
  • ${a:offset} (offset偏移量,这里写数值) 在变量中,从位置offset(偏移量)之后开始提取字符到结尾
[root@m01 ~]# a=nginx
[root@m01 ~]# echo ${a:0}
nginx
[root@m01 ~]# echo ${a:1}
ginx        #查看从1以后的字串,默认从0开始
  • ${a:offset:length} :在变量中,从位置offset(偏移量,数值)之后开始提取长度为length(长度,这里也是数值)的字串
[root@m01 ~]# a=nginx
[root@m01 ~]# echo ${a:3:2}
nx        #打印从开头第三个字符之后的两个字符为nx
  • ${a#word}:从变量开头开始删除最短匹配word的字串
[root@m01 ~]# a=aaabbbcccddd
[root@m01 ~]# echo ${a#a*b}
bbcccddd    #删除了开头到b最短的字符
[root@m01 ~]# echo ${a##aaab}
bbcccddd    #也可以直接输入内容,注意是要从第一个字母开始

注:这里必须从开头开始,*是代表任意

  • ${a##word}:从变量开头开始删除最长匹配Word的字串
[root@m01 ~]# a=aaabbbcccddd
[root@m01 ~]# echo ${a##a*b}
cccddd      #从开头删除匹配到b的最长字符串
[root@m01 ~]# echo ${a##aaabb}
bbcccddd    #也可以直接指定,这样的话就不会匹配最长值了

注:以上两个,如果指定的话,就删除指定的值,不会去匹配最长最短的值了

  • ${a%word}:从变量开始删除最短匹配Word的子串
[root@m01 ~]# a=aaabbbcccddd
[root@m01 ~]# echo ${a%c*b}
aaabbbc    #从变量结尾开始删除最短匹配Word的字串
[root@m01 ~]# echo ${a%ccddd}
aaabbbc # 同样可以指定字符串来删除
  • ${a%%word}:从变量结尾开始删除最长匹配Word的字串
[root@m01 ~]# a=aaabbbcccddd
[root@m01 ~]# echo ${a%%c*d}
aaabbb     #从结尾开始匹配c到结尾d的最长的字符串并删除
[root@m01 ~]# echo ${a%%ccddd}
aaabbbc    #同样可以指定字符串来删除
  • ${a/pattern/string}:使用string替换第一个匹配的pattern
[root@m01 ~]# a=aaabbbcccddd
[root@m01 ~]# echo ${a/b/f}
aaafbbccc  #替换掉第一个匹配的字符
  • ${a//pattern/string}:使用string替换所有匹配的pattern
[root@m01 ~]# a=aaabbbcccddd
[root@m01 ~]# echo ${a//b/f}
aaafffccc    #替换掉全部匹配的字符

数学运算

使用$(( ))

格式:$(( 表达式1,表达式2 ))

特点:

  1. 在双括号结构中,所有表达式可以像C语言一样,如:a++,b-- 等 。a++等价于 a=a+1

  2. 在双括号中,所有变量可以不加入:”$“符号前缀

  3. 双括号可以进行逻辑运算,四则运算

  4. 双括号结构 扩展了 for ,while,if 条件测试运算

  5. 支持多个表达式运算,各个表达式之间用 ” , “ 分开

常用的算数运算符

运算符含义(*表示常用)
+、- 加法(或正号)、减法(或负号)*
*、/、% 乘法、除法、取余(取模)*
** 幂运算*
++ 、-- 增加及减少,可前置也可以放在变量结尾,默认步长为1*
!、&&、|| 逻辑非(取反)、逻辑与(and)、逻辑或(or)
==、!=、= 比较符号(相等、不相等,对于字符串” = “也可以表示相当)
<<、>> 向左移位、向右移位
~、|、&、^ 按位取反,按位异或、按位与、按位或
=、+=、-=、*=、/=、%= 赋值运算符,例如a+=1 相当a=a+1,a-=1 相当a=a-
?: 条件判断
[root@m01 ~]# b=$((1+2))
[root@m01 ~]# echo $b
3
[root@m01 ~]# echo $((2*3))
6
[root@m01 ~]# echo $((b++))
4
[root@m01 ~]# echo $((++b))
6

说明:a++或a--为先赋值再+1或-1 ; ++a或--a为先加1或减1,然后在赋值

expr 命令

expr命令是一个手工命令行计数器,用于在UNIX/LINUX下求表达式变量的值,一般用于整数值,也可用于字符串。

表达式说明:

  • 用空格隔开每个项;

  • 用 / (反斜杠) 放在 shell 特定的字符前面;

  • 对包含空格和其他特殊字符的字符串要用引号括起来

1. 计算字串长度

> expr length “this is a test”
 14

2. 抓取字串

> expr substr “this is a test” 3 5
is is

3. 抓取第一个字符数字串出现的位置

> expr index "sarasara"  a
 2

4. 整数运算

 > expr 14 % 9
 5
 > expr 10 + 10
 20
 > expr 1000 + 900
 1900
 > expr 30 / 3 / 2
 5
 > expr 30 \* 3 (使用乘号时,必须用反斜线屏蔽其特定含义。因为shell可能会误解显示星号的意义)
 90
 > expr 30 * 3
 expr: Syntax error

流程控制语句if

read 命令

read 内部命令被用来从标准输入读取单行数据。这个命令可以用来读取键盘输入,当使用重定向的时候,可以读取文件中的一行数据。

菜鸟教程:https://www.runoob.com/linux/linux-comm-read.html

参数 含义
-s 一般用于密码输入,隐藏输入的信息
-p 显示提示信息,参数后面用 " 内容 "
-t 秒数,限时输入信息 -t 3 #三秒内输入
-n 设置输入信息长度
[root@m01 ~]# vim  VAR1.sh  #写入以下内容
#!/bin/bash
read -p "请输入姓名:" NAME
read -p "请输入年龄:" AGE
read -p "请输入性别:" SEX

cat<<eof
*********************
你的基本信息如下:
姓名: $NAME
年龄:$AGE
性别:$SEX
********************
eof
[root@xuegod63 ~]# sh VAR1.sh
请输入姓名:ll
请输入年龄:18
请输入性别:man
*********************
你的基本信息如下:
姓名: ll
年龄:18
性别:man

流程控制语句 if

if 语句语法格式:

if condition
then
    command1 
    command2
    ...
    commandN 
fi

单分支流程图:

案例:把计算脚本 修改为 if 判断格式

vim cal-awk-if.sh
#!/bin/bash
num1=$1
num2=$2

#1.检查参数的个数是否正确
[ $# -ne 2 ] && {
echo "Usage:$0 num1 num2"
exit
} 

#2.检查参数是否为数字
expr $num1 + $num2 &> /dev/null
[ $? -ne 0 ] && {
echo "Usage:$0 num1 num2"
exit 2
}

#3.进行计算
awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1/n2}'
awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1*n2}'
awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1-n2}'
awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1+n2}'

修改 比较两个数字大小为 if 格式

vim compare.sh
#!/bin/bash
read -p "PLS INPUT num:2" n1 n2 
num1=$n1
num2=$n2
if [[ "$num1" =~ ^[0-9]+$ && "$num2" =~ ^[0-9]+$ ]]
then
else
    echo "Usage:$0 num1 num2"
    exit 1 
fi 

if [ $num1 -eq $num2 ]
then
    echo "$num1 == $num2"
elif [ $num1 -ge $num2 ]
then
    echo "$num1 > $num2"
else
    echo "$num1 < $num2"
fi     

双分支语句if else

if 双分支格式

if condition
then
    command1 
    command2
    ...
    commandN
else
    command
fi

双分支流程图:

案例:查看内存情况,不足发送邮件

vim chk_mem.sh
#!/bin/bash

mem=$(free -m |awk 'NR==2{print $NF}')

if [ "$mem" -lt "10000" ]
then
    echo "Out of Memory" | tee /root/mem.log
    mail -s "Out of Memory" XXXX@qq.com < /root/mem.log    
else
    echo "Memort is ok !"
fi    

多分支语句 if elif else

语法格式:

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

多分支流程图:

案例:判断系统用户在系统中是否存在,是否有家目录

[root@m01 ~]# vim   if-4.sh
#!/bin/bash

read -p "input a user:" user
if grep $user /etc/passwd;then
    echo "the user $user exists on this system"
elif ls -d /home/$user ; then
    echo "the user $user not exists on this system"
    echo "$user has a home directory"
else
    echo "the user $user not exists on this system"
    echo "$user not has a direcotry"
fi     
[root@m01 /server/scripts]# sh if-4.sh 
input a user:root 
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
the user root exists on this system

test 测试命令

shell 中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试

格式:test 测试文件

如果结果是对的,也叫结果为真,用 $?=0表示,反之为假,用非0表示

数值比较

参数说明示例
-eq 等于则为真 [ "$a" -eq "$b" ]
-ne 不等于则为真 [ "$a" -ne "$b" ]
-gt 大于则为真 [ "$a" -gt "$b" ]
-ge 大于等于则为真 [ "$a" -ge "$b" ]
-lt 小于则为真 [ "$a" -lt "$b" ]
-le 小于等于则为真 [ "$a" -le "$b" ]

案例1:比较大小

[root@m01 ~]# vim test1.sh
#!/bin/bash
if test -eq 1 ;then
    echo "ok"
else
    echo "error"
fi     

案例2:比较整数大小

[root@m01 ~]# vim test2.sh
#!/bin/bash

read -p "input var1 var2:" var1 var2
if [ $var1 -gt $var2 ];then
    echo "$var1 > $var2"
elif [ $var1 -lt $var2 ];then
    echo "$var1 < $var2"
else
    echo "$var1 = $var2"
fi    

注意:在做数值比较时,只能用整数

字符串比较

参数 说明 示例
== 等于则为真 [ "$a" == "$b" ]
!= 不等于则为真 [ "$a" != "$b" ]
-z 字符串 字符串的长度为零则为真 [ -z "$a" ]
-n 字符串 字符串的长度不为空则为真 [ -n "$a" ]
str1 > str2 str1 大于 str2 为真 [ str1 \> str2 ]
str1 < str2 str1 小于 str2 为真 [ str1 \< str2 ]

案例1:根据用户判断是否是超级管理员

[root@m01 ~]# vim test3.sh
#!/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    

案例2:在做字符串大小比较的时候,注意字符串的顺序

  • 大于号和小于号必须转义,否则shell会把它当作重定向

  • 大于号和小于号他们的顺序和sort排序不一样

  • test比较测试中,它使用的是ASCII顺序,大写字母是小于小写字母;sort 刚好相反

扩展:ASCII 美国信息交换标准代码 是基于阿拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧英语。他是现今最通用的单字节编码系统,并等同于国际标准 ISO/IEC 646.

 

[root@m01 ~]# cat test-4.sh
#!/bin/bash
var1=test
var2=Test
if [ $var1 \> $var2 ];then
    echo "$var1 > $var2"
else
    echo "$var1 < $var2"
fi    

文件比较

参数说明示例
-e 文件名 如果文件或目录存在则为真 [ -e file ]
-r 文件名 如果文件存在且可读则为真 [ -r file ]
-w 文件名 如果文件存在且可写则为真 [ -w file ]
-x 文件名 如果文件存在执行权限则为真 [ -x file ]
-s 文件名 如果文件存在且至少有一个字符则为真 [ -s file ]
-d 文件名 如果文件存在且为目录则为真 [ -d file ]
-f 文件名 如果文件存在且为普通文件则为真 [ -f file ]
-c 文件名 如果文件存在且为字符型文件则为真 [ -c file ]
-h 文件名 如果文件存在且是软连接文件则为真 [ -h file ]
-b 文件名 如果文件存在且为块特殊文件则为真 [ -d file ]
file1 -nt file2 检查file1 是否比 file2 新 [ file1 -nt file2]
file1 -ot file2 检查file1是否比 file2 旧 [ file1 -ot file2]

 案例1:清空日志文件

[root@m01 ~]# vim clear-log.sh
#!/bin/bash
#clear /var/log/messages
#确定当前是root用户

if [ $USER != "root" ];then
    echo "你必须使用root用户才能执行脚本"
    echo 10  #直接退出
fi

#判断文件是否存在
if [ ! -f /var/log/messages ];then
    echo "文件存在"
    echo 12
fi

#保留最近100行的日志文件
tail -100 /var/log/messages > /var/log/mesg.tmp

#日志清理
> /var/log/messages
#cat /dev/null > /var/log/messages

mv /var/log/messages.tmp /var/log/messages
echo "Logs clean up"

注:退出码 exit,取值范围是 0-255

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

三种判断方式

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

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

判断第二种

  注:-o = or , -a = and

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

判断第三种

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

例1:设置umask

[root@m01 ~]# vim /etc/profile
 59 if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then
 60     umask 002
 61 else
 62     umask 022
 63 fi
 
[root@m01 ~]# vim  umask.sh
 if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then
    echo "umask 002"
else
    echo "i am root :umask 022"
fi

[root@m01 ~]# sh umask.sh
i am root:umask 022

例2:[[.......]] 和 [.......] 的区别

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

#判断当前用户是否是root
[root@m01 ~]# if [[ $USER == r* ]] ; then echo "hello,$USER" ; else echo $USER not ; fi
hello,root

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

[root@m01 ~]# if [ $USER == r* ] ; then echo "hello,$USER" ; else echo $USER not ; fi
root not 

#对比时 r* ,就表示两个字符串 r*

也可以这样写:

[root@m01 ~]#  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中的通配符

字符含义实例
* 匹配 0 或多个字符 a*b a与b 之间可以有任意长度的任意字符,也可以一个也没有,如 aabcd,axyzb
匹配任意一个字符 a?b a与b之间必须也只能有一个字符,可以是任意字符,如aab,abb,acb,a0b
[list] 匹配 list 中的任意单一字符 a[xyz]b a与b之间必须也只能有一个字符,但只能是 x 或 y 或 z,如:axb ayb azb
[!list] 匹配除 list 中的任意单一字符 a[!0-9]b a与b 之间必须也只能有一个字符,但不能是阿拉伯数字,如 axb,abb,a-b
[c1-c2] 匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z] a[0-9]b 0与9之间必须也只能有一个字符 如 a0b,a1b......a9b
{string,string,....} 匹配 string1 或 string2 (或更多)其一字符串 a{abc,xyz,123}b a与b之间只能是abc 或 xyz 或 123 这三个字符串之一。

例:

[root@m01 ~]# ls /var/log/*.log
[root@m01 ~]# ls /vat/log/????.log
/var/log/boot.log
[root@m01 ~]# touch  /opt/a{1,2,3}.txt
[root@m01 ~]# ls /opt/a[123].txt
/opt/a1.txt  /opt/a2.txt  /opt/a3.txt
[root@m01 ~]# ls /opt/a[1,2,3].txt
[root@m01 ~]# ls /opt/a[13].txt
/opt/a1.txt  /opt/a3.txt 

练习题

1. 命令行传入1个字符或数字,数字等于1则显示1,如果等于2则显示

    如果既不等于1也不等于2,就显示输入不对,然后退出程序

项目分析:

  1. 输入数字

  2. 数字等于1显示1退出

  3. 数字等于2显示2退出

  4. 数字不是1也不是2显示帮助 退出

[root@m01 ~]# cat num.sh
#!/bin/bash
num=$1

#判断参数个数,{ } 花括号里面的命令一块执行
[ $# -ne 1 ]&&{
echo "Usage: input number"
exit 1
}

#判断是否是数学
#需要填写数字
[ $num -eq 1 ]&&{
echo "input number is 1" 
exit 0
}

[ $num -eq 2 ]&&{
echo "input number is 2"
exit 0
}

[ $num -ne 1 -a $num -ne 2 ]&&{
echo "Usage: input number"
exit 1
} 

#使用正则表达式

[root@m01 ~]# cat number.sh
#!/bin/bash

input=$1
[[ ! "$input" =~ ^(1|2)$ ]]&&{
echo input error
exit 1 
}
[ "$input" -eq 1 ]&&{
echo 1
exit 
}
[ "$input" -eq 2 ]&&{
echo 2 
exit 
}

2. 开发shell脚本,使用传参或read实现比较2个整数大小,用条件表达式(禁用 if )进行判断并输出到屏幕方式提醒用户结果。

 注意:用两种方式实现的时候,需要对变量是否为数字及参数是否正确给予提示

项目分析:read / 传参

   判断参数个数是否为2,注意:read命令的时候不需要对比命令行参数

   参数或变量是否是参数

进行比较

[root@m01 ~]# cat number.sh
#!/bin/bash
read -p "please input num1 num2:" n1 n2
num1=$1
num2=$2
 [[ "$num1" =~ ^[0-9]+$ && "$num2" =~ ^[0-9]+$ ]] || {
     echo "Usage: $0 num1 num2"
     exit 1
 }
 
 [ $num1 -eq $num2 ] && {
     echo "$num1 == $num2"
     exit 0
 }
 [ $num1 -gt $num2 ] && {
     echo "$num1 > $num2"
     exit 0
 }
 [ $num1 -lt $num2 ] && {
     echo "$num1 < $num2"
     exit 0
 }
 
 #升级版本
 
 #!/bin/bash
 read -p "please input num1 num2:" n1 n2 
 num1=$1
 num2=$2
 [[ "$num1" =~ ^[0-9]+$ && "$num1" =~ ^[0-9]+$ ]] || {
     echo "Usage: $0 num1 num2"
     exit 1 
 }
 [ $num1 -eq $num2 ] && {
     echo "$num1 == $num2"
     exit 0
 }
 [ $num1 -gt $num2 ] && {
     echo "$num1 > $num2"
     exit
 }
 [ $num1 -lt $num2 ] && {
     echo "$num1 < $num2"
     exit 0
 }

流程控制语句case

  控制语句:用来实现对程序流程的选择、循环、转向和返回等进行控制。case是开端语句的一个组成部分;

  它是根据变量的不同进行取值比较,然后针对不同的取值分别执行不同的命令操作

  适用于多分支,是一个多选择语句

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

case 语句执行流程控制:

 

执行过程:

  首先使用"变量或表达式"的值与值1进行比较,若取值相同则执行值1后的命令序列,知道遇到双引号” ;;  “后跳转至esac

  若值与1不相匹配,则继续与值2比较,若取值相同则执行值2后的命令序列,直到遇到双引号” ::“ 后跳转至esac,表示结束分支。

  依次类推,若找不到任何匹配的值,则执行默认模式 ” *) “ 后的命令序列,直到遇到双分支” esac 后结束分支

注意事项:

  • ”变量或表达式“ 后面必须为单词 in,每一个 ”变量或表达式“ 的值必须以右括号结束,取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有生命开始执行直至 ;;

  • 匹配中的值可以是多个值,通过 ” “ 来分隔

service服务启动nginx服务

[root@m01 ~]# vim  /etc/init.d/nginx
#!/bin/bash
#chkconfig: 2345 99 98
# 上面是chkconfig 默认设置(如果要chkconfig管理就要加上)  2345:启动流程   99:开机顺序 20:关机顺序
# ll /etc/rc.d/rc3.d/S*  不要和里面的服务冲突

#description: nginx-server
nginx=/usr/local/nginx/sbin/nginx
case $1 in


start)
netstat -anptu | grep nginx
if [ $? -eq 0 ]
then
echo "nginx-server is already running"
else
echo "nginx-server begin start"
$nginx
fi
;;


stop)
$nginx -s stop
if [ $? -eq 0 ]
then
echo "nginx-server is stoped"
else
echo "nginx-server stop fail,try again"
fi
;;


status)
netstat -anlpt | grep nginx
if [ $? -eq 0 ]
then
echo "nginx-server is running"
else
echo "nginx-server is stoped"
fi


;;

restart)
$nginx -s reload
if [ $? -eq 0 ]
then
echo "nginx-server is begin restart"
else
echo "nginx-server restart fail"
fi
;;

*)
echo "please enter {start restart status stop}"
;;

esac
exit 0

循环语句

for-do-done

  for循环格式:

for var in list 
do
  commands
done
或:
for var in list ; do
  commands
done

  for循环流程图:

取值列表有多种取值方式,比如:

1. 可以直接读取 in 后面的值,默认以空格分隔

[root@m01 ~]# cat for-1.sh
#!/bin/bash
for var in a1 b1 c1 d1
do
    echo "the text is  $var"
done

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

[root@m01 ~]# cat for-2.sh
#!/bin/bash
for var in a1 "c1 d1" e2 "hello world"
do 
    echo the text is $var
done
[root@m01 ~]# cat for-3.sh
#!/bin/bash
for var in a1 b\'1 "c1 d1" e2 "hello world" |\'s a22
do
    echo the text is $var
done

3. 从变量中取值

[root@m01 ~]# cat for-4.sh
#!/bin/bash
list="a1 b1 c1 d1"
for i in list
do 
    echo is a $1
done    

4. 从命令中取值

[root@m01 ~]# cat for-5.sh
#!/bin/bash
for i in `cat /etc/hosts`
do 
    echo "$i"
done    

5. 自定义shell 分隔符

  默认情况下,bash shell 会以空格、制表符、换行符作为分隔符。通过IFS来自定义分隔符

  指定单个字符做分隔符:

  IFS=: #以:冒号做分隔符

  可以指定多个

  如 IFS='\n:;' #这样赋值会将反斜杠、n、冒号、分号和双引号作为字段分隔符

  注:$'\n' 与 '\n:;' #这个赋值会将反斜杠、n、冒号、分号和双引号作为字段分隔符。

  注:$'\n' 与'\n' 时的区别

  IFS='\n' #将字符 \ 和字符 n 作为 IFS 的换行符。

  IFS=$'\n' #真正的使用换行符作为字段分隔符。

6. C语言风格for

  语法格式:

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

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

[root@m01 ~]# cat for-8.sh
#!/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的升序和降序

[root@m01 ~]# cat for-9.sh
#!/bin/bash
for ((a=1,b=9;a<10;a++,b--))
do 
    echo num is $a-$b
done

例3:使用for循环在/tmp目录下通过随机小写10个字母加固定字符串wei批量创建10个html文件。

获取随机数的方法:https://www.cnblogs.com/gaoyuechen/p/7529282.html

 

#生成随机字符串方法
[root@m01 ~]# openssl rand -base64 10
Sm8rkVboxdDl9Q==
[root@m01 ~]# date +%N | md5sum
3da7567b4e32f56cf78308e37133b1e0  -
[root@m01 ~]# echo $RANDOM
27267
[root@m01 ~]# echo $((RANDOM+1000000))
1022112
[root@m01 ~]# tr -cd 'a-zA-Z0-9' </dev/urandom | head -c 10 
5z3CKhbJxm[root@m01 ~]#

 

[root@m01 ~]# cat tpuc-random.sh
#!/bin/bash
[ -d /tmp/ ] || mkdir /tmp
for n in {1..10}
do
    rand=`tr -cd 'a-zA-Z' </dev/urandom | head -c10`
    file=${rand}_wei.html
    touch /tmp/$file
done



#第二种方法
#!/bin/bash
dir=/test/
create(){
i=1
while (($i<11))
do
cd $dir && touch `tr -dc "a-z"</dev/urandom | head -c 10`_wei.html
i=$(($i+1))
done
}
check(){
if [ -d $dir ];
then
    create
else
    mkdir $dir
    create
}
check
#第三种方法
#!/bin/bash
dir=/test/
mkdir -p /test
for i in `seq 10`
do
    file=$(date +%N%s | md5sum | head -c 10 | tr [0-9] [a-j])
    touch ${dir}${file}_wei.html
done



#tr
#-d 删除
#-c 取反 排除
#tr -cd 'z-a' <

例4:99乘法表

#!/bin/bash
for i in {1..9}
do
    for j in {1..9}
    do
        if [ $i -ge $j ];then
        res=$((i*j))
        echo -n 

while循环语句和循环嵌套

1. while-do-done

  重复测试之指令的条件,只要条件成立就反复执行对应的命令操作,直到命令不成立或为假;

语法格式如下:

while 测试命令
do
命令
done

注意:避免陷入死循环 while true

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

[root@m01 ~]#  cat while-1.sh
#!/bin/bash
var=10
while [ $var -gt 0 ]
do
    echo $var
    var=$[$var-1]
done    

例2:输入如下两数据相乘的效果

自增操作 let var++

自减操作 let var--

[root@m01 ~]# cat while-2.sh
#!/bin/bash
num=1
while [$num -lt 10]
do
    sum=$(( $num*$num ))
    echo "$num*$num  = $sum"
    ((num++))
    # let num++
done    

2. 镶套语句

例1:批量创建用户---密码随机

编写思路:

  1. 脚本功能:手动输入用户名和数量,批量创建系统用户
  2. 关键命令:密码根据date随机产生不相同
  3. 使用到的语法:选择、循环、分支
[root@m01 ~]# cat while-3.sh
#!/bin/bash
#创建一个批量创建脚本
read -p "please input you user name|number:" name num
cat<<eof
*********************
user name:$name
user number: $num
********************
eof
for i  in  $(seq $num)
do
        user=${name}${i}
        id $user &> /dev/null
        if [ $? -ne 0  ]; then
                useradd $user
                pass=`date +%s|md5sum|cut -c 1-8 `
                sleep 1
                echo " $user:$pass" >> /tmp/passwd.log
                echo $pass | passwd --stdin $user  &> /dev/null
                echo " user $user Password creation completed !"
      else
                 echo " This user already exists!"
      fi
done

例2:批量创建 a.txt 文件中的用户

[root@m01 ~]# cat a.txt
#!/bin/bash
dba01
dba02
web01
web02
ssl01

编写脚本的思路

  1. 明确脚本作用:将文件中的用户创建完成
  2. 编写脚本需要的命令:useradd passwd
  3. 把变化的数据使用变量表示
  4. 选择合适的流程控制 (选择、循环、分支)
[root@m01 ~]# cat for-address.sh
#!/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 exit"
    fi
done   

注:&> 不管是正确或者错误的信息都重定向到 /dev/null 里面

例3:打印九九乘法表

注:外层循环循环行,内层循环循环列

规律:内层循环的变量<=外层循环的变量

[root@m01 ~]# cat for-for.sh
#!/bin/bash
for i in `seq 9`
do 
    for j in `seq $i`
    do
        echo -n "$i*$j= `echo $(($i*$j))`"
    done
    echo " "
done  

案例:使用脚本解决ddos攻击

短时间内pv达到100( 阈值 ) 立即调用防火墙命令封掉对应的ip。防火墙命令:iptables -I INPUT -s ip地址 -j DROP 关键命令,awk取出超过100连接数的IP地址

#!/bin/bash
#找到访问的IP地址在进行排序,访问失败超过100次的IP地址进行拒绝
res=/server/scripts/ip_conn.txt
awk -F "[ :]+" '/^tcp/ && /ESTABLISHED/{print $6}' /server/scripts/netstat.log  |sort| uniq -c| sort  -nr > $res

while read cnt ip 
do     
     if [ $cnt -eq 172.16.1.{1..253}];then
        
     else
        #echo "$cont次数 $ip 地址"
        if [ $cnt -gt 2 -a `iptables -nL | grep -wc "$ip"` -eq 0 ]
        then
        iptables -I INPUT -s $ip -j DROP
        fi
     fi
done<$res

跳出循环

break 和 continue

Break:跳出整行循环

Continue:跳过本次循环,进行下次循环

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

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

例1:写一个shell菜单,当按数字键4时退出,否则一直循环显示

[root@m01 ~]# cat break-continue.sh
#!/bin/bash
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命令就不执行了
        echo "your selection is Copy"
    ;;
    2)
        echo "your selection is Delete"
        ;;
    3)
        echo "your selection is Backup"
        ;;
    4)
         echo "Exit ......"
         break    #跳出循环体
         ;;
    *)
         echo "incalide selection,please try again"
    esac
done 

例2:使用交互式批量添加用户

[root@m01 ~]# adduser.sh
#!/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  
#$(seq  $num) 等于  ` seq  $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

shell 数组使用

简介

  数组:数组的特性就是一组数据类型相同的集合

  shell数组的分类:

  • 数据类型的数组
  • 字符串类型的数组

数值类型的数组:一对括号数组,数组中元素之间使用“空格”来隔开

例子:array=(1 2 3 4 5);

字符串类型数组:同样使用一对括号表示数组,其中数组中的元素使用双引号或者单引号包含,同样使用“空格”来隔开

例子:array_string=("abc" "def" "jhi");或者 array_string=('abc' 'def' 'jhi') 

直接定义

就像上面举的例子一样,使用 ( ) 直接定义数组,括号中元素用空格隔开;在括号中也可以声明下标。

#使用()直接数组
days1=(one two three four five)
#或者在()中声明下标,默认从0开始
days2=([0]=’one’ [1]=’two’ [2]=’three’ [3]=’four’)
#下标可以不连续
days3=([0]=’one’ [2]=’three’)

#直接赋值(批量)
arr=(
10.0.0.61
10.0.0.71
10.0.0.81
)

带下标定义

ip[0]=10.0.0.1
ip[1]=10.0.0.2
ip[2]=10.0.0.3
ip[3]=10.0.0.4
echo ${ip[*]} #数组中的所有内容取出

从文件读取数组

array=(`cat /server/scripts/ip.txt`)
echo ${array[*]}

取值

指定下标,使用 [ ] 操作符从数组中对应元素,然后使用$取值,格式:${数组名[索引]}

ip=(10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4)
echo ${ip[0]}
#取出第一个元素
echo ${ip[2]}
#取出第二个元素

获得数组中所有值:${数组名[@]}、${数组名[*]}

${数组名[@]}得到是以空格隔开的元素,可以用在数组遍历中;${数组名[*]}得到的是一整个字符串。

注意:${数组名}并不会获得所有值,它只会获得到第一个元素的值。即${数组名}等价于${数组名[0]}

例1:测试这些网段是否可以ping通

[root@m01 ~]# vim check_ip.sh
#!/bin/bash
. /etc/init.d/functions
#ip_list=(10.0.0.61 10.0.0.71 10.0.0.81)
ip_list=(
10.0.0.61
10.0.0.71
10.0.0.81
)
for ip in  ${ip_list[*]}
do 
    {
    ping -c1 -W1 -i1 $ip &> /dev/null
    if [ $? -eq 0 ]
    then
        action "$ip is ok" /bin/true
    else
        action "$ip is failed" /bin/false
    fi    
    }&
    usleep 500   #单位:微秒
done    

[root@m01 ~]# sh check_ip.sh
10.0.0.61
10.0.0.71
10.0.0.81

例2:试编写一个shell程序,该程序能接受用户从键盘输入的10个整数,然后求出其总和、平均值

#方法一
[root@m01 ~]# cat array-1.sh
#!/bin/bash
file=/server/files/num.txt
[ -s "$file" ]&& > $file
for n in {1..5}
do 
    read -p "input ${n}number:" num
    echo $num >> $file
done 
awk '[sum+=$1]END{print sum,sum/NR}' $file

#方法二:
[root@m01 ~]# cat array-2.sh
#!/bin/bash
file=/server/files/num.txt
[ -s "$file" ]&& > file
for n in {1..5}
do
    read -p "input ${n}number:" num
    echo $num >> $file
done

arr=(`cat $file`)
sum=0
for num in ${arr[@]}
do 
    ((sum+=num))
done
awk -vn=$sum 'BEGIN{print n,n/5}'

Shifs 参数左移指令

  原来旧的 $1 值会消失,但是 $0 (脚本名称)不会改变. 如果你把大量的位置参数传给脚本,那么可以使用 shift 命令存取超过 10 的位置参数, 虽然这个功能也能由 {bracket} 花括号 做到.

$1 <--- $2, $2 <--- $3, $3 <--- $4, 以此类推.
[root@m01 ~]# shifs.sh
#!/bin/bash
# 用 'shift'命令逐步存取所有的位置参数
  
#  给这个脚本一个命名,比如说shft,
#+ 然后以一些参数来调用这个脚本,例如
#          ./shft a b c def 23 skidoo
until [ -z "$1" ]  # 直到所有的位置参数被存取完...
do
  echo -n "$1 "
  shift
done
echo               # 换行.
exit 0

例子:加法计算器

[root@m01 ~]#  cat shift.sh
#!/bin/bash
if [ $# -le 0 ];then
    echo  “没有足够的参数”
    exit
fi

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

测试:

[root@m01 ~]# bash a shift.sh  11 2 3 4 
result is 20

函数的使用

菜鸟教程:https://www.runoob.com/linux/linux-shell-func.html

linux shell 可以自定义函数,然后在shell脚本中随意调用

函数创建语法

方法一:

function name {
        commands
}

注:name 是函数唯一的名称

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

name(){
    commands
}

调用函数语法:

函数名 参数1 参数2

调用函数时可以传递参数。在函数中用$1、$2....来引用传递的参数

函数的使用

例1:

[root@m01 ~]# cat funcTest.sh
#!/bin/bash
parameters1=$1 #全局变量
parameters2=$2 #全局变量
function testFun() {                       #定义函数testFun
        local cc="this is cc"              #定义局部变量$cc
        local dd="this is dd"              #定义局部变量$dd
        echo $parameters1, $parameters2    #访问参数1和参数2
        echo $cc                           #打印局部变量
        return 0                           #shell函数返回值是整形,并且在0~257之间。
}

echo $dd                              #这里将会打印不生效,因为dd是局部变量。
testFun                               #使用函数testFun
[root@m01 ~]# cat fun-1.sh
#!/bin/bash
function fun_1 {
    echo "this is function"
}
function fun_1 {
    echo "this is 12306"
}
fun_1

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

返回值

  使用 return 命令来退出函数并返回特定的退出码

例1:

[root@m01 ~]# cat fun-1.sh
#!/bin/bash
function fun_1 {
    echo "this is function"
    ls /etc/passwd
    return 3
}
fun_1

[root@m01 ~]# bash fun-1.sh
this is function
/etc/passwd
[root@m01 ~]# echo $?
3

注:状态码的确定必需要在函数一结束就运行return返回值;状态码

exit 数字 和 return 数字的区别

详细解释:https://blog.csdn.net/gjs935219/article/details/85293826

  • exit用于在程序运行的过程中随时结束程序,exit的参数是返回给OS

  • return 只是在函数最后添加一行,然后返回数字,只能让函数后面的命令不执行,无法强制退出整个脚本的。

把函数赋值给变量使用

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

[root@m01 ~]# cat fun-3.sh
#!/bin/bash
fun1(){
    read -p "input a value:" va
    echo $[$va*5]
}
num=$(fun1)
echo current num is $num

[root@m01 ~]# sh fun-3.sh
input a value: 22
current num is 110

函数的参数传递

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

[root@m01 ~]# cat fun-4.sh a.txt
#!/bin/bash
fun1(){
    rm -rf $1
}
fun1 $1

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

[root@m01 ~]# touch /root/a.txt
[root@m01 ~]# cat fun-4.sh
#!/bin/bash
fun1(){
    rm -rf $1
}
fun1 /root/a.txt

[root@m01 ~]# bash fun-1.sh   #测试
[root@m01 ~]# ls /root/a.txt
ls: cannot access /root/a.txt: No such file or directory

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

[root@m01 ~]# cat fun-5.sh
#!/bin/bash
fun1(){
    echo $[$1*5]
    echo $[$2*5]
}
fun1 5 2 #直接传两个参数
[root@m01 ~]# bash fun-1.sh #测试
25
4

函数中变量的处理

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

  • 局部变量

  • 全局变量

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

例子:

[root@m01 ~]# cat fun-5.sh
#!/bin/bash
function fun1{
    num1=$[$vsr1*2]
}
read -p "input a num:" var1
fun1
echo the new value is: $num1 

[root@m01 ~]# bash fun-1.sh
input a num:2
the new value is:4

shell 颜色

man手册:http://man7.org/linux/man-pages/man4/console_codes.4.html

shell 脚本中 echo 显示内容带颜色,echo 显示带颜色,需要使用参数-e

格式1:echo -e "\033[背景颜色;文字颜色 m 要输出的字符 \033[0m"

格式2:echo -e "\e[背景颜色;文字颜色 m 要输出的字符\e[0m"

linux命令给字体加颜色命令为:

[root@m01 ~]# echo -e "\E[1;31m红色字\E[0m"
红色字
[root@m01 ~]# echo -e "\033[31m红色字 \033[0m"
红色字

注:其中 42 的位置代表底色,34 位置代表的是字的颜色,0m 是清除所有格式

  1. 字背景颜色和文字颜色之间是英文的分号" ; "

  2. 文字颜色后面有个 m

  3. 字符串前后可以没有空格,如果有的话,输出也是同样有空格

  4. echo 显示带颜色,需要使用参数-e,-e 允许下面列出的加反斜线转义的字符进行解释

控制选项:

  • \033[0m 关闭所有属性

  • \033[1m 设置高亮度,加粗

  • \033[5m 闪烁

常见 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"

实战--自动备份 mysql 数据库脚本

1. 安装mariadb数据库

[root@m01 ~]# yum install -y mariadb mariadb-server -y
# mariadb 是mysql的客户端命令;mariadb mariadb-server 是服务端命令
[root@m01 ~]# rpm -qf /usr/bin/mysql   #查看数据库是否安装
mariadb-5.5.56-2.el7.x86_64
[root@m01 ~]# systemctl start mariadb
登录 mysql:
[root@m01 ~]# mysqladmin -uroot password "123456" 
#给root一个登录密码,实际生产环境下密码更为复杂
[root@m01 ~]# mysql -uroot -p123455
MariaDB [(none)]> show databases;
MariaDB [(none)]> create database china;  #创建china数据库 
MariaDB [china]> create table user (id int); #创建 user 表只有一个id字段
MariaDB [china]> insert into user values(1); #插入一条记录,id字段值1
MariaDB [china]> insert into user values(2); #插入一条记录,id字段值2
MariaDB [china]> select *from user;          #查看表中的数据
+------+
| id   |
+------+
|    1 |
|    2 |
+------+

mysql 自动化备份脚本:

思路:

  1. 检查一下运行环境;目录是否存在,时间,权限,用户

  2. 运行要执行的命令:备份,导入数据

  3. 把命令执行过程中的没有用的文件删除

  4. 弹出命令运行成功的小心(也可以发邮件)

[root@m01 ~]# cat mysql-back-auto.sh
#!/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 "

注:加上定时任务 crontab -e

posted @ 2020-01-05 20:42  kerwin-  阅读(519)  评论(0编辑  收藏  举报