当前位置:   article > 正文

shell学习教程(看这一篇就够了)_shell教程

shell教程

一、什么是shell?

二、shell的分类

编写规范:

文件命名规范:

输入输出重定向

输出重定向

多命令顺序执行

shell脚本的执行

三、shell变量

1. 变量的命名规则:

2.1 用户自定义变量:

2.1.1 变量定义

2.1.2 变量调用

2.1.3 变量查看

2.1.4 变量删除

2.2 环境变量:

2.2.1 环境变量设置

2.2.2 环境变量查询和删除

2.3 位置参数变量:

2.4 预定义变量:

3. 只读变量:

4. 接受键盘输入:

四、shell 运算符

1. 算数运算符

2. 关系运算符

3. 逻辑运算符

4. 字符串运算符

5. 文件测试运算符(重点)

五、小案例

1、小案例:

六、流程控制

1. if条件判断

1.1 单分支if条件

1.2 双分支if条件语句

1.3 多分支if条件语句

2. 多分支case条件语句

3. for循环

4. while循环

5. until循环

6. 函数

7. 特殊流程控制语句

7.1 exit语句

7.2 break语句

7.3 continue语句

一、什么是shell?

shell是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。

Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。

为什么要学习和使用shell?

Shell属于内置的脚本,程序开发的效率非常高,依赖于功能强大的命令可以迅速地完成开发任务(批处理)语法简单,代码写起来比较轻松,简单易学

cat /etc/shells

在linux中有很多类型的shell,不同的shell具备不同的功能,shell还决定了脚本中函数的语法,Linux中默认的shell是 / b a s h / b a s h ( 重 点 ) \color{#FF3030}{/bash/bash(重点)}/bash/bash(重点),流行的shell有ash、bash、ksh、csh、zsh等,不同的shell都有自己的特点以及用途。

二、shell的分类

编写规范:
  1. #!/bin/bash [指定告知系统当前这个脚本要使用的shell解释器]
  2. Shell相关指令
文件命名规范:

文件名.sh .sh是linux下bash shell 的默认后缀

Bash 常用快捷键

快捷键 作用

ctrl+A 把光标移动到命令行开头。如果我们输入的命令过长,想要把光标移动到命令行开头时使用。

ctrl+E 把光标移动到命令行结尾。

ctrl+C 强制终止当前的命令。

ctrl+L 清屏,相当于clear命令。

ctrl+U 删除或剪切光标之前的命令。我输入了一行很长的命令,不用使用退格键一个一个字符的删除,使用这个快捷键会更加方便

ctrl+K 删除或剪切光标之后的内容。

ctrl+Y 粘贴ctrl+U或ctul+K剪切的内容。

ctrl+R 在历史命令中搜索,按下ctrl+R之后,就会出现搜索界面,只要输入搜索内容,就会从历史命令中搜索。

ctrl+D 退出当前终端。

ctrl+Z 暂停,并放入后台。这个快捷键牵扯工作管理的内容,我们在系统管理章节详细介绍。

ctrl+S 暂停屏幕输出。

ctrl+Q 恢复屏幕输出。

输入输出重定向

linux 的标准输入与输出

设备

设备名

文件描述符

类型

键盘

/dev/stdin

0

标准输入

显示器

/dev/stdout

1

标准输出

显示器

/dev/stderr

2

标准错误输出


输入重定向:是指不使用系统提供的标准输入端口,而进行重新的指定。换言之,输入重定向就是不使用标准输入端口输入文件,而是使用指定的文件作为标准输入设备。(重定向简单理解就是使用 “<”符来修改标准输入设备)

类型

符号(语法)

功能

标准输入

命令<文件1

命令把文件1的内容作为标准输入设备

标识符限定输入

命令<<标识符

命令把标准输入中读入内容,直到遇到“标识符”分解符为止

输入输出重定向(同时使用)

命令< 文件1 >文件2

命令把文件1的内容作为标准输入,把文件2作为标准输出。

输出重定向

输出重定向:(通俗的讲,重定向输出就是把要输出的文件信息写入到一个文件中去,而不是将要输出的文件信息输出到控制台(显示屏),在linux中,默认的标准输出设备是控制台(或称为显示器),用户输出的信息默认情况下都会显示到控制台

&表示全部文件,文件不管对错,1表示标准输出文件,2表示标准错误输出。

类型

符号

作用

标住输出重定向

命令 > 文件

以覆盖方式,把命令的正确输出内容输出到指定的文件或设备当中

标住输出重定向

命令 >> 文件

以追加方式,把命令的正确输出内容输出到指定的文件或设备当中

标准错误输出重定向

错误命令2 > 文件

以覆盖方式,把命令的错误输出输出到指定的文件或设备当中

标准错误输出重定向

错误命令2 >> 文件

以追加方式,把命令的错误输出输出到指定的文件或设备当中

正确输出和错误输出同时保存

命令 > 文件 2>&1

以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中。

正确输出和错误输出同时保存

命令 >> 文件 2>&1

以追加的方式,把正确输出和错误输出都保存到同一个文件当中。

正确输出和错误输出同时保存

命令 &> 文件

以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中。

正确输出和错误输出同时保存

命令 &>> 文件

以追加的方式,把正确输出和错误输出都保存到同一个文件当中。

正确输出和错误输出同时保存

命令 >> 文件1 2>>文件2

把正确的输出追加到文件1中,把错误的输出追加到文件2中。

/dev/null 文件

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到**/dev/null**中

[root@localhost ~]$  command > dev/null
多命令顺序执行

多命令执行符

作用

格式

命令1 ;命令2

多个命令顺序执行,命令之间没有任何逻辑联系

&&

命令1 && 命令2


 

当命令1正确执行(? = 0 ) , 则 命 令 2 才 会 执 行 当 命 令 1 执 行 不 正 确 ( ?=0),则命令2才会执行 当命令1执行不正确(?=0),则命令2才会执行当命令1执行不正确(?≠0),则命令2不会执行

ll


 

命令1 || 命令2


 

当命令1执行不正确(? ≠ 0 ) , 则 命 令 2 才 会 执 行 当 命 令 1 正 确 执 行 ( ?≠0),则命令2才会执行当命令1正确执行(?

=0),则命令2才会执行当命令1正确执行(?=0),则命令2不会执行

shell脚本的执行
  1. [root@localhost ~]$ vim test.sh
  2. #!/bin/bash
  3. echo “hello world”


两种方式执行shell脚本

第一种:给文件增加执行权限

  1. [root@localhost ~]$ chmod u+x test.sh
  2. [root@localhost ~]$ ./test.sh #绝对路径或相对路径执行

第二种(了解):通过Bash调用执行脚本

[root@localhost ~]$ bash test.sh

三、shell变量

1. 变量的命名规则:

在定义变量时,有一些规则需要遵守:

1、命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。

2、等号左右两侧不能有空格,可以使用下划线“_”,变量的值如果有空格,需要使用单引号或双引号包括。如:“test=“hello world!””。其中双引号括起来的内容“$”,“(”和反引号都拥有特殊含义,而单引号括起来的内容都是普通字符。

3、不能使用标点符号,不能使用bash里的关键字(可用help命令查看保留关键字)。

4、环境变量建议大写,便于区分

5、如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含"$变量名"或用${变量名}包含变量名。

  1. [root@localhost ~]$ test=123
  2. [root@localhost ~]$ test="$test"456
  3. [root@localhost ~]$ echo $test
  4. 123456
  5. #叠加变量test,变量值变成了123456
  6. [root@localhost ~]$ test=${test}789
  7. [root@localhost ~]$ echo $test
  8. 123456789
  9. #再叠加变量test,变量值编程了123456789

关于单双引号的问题:
双引号能够识别变量,双引号能够实现转义(类似于“\*”)
单引号是不能识别变量,只会原样输出,单引号是不能转义的

shell中特殊符号

单引号和双引号

  1. [root@localhost ~]$ name=sc
  2. #定义变量name 的值是sc(就是最正直的人,超哥我了!)
  3. [root@localhost ~]$ echo '$name'
  4. $name
  5. #如果输出时使用单引号,则$name原封不动的输出
  6. [root@localhost ~]$ echo "$name"
  7. sc
  8. #如果输出时使用双引号,则会输出变量name的值 sc
  9. [root@localhost ~]$ echo `date`
  10. 20181021日星期一18:16:33 CST
  11. #反引号括起来的命令会正常执行
  12. [root@localhost ~]$ echo '`date`'
  13. `date`
  14. #但是如果反引号命令被单引号括起来,那么这个命令不会执行,―date`会被当成普通字符输出
  15. [root@localhost ~]$ echo "`date'"
  16. 20181021日星期一18:14:21 CST
  17. #如果是双引号括起来,那么这个命令又会正常执行

反引号

  1. [root@localhost ~]$ echo ls
  2. ls
  3. #如果命令不用反引号包含,命令不会执行,而是直接输出
  4. [root@localhost ~]$ echo `ls`
  5. anaconda-ks.cfginstall.loginstall.log.syslog sh test testfile
  6. #只有用反引号包括命令,这个命令才会执行
  7. [root@localhost ~]$ echo $(date)
  8. 2018年10月21日星期一18:25:09 CST
  9. #使用$(命令)的方式也是可以的

2. 变量的分类:

1、用户自定义变量: 这种变量是最常见的变量,由用户自由定义变量名和变量的值。

2、环境变量: 这种变量中主要保存的是和系统操作环境相关的数据,比如当前登录用户,用户的家目录,命令的提示符等。不是太好理解吧,那么大家还记得在Windows中,同一台电脑可以有多个用户登录,而且每个用户都可以定义自己的桌面样式和分辨率,这些其实就是Windows的操作环境,可以当做是Windows的环境变量来理解。环境变量的变量名可以自由定义,但是一般对系统起作用的环境变量的变量名是系统预先设定好的。

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

4、预定义变量: 是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。

2.1 用户自定义变量:
2.1.1 变量定义
  1. [root@localhost ~]$ 2name="shen chao"
  2. -bash: 2name=shen chao: command not found
  3. #变量名不能用数字开头
  4. [root@localhost ~]$ name = "shenchao"
  5. -bash: name: command not found
  6. #等号左右两侧不能有空格
  7. [root@localhost ~]$ name=shen chao
  8. -bash: chao: command not found
  9. #变量的值如果有空格,必须用引号包含
2.1.2 变量调用
  1. [root@localhost ~]$ name="shen chao"
  2. #定义变量name
  3. [root@localhost ~]$ echo $name #调用变量使用 $变量名
  4. shen chao
  5. #输出变量name的值
2.1.3 变量查看
  1. [root@localhost ~]$ set [选项]
  2. 选项:
  3. -u:如果设定此选项,调用未声明变量时会报错(默认无任何提示)
  4. -x:如果设定此选项,在命令执行之前,会把命令先输出一次
  5. +<参数> :取消某个set曾启动的参数。
  6. [root@localhost ~]$ set
  7. BASH=/bin/bash
  8. …省略部分输出…
  9. name='shen chao'
  10. #直接使用set 命令,会查询系统中所有的变量,包含用户自定义变量和环境变量
  11. [root@localhost ~]$ set -u
  12. [root@localhost ~]$ echo $file
  13. -bash: file: unbound variable
  14. #当设置了-u选项后,如果调用没有设定的变量会有报错。默认是没有任何输出的。
  15. [root@localhost ~]$ set -x
  16. [root@localhost ~]$ ls
  17. +ls --color=auto
  18. anaconda-ks.cfginstall.loginstall.log.syslog sh tdir testtestfile
  19. #如果设定了-x选项,会在每个命令执行之前,先把命令输出一次
  20. [root@localhost ~]$ set +x
  21. #取消启动的x参数
2.1.4 变量删除
[root@localhost ~]$ unset 变量名
2.2 环境变量:
2.2.1 环境变量设置
  1. [root@localhost ~]$ export age="18"
  2. #使用export声明的变量即是环境变量
2.2.2 环境变量查询和删除

env命令和set命令的区别:
set命令可以查看所有变量,而env命令只能查看环境变量。

  1. [root@localhost ~]$ unset gender #删除环境变量gender
  2. [root@localhost ~]$ env | grep gender

2.2.3 系统默认环境变量

  1. [root@localhost ~]$ env
  2. HOSTNAME=localhost.localdomain #主机名
  3. SHELL=/bin/bash #当前的shell
  4. TERM=linux #终端环境
  5. HISTSIZE=1000 #历史命令条数
  6. SSH_CLIENT=192.168.4.1594824 22 #当前操作环境是用ssh连接的,这里记录客户端ip
  7. SSH_TTY=/dev/pts/1 #ssh连接的终端时pts/1
  8. USER=root #当前登录的用户
  9. ..........更多参数可以使用set和env命令查看.............
2.3 位置参数变量:

$1 是你给你写的shell脚本传的第一个参数,$2 是你给你写的shell脚本传的第二个参数…

  1. [root@localhost sh]$ vim test.sh
  2. #!/bin/sh
  3. echo "shell脚本本身的名字: $0"
  4. echo "传给shell的第一个参数: $1"
  5. echo "传给shell的第二个参数: $2"

保存退出后,你在Test.sh所在的目录下输入 bash Test.sh 1 2

结果输出:

  1. shell脚本本身的名字: Test.sh
  2. 传给shell的第一个参数: 1
  3. 传给shell的第二个参数: 2

$*会把接收的所有参数当成一个整体对待,而$@则会区分对待接收到的所有参数。举个例子:

  1. [root@localhost sh]$ vi parameter2.sh
  2. #!/bin/bash
  3. for i in"$*"
  4. #定义for循环,in后面有几个值,for会循环多少次,注意“$*”要用双引号括起来
  5. #每次循环会把in后面的值赋予变量i
  6. #Shell把$*中的所有参数看成是一个整体,所以这个for循环只会循环一次
  7. do
  8. echo "The parameters is: $i"
  9. #打印变量$i的值
  10. done
  11. x=1
  12. #定义变量x的值为1
  13. for y in"$@"
  14. #同样in后面的有几个值,for循环几次,每次都把值赋予变量y
  15. #可是Shel1中把“$@”中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次
  16. do
  17. echo "The parameter$x is: $y"
  18. #输出变量y的值
  19. x=$(( $x +1 ))
  20. #然变量x每次循环都加1,为了输出时看的更清楚
  21. done
2.4 预定义变量:

先来看看”$?”这个变量,举个例子说明

  1. [root@localhost sh]$ ls
  2. count.sh hello.sh parameter2.sh parameter.sh
  3. #ls命令正确执行
  4. [root@localhost sh]$ echo $?
  5. #预定义变量“$?”的值是0,证明上一个命令执行正确
  6. [root@localhost sh]$ ls install.log
  7. ls:无法访问install.log:没有那个文件或目录
  8. #当前目录中没有install.log文件,所以ls命令报错了
  9. [root@localhost sh]$ echo $?
  10. 2
  11. #变量“$?”返回一个非О的值,证明上一个命令没有正确执行
  12. #至于错误的返回值到底是多少,是在编写ls命令时定义好的,如果碰到文件不存在就返回数值2

再来说明下”$$”和”$!”这两个预定义变量

  1. [root@localhost sh]$ vi variable.sh
  2. #!/bin/bash
  3. echo "The current process is $$"
  4. #输出当前进程的PID.
  5. #这个PID就是variable.sh这个脚本执行时,生成的进程的PID
  6. find /root -name hello.sh &
  7. #使用find命令在root目录下查找hello.sh文件
  8. #符号&的意思是把命令放入后台执行,工作管理我们在系统管理章节会详细介绍
  9. echo "The last one Daemon process is $!"
  10. #输出这个后台执行命令的进程的PID,也就是输出find命令的PID号
3. 只读变量:
  1. [root@localhost sh]$ vi readonly.sh
  2. #!/bin/bash
  3. a=10
  4. #语法:readonly 变量名
  5. readonly a
  6. a=20 #会报错readonly variable
  7. echo $a
4. 接受键盘输入:
  1. [root@localhost ~]$ read [选项][变量名]
  2. 选项:
  3. -a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
  4. -p: “提示信息”:在等待read输入时,输出提示信息
  5. -t: 秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间
  6. -n: 数字:read命令只接受指定的字符数,就会执行
  7. -s: 隐藏输入的数据,适用于机密信息的输入
  8. -d: 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
  9. -e: 在输入的时候可以使用命令补全功能。
  10. 变量名:
  11. 变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY.
  12. 如果只提供了一个变量名,则整个输入行赋予该变量.
  13. 如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字

写个例子来解释下read命令:

  1. [root@localhost sh]$ vi read.sh
  2. #!/bin/bash
  3. read -t 30 -p "Please input your name: " name
  4. #提示“请输入姓名”并等待30 秒,把用户的输入保存入变量name 中
  5. echo "Name is $name"
  6. #看看变量“$name”中是否保存了你的输入
  7. read -s -t 30 -p "Please enter your age: " age
  8. #提示“请输入年龄”并等待30秒,把用户的输入保存入变量age中
  9. #年龄是隐私,所以我们用“-s”选项隐藏输入
  10. echo -e "\n"
  11. #调整输出格式,如果不输出换行,一会的年龄输出不会换行
  12. echo "Age is $age"
  13. read -n 1 -t 30 -p "Please select your gender[M/F]:" gender
  14. #提示“请选择性别”并等待30秒,把用户的输入保存入变量gender
  15. #使用“-n1”选项只接收一个输入字符就会执行(都不用输入回车)
  16. echo -e "\n"
  17. echo "Sex is $gender"


四、shell 运算符

在shell中,运算符和其他编程脚本语言一样,常见的有算数运算符、关系运算符、逻辑运算符、字符串运算符、文件测试运算符等

1. 算数运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
例如,两个数相加(注意使用的是反引号 ` 而不是单引号 '):

  1. [root@localhost ~]$ vi computer.sh
  2. #!/bin/bash
  3. val=`expr 2 + 2`
  4. echo "两数之和为 : $val"
  5. #注意
  6. #表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
  7. #完整的表达式要被 ` ` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。

下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20

|加法 |expr $a + $b 结果为 30。

|减法 |expr $a - $b 结果为 -10。

*| 乘法 |expr $a \* $b 结果为 200。

/ |除法 |expr $b / $a 结果为 2。

% |取余| expr $b % $a 结果为 0。

= |赋值| a=$b 将把变量 b 的值赋给 a。

==| 相等。用于比较两个数字,相同则返回 true(真)。| [ $a == $b ] 返回 false(假)。

!= |不相等。用于比较两个数字,不相同则返回 true。 |[ $a != $b ] 返回 true。

注意:条件表达式要放在方括号之间,并且要有空格,必须写成 [ $a == $b ]。

  1. [root@localhost ~]$ vi computers.sh
  2. #!/bin/bash
  3. a=10
  4. b=20
  5. echo ' '
  6. echo 'a+b= ' `expr $a + $b`
  7. echo 'a-b= ' `expr $a - $b`
  8. echo 'a*b= ' `expr $a \* $b`
  9. echo 'a/b= ' `expr $a / $b`
  10. echo 'a%b= ' `expr $a % $b`
  11. #判断是否相等
  12. if [ $a == $b ]
  13. then
  14. echo 'a等于b'
  15. else
  16. echo 'a不等于b'
  17. fi
2. 关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

  1. [root@localhost ~]$ [ 10 -gt 10 ]
  2. [root@localhost ~]$ echo $?
  3. 1
  4. [root@localhost ~]$ [ 10 -eq 10 ]
  5. [root@localhost ~]$ echo $?
  6. 0

案例:判断当前输入的用户是否存在。如果存在则提示“用户存在”否则提示“用户不存在”。

如果要在shell脚本使用linux命令,可以使用$()包裹命令
例如:disk_size=$(df -h | awk ‘NR==2 {print $5}’)

  1. [root@localhost ~]$ vim demo.sh
  2. #!/bin/bash
  3. #接受用户的输入
  4. read -p '请输入需要查询的用户名:' username
  5. #获取指定用户名在passwd文件中出现的次数
  6. count=$(cat /etc/passwd | grep $username | wc -l)
  7. #count=`cat /etc/passwd | grep $username | wc -l`
  8. #判断出现的次数,如果次数=0则用户不存在,反之存在
  9. if [ $count == 0 ]
  10. then
  11. echo '用户不存在'
  12. else
  13. echo '用户存在'
  14. fi
3. 逻辑运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

或运算:一个为真即为真,全部为假才是假
与运算:一个为假即为假,全部为真才是真

4. 字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:

5. 文件测试运算符(重点)

文件测试运算符用于检测 Unix/Linux 文件的各种属性。

注意:权限几个判断,如果只有一个部分符合,则认为是有权限的。

五、小案例

1、小案例:

要求:目录下有两个文件,要求根据用户选择的不同,执行不用shell脚本取安装不用的软件

1、首先对脚本的目录进行判断是否存在

2、条件判断->优先处理错误的情况,因为错误的情况比较简单

3、显示用户可以选择的条件

4、read 去读取用户输入的数字

5、根据相加的结果,来判断输入的值是否正确,不正确的情况给他丢进黑洞中,不打印

6、 输入对数字,是1,2,3进行相应对判断

7、限制用户必须输入的是1,2,3

重定向创建两个目录

  1. [root@localhost xixi]# echo "echo LAMP is installed..." > ./lamp.sh
  2. [root@localhost xixi]# echo "echo LNMP is installed..." > ./lnmp.sh

  1. #! /bin/bash
  2. #对脚本对目录进行判断是否存在
  3. path=/mnt/shell/test_scripts
  4. #条件判断
  5. #开发脚本,真,和假2个情况,优先处理错误对情况,因为错误对情况比较简单
  6. [ ! -d "$path" ] && mkdir $path -p
  7. #开发该脚本的正常的逻辑
  8. cat <<END
  9. 1.[install lamp]
  10. 2.[install lnmp]
  11. 3.[exit]
  12. pls input the num you want;
  13. END
  14. read num
  15. #根据num的变量进行逻辑的处理
  16. #根据相加的结果,来判断输入的值是否正确,不正确的情况给他丢进黑洞中,不打印
  17. expr $num + 1 &> /dev/null
  18. [ $? -ne 0 ] && {
  19. echo "you input num is must be {1|2|3}"
  20. exit 1
  21. }
  22. # 输入对数字,是123进行相应对判断
  23. [ "$num" -eq "1" ] && {
  24. echo "Statring installing lamp ...."
  25. sleep 2;
  26. # 执行lamp.sh安装脚本了
  27. # 对文件权限判断
  28. [ -x "$path/lamp.sh" ] || {
  29. ehco "The file does not exit or can't be exec"
  30. exit 1
  31. }
  32. $path/lamp.sh
  33. exit $?
  34. }
  35. #选择2的情况,安装lnmp
  36. [ "$num" -eq "2" ] && {
  37. echo "Stating installing lnmp ..."
  38. sleep 2;
  39. #执行lnmp.sh安装脚本
  40. #对权限判断
  41. [ -x "$path/lnmp.sh" ] || {
  42. echo "The file is not exit or exec "
  43. exit 1
  44. }
  45. $path/lnmp.sh
  46. exit $?
  47. }
  48. #退出
  49. [ $num -eq "3" ] && {
  50. echo "用户选择了退出文件不执行"
  51. exit 3
  52. }
  53. # 限制用户必须输入的是123
  54. #[[]] 支持正则表达式 [[ $num =~ [1 - 3] ]]
  55. # 取反
  56. [[ ! "$num" =~ [1-3] ]] && {
  57. echo "The num you input must be {1|2|3}"
  58. echo "你这个大笨蛋,必须按照说明来写"
  59. exit 4
  60. }


六、流程控制

1. if条件判断
1.1 单分支if条件

语法:

  1. if [ 条件判断式 ]
  2. then
  3. 程序
  4. fi

案例:统计根分区使用率

  1. [root@localhost ~]$ vi sh/if1.sh
  2. #!/bin/bash
  3. #统计根分区使用率
  4. rate=$(df -h | grep "/dev/sda2" | awk '{print $5}’| cut -d "%"-f1)
  5. #把根分区使用率作为变量值赋予变量rate
  6. if [ $rate -ge 80 ]
  7. #判断rate的值如果大于等于80,则执行then程序
  8. then
  9. echo "Warning!/dev/sda3 is fu11!!"
  10. #打印警告信息。在实际工作中,也可以向管理员发送邮件。
  11. fi

案例:创建目录

  1. [root@localhost ~]$ vi sh/add_dir.sh
  2. #!/bin/bash
  3. #创建目录,判断是否存在,存在就结束,反之创建
  4. echo "当前脚本名称为$0"
  5. DIR="/media/cdrom"
  6. if [ ! -e $DIR ]
  7. then
  8. mkdir -p $DIR
  9. fi
  10. echo "$DIR 创建成功"
1.2 双分支if条件语句

语法:

  1. if [ 条件判断式 ]
  2. then
  3. 条件成立时,执行的程序
  4. else
  5. 条件不成立时,执行的另一个程序
  6. fi
  1. [root@localhost ~]$ vi sh/bakmysql.sh
  2. #!/bin/bash
  3. #备份mysql数据库。
  4. ntpdate asia.pool.ntp.org &>/dev/null
  5. #同步系统时间
  6. date=$(date +%y%m%d)
  7. #把当前系统时间按照“年月日”格式赋子变量date
  8. size=$(du -sh/var/lib/mysql)
  9. #统计mysql数据库的大小,并把大小赋予size变量
  10. if [ -d /tmp/dbbak ]
  11. #判断备份目录是否存在,是否为目录
  12. then
  13. #如果判断为真,执行以下脚本
  14. echo "Date : $date!" > /tmp/dbbak/dbinfo.txt
  15. #把当前日期写入临时文件
  16. echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt
  17. #把数据库大小写入临时文件
  18. cd/tmp/dbbak
  19. #进入备份目录
  20. tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &> /dev/null
  21. #打包压缩数据库与临时文件,把所有输出丢入垃圾箱(不想看到任何输出)
  22. rm -rf /tmp/dbbak/dbinfo.txt
  23. #删除临时文件
  24. else
  25. mkdir /tmp/dbbak
  26. #如果判断为假,则建立备份目录
  27. echo "Date : $date!" > /tmp/dbbak/dbinfo.txt
  28. echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt
  29. #把日期和数据库大小保存如临时文件
  30. cd /tmp/dbbak
  31. tar -zcf mysql-lib-$date.tar. gz dbinfo.txt /var/lib/mysql &> /dev/null
  32. #压缩备份数据库与临时文件
  33. rm -rf/tmp/dbbak/dbinfo.txt
  34. #删除临时文件
  35. fi

案例2:判断apache是否启动,如果没有启动则自动启动

  1. [root@localhost ~]$ vi sh/autostart.sh
  2. #!/bin/bash
  3. #判断apache是否启动,如果没有启动则自动启动
  4. port=$(nmap -sT 192.168.4.210 | grep tcp | grep http | awk '{print $2}’)
  5. #使用nmap命令扫描服务器,并截取 apache服务的状态,赋予变量port
  6. #只要状态是open,就证明正常启动
  7. if [ "$port" == "open"]
  8. #如果变量port的值是“open”
  9. then
  10. echo "$(date) httpd is ok!” >> /tmp/autostart-acc.log
  11. #则证明apache 正常启动,在正常日志中写入一句话即可
  12. else
  13. /etc/rc.d/init.d/httpd start &>/dev/null
  14. #否则证明apache没有启动,自动启动apache
  15. echo "$(date) restart httpd !!" >> /tmp/autostart-err.log
  16. #并在错误日志中记录自动启动apche 的时间
  17. fi

nmap端口扫描命令,格式如下:

  1. [root@localhost ~]$ nmap -sT 域名或IP
  2. 选项:
  3. -s 扫描
  4. -T 扫描所有开启的TCP端口
  5. #知道了nmap命令的用法,我们在脚本中使用的命令就是为了截取http的状态,只要状态是“or.
  6. #就证明apache启动正常,否则证明apache启动错误。来看看脚本中命令的结果:
  7. [root@localhost ~]$ nmap -sT 192.168.4.210 | grep tcp | grep http | awk ' fprint $2}’
  8. #扫描指定计算机,提取包含tcp 的行,在提取包含httpd 的行,截取第二列open
  9. #把截取的值赋予变量port
1.3 多分支if条件语句

语法:

  1. if [ 条件判断式1 ]
  2. then
  3. 当条件判断式1成立时,执行程序1
  4. elif [ 条件判断式2 ]
  5. then
  6. 当条件判断式2成立时,执行程序2
  7. …省略更多条件…
  8. else
  9. 当所有条件都不成立时,最后执行此程序
  10. fi

案例:判断用户输入的是什么文件

  1. [root@localhost ~]$ vi sh/if-elif.sh
  2. #!/bin/bash
  3. #判断用户输入的是什么文件
  4. read -p "Please input a filename: " file
  5. #接收键盘的输入,并赋予变量file
  6. if [ -z "$file” ]
  7. #判断file变量是否为空
  8. then
  9. echo "Error, please input a filename"
  10. #如果为空,执行程序1,也就是输出报错信息
  11. exit 1
  12. #退出程序,并返回值为Ⅰ(把返回值赋予变量$P)
  13. elif [ ! -e "$file” ]
  14. #判断file的值是否存在
  15. then
  16. echo "Your input is not a file!"
  17. #如1果不存在,则执行程序2
  18. exit 2
  19. #退出程序,把并定义返回值为2
  20. elif [ -f "$file” ]
  21. #判断file的值是否为普通文件
  22. then
  23. echo "$file is a regulare file!”
  24. #如果是普通文件,则执行程序3
  25. elif [ -d "$file” ]
  26. #到断file的值是否为目录文件
  27. then
  28. echo "$file is a directory!"
  29. #如果是目录文件,网执行程序4
  30. else
  31. echo "$file is an other file!”
  32. #如果以上判断都不是,则执行程序5
  33. fi
2. 多分支case条件语句

case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。

case语句语法如下:

  1. case $变量名 in
  2. "值1")
  3. 如果变量的值等于值1,则执行程序1
  4. ;;
  5. "值2")
  6. 如果变量的值等于值2,则执行程序2
  7. ::
  8. …省略其他分支…
  9. *)
  10. 如果变量的值都不是以上的值,则执行此程序
  11. ;;
  12. esac

这个语句需要注意以下内容:

case语句,会取出变量中的值,然后与语句体中的值逐一比较。如果数值符合,则执行对应的程序,如果数值不符,则依次比较下一个值。如果所有的值都不符合,则执行 “*)” (*代表所有其他值)中的程序。

case语句以“case”开头,以“esac”结尾。

每一个分支程序之后要通过“;;”双分号结尾,代表该程序段结束(千万不要忘记,每次写case语句,都不要忘记双分号)。

  1. [root@localhost ~]$ vi sh/if-case.sh
  2. #!/bin/bash
  3. read -p "请输入一个字符,并按Enter确认:" KEY
  4. case "$KEY" in
  5. [a-z]|[A-Z])
  6. echo "您输入的是字母"
  7. ;;
  8. [0-9])
  9. echo "您输入的是数字"
  10. ;;
  11. *)
  12. echo "您输入的是其他字符"
  13. ;;
  14. esac
  1. #! /bin/bash
  2. read -p "请输入一个字符,并按Enter确认:" key
  3. case "$key" in
  4. "1")
  5. echo "您输入的数字是1"
  6. ;;
  7. "2")
  8. echo "您输入的数字是2"
  9. ;;
  10. *)
  11. echo "你输入的数字比较大"
  12. ;;
  13. esac
3. for循环

for循环是固定循环,也就是在循环时已经知道需要进行几次的循环,有时也把for循环称为计数循环。for的语法有如下两种:

语法一:

  1. for 变量 in123 …(可以是一个文件等)
  2. do
  3. 程序
  4. done
  5. 这种语法中for循环的次数,取决于in后面值的个数(空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。
  6. 也就是说,假设in后面有三个值,for会循环三次,第一次循环会把值1赋予变量,第二次循环会把值2赋予变量,以此类推。

语法二:

  1. for (( 初始值;循环控制条件;变量变化 ))
  2. do
  3. 程序
  4. done
  5. 语法二中需要注意:
  6. 初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;
  7. 循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;
  8. 变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1

语法一举例:打印时间

  1. [root@localhost ~]$ vi sh/for.sh
  2. #!/bin/bash
  3. #打印时间
  4. for time in morning noon afternoon evening
  5. do
  6. echo "This time is $time!"
  7. done

语法一举例:批量解压缩脚本

  1. [root@localhost ~]$ vi sh/auto-tar. sh
  2. #!/bin/bash
  3. #批量解压缩脚本
  4. cd/lamp
  5. #进入压缩包目录
  6. ls *.tar.gz > ls.log
  7. #把所有.tar.gz结尾的文件的文件覆盖到ls.log 临时文件中
  8. for i in $(cat ls.log) `
  9. #或者这样写for i in `cat ls.log`
  10. #读取ls.log文件的内容,文件中有多少个值,就会循环多少次,每次循环把文件名赋予变量i
  11. do
  12. tar -zxf $i &>/dev/null
  13. #加压缩,并把所有输出都丢弃
  14. done
  15. rm -rf /lamp/ls.log
  16. #删除临时文件ls.log

语法二举例:从1加到100

  1. [root@localhost ~]$ vi sh/add. sh
  2. #!/bin/bash
  3. #从1加到100
  4. s=0
  5. for (( i=1;i<=100;i=i+1 ))
  6. #定义循环100
  7. do
  8. s=$(( $s+$i ))
  9. #每次循环给变量s赋值
  10. done
  11. echo "The sum of 1+2+...+100 is : $s"
  12. #输出1加到100的和

语法二举例:批量添加指定数量的用户

  1. [root@localhost ~]$ vi useradd.sh
  2. #!/bin/bash
  3. #批量添加指定数量的用户
  4. read -p "Please input user name: " -t 30 name
  5. #让用户输入用户名,把输入保存入变量name
  6. read -p "Please input the number of users: " -t 30 num
  7. #让用户输入添加用户的数量,把输入保存入变量num
  8. read -p "Please input the password of users: " -t 30 pass
  9. #让用户输入初始密码,把输入保存如变量pass
  10. if [ ! -z "$name" -a ! -z "$num"-a ! -z "$pass"]
  11. #判断三个变量不为空
  12. then
  13. y=$(echo $num | sed 's/[0-9]//g')
  14. #定义变量的值为后续命令的结果
  15. #后续命令作用是,把变量num 的值替换为空。如果能替换为空,证明num 的值为数字
  16. #如果不能替换为空,证明num的值为非数字。我们使用这种方法判断变量num 的值为数字
  17. if [ -z "$y"]
  18. #如果变量y的值为空,证明num变量是数字
  19. then
  20. for (( i=1 ; i<=$num; i=i+1 ))
  21. #循环num变量指定的次数
  22. do
  23. /usr/sbin/useradd $name$i &>/dev/null
  24. #添加用户,用户名为变量name 的值加变量i的数字
  25. echo $pass | /usr/bin/passwd --stdin $name$i &>/dev/null
  26. #给用户设定初始密码为变量pass 的值
  27. done
  28. fi
  29. fi

语法二举例:批量删除用户

  1. [root@localhost ~]$ vi sh/userdel.sh
  2. #!/bin/bash
  3. #批量删除用户
  4. user=$(cat /etc/passwd | grep " /bin/bash"|grep -v "root"Icut -d ":" -f 1)
  5. #读取用户信息文件,提取可以登录用户,取消root用户,截取第一列用户名
  6. for i in $user
  7. #循环,有多少个普通用户,循环多少次
  8. do
  9. userdel -r $i
  10. #每次循环,删除指定普通用户
  11. done
4. while循环

对while循环来讲,只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。

语法:

  1. while [ 条件判断式 ]
  2. do
  3. 程序
  4. done

案例:1加到100

  1. [root@localhost ~]$ vi sh/addnum.sh
  2. #!/bin/bash
  3. #从1加到100
  4. i=1
  5. s=0
  6. #给变量i和变量s赋值
  7. while [ $i -le 100 ]
  8. #如果变量i的值小于等于100,则执行循环
  9. do
  10. s=$(( $s+$i ))
  11. i=$(( $i+1 ))
  12. done
  13. echo "The sum is: $s"
5. until循环

和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。

语法:

  1. until [ 条件判断式 ]
  2. do
  3. 程序
  4. done

案例一:1加到100

  1. [root@localhost ~]$ vi sh/until.sh
  2. #!/bin/bash
  3. #从1加到100
  4. i=1
  5. s=0
  6. #t给变量i和变量s赋值
  7. until [ $i -gt 100 ]
  8. #循环直到变量i的值大于100,就停止循环
  9. do
  10. s=$(( $s+$i ))
  11. i=$(( $i+1 ))
  12. done
  13. echo "The sum is: $s"
6. 函数

语法:

  1. function 函数名 () {
  2. 程序
  3. }

案例:接收用户输入的数字,然后从1加到这个数字

  1. [root@localhost ~]$ vi sh/function.sh
  2. #!/bin/bash
  3. #接收用户输入的数字,然后从1加到这个数字
  4. function sum () {
  5. #定义函数sum
  6. s=0
  7. for (( i=0; i<=$num;i=i+1 ))
  8. #循环直到i大于$1为止。$1是函数sum 的第一个参数
  9. #在函数中也可以使用位置参数变量,不过这里的$1指的是函数的第一个参数
  10. do
  11. s=$(( $i+$s ))
  12. done
  13. echo "The sum of 1+2+3...+$1 is :$s"
  14. #输出1加到$1的和
  15. }
  16. read -p "Please input a number: " -t 30 num
  17. #接收用户输入的数字,并把值赋予变量num
  18. y=$(echo $num | sed 's/[0-9]//g')
  19. #把变量num的值替换为空,并赋予变量y
  20. if [ -z "$y"]
  21. #判断变量y是否为空,以确定变量num中是否为数字
  22. then
  23. sum $num
  24. #调用sum函数,并把变量num的值作为第一个参数传递给sum函数
  25. else
  26. echo "Error!! Please input a number!"
  27. #如果变量num 的值不是数字,则输出报错信息
  28. fi
7. 特殊流程控制语句
7.1 exit语句

系统是有exit命令的,用于退出当前用户的登录状态。可是在Shell脚本中,exit语句是用来退出当前脚本的。也就是说,在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本

exit的语法如下:

exit [返回值]


如果exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过查询$?这个变量,来查看返回值。如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit 语句之前,最后执行的一条命令的返回值。写一个exit 的例子:

  1. [root@localhost ~]$ vi sh/exit.sh
  2. #!/bin/bash
  3. #演示exit的作用
  4. read -p "Please input a number: " -t 30 num
  5. #接收用户的输入,并把输入赋予变量num
  6. y=$ (echo $num | sed 's/[0-9]//g')
  7. #如果变量num 的值是数字,则把num的值替换为空,否则不替换
  8. #把替换之后的值赋予变量y
  9. [ -n "$y" ] && echo "Error! Please input a number!" && exit 18
  10. #判断变量y的值如果不为空,输出报错信息,退出脚本,退出返回值为18
  11. echo "The number is: $num"
  12. #如果没有退出加班,则打印变量num中的数字
7.2 break语句

当程序执行到break语句时,会结束整个当前循环。而continue 语句也是结束循环的语句,不过continue 语句单次当前循环,而下次循环会继续。

案例:

  1. [root@localhost ~]$ vi sh/break.sh
  2. #!/bin/bash
  3. #演示break 跳出循环
  4. for (( i=1;i<=10; i=i+1 ))
  5. #循环十次
  6. do
  7. if ["$i" -eq 4 ]
  8. #如果变量i的值等于4
  9. then
  10. break
  11. #退出整个循环
  12. fi
  13. echo $i
  14. #输出变量i的值
  15. done

执行下这个脚本,因为一旦变量i的值等于4,整个循环都会跳出,所以应该只能循环三次:

  1. [root@localhost ~]$ chmod 755 sh/break.sh
  2. [root@localhost ~]#sh/break.sh
  3. 1
  4. 2
  5. 3
7.3 continue语句

continue也是结束流程控制的语句。如果在循环中,continue语句只会结束单次当前循环。

案例:

  1. [root@localhost ~]$ vi sh/break.sh
  2. #!/bin/bash
  3. #演示continue
  4. for (( i=1;i<=10;i=i+1 ))
  5. #循环十次
  6. do
  7. if ["$i" -eq 4 ]
  8. #如果变量i的值等于4
  9. then
  10. continue
  11. #退出换成continue
  12. fi
  13. echo $i
  14. #输出变量i的值
  15. done

执行下这个脚本:

  1. [root@localhost ~]$ chmod 755 sh/continue.sh
  2. [root@localhost ~]#sh/break.sh
  3. 1
  4. 2
  5. 3
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. 10
  12. #少了4这个输出
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/216028
推荐阅读
相关标签
  

闽ICP备14008679号