1.shell编程简介

1.1什么是shell编程

  • shell命令解释器
    • 红帽系列:bash
    • Centos/Ubuntu20.04都是bash解释器,Ubuntu之前的命令解释器是dash
    • 不同的命令解释器语法格式是有区别的

1.2 开发语言中程序代码的分类

  • 编译型:写完后需要编译才能运行,编译完才能运行
  • 解释型:书写完成通过命令解释器运行,写完就能运行

1.3书写与执行脚本习惯

1.书写脚本

  1. #!/bin/bash #必须以该命令开头,代表你使用的命令解释器
  2. #当然也有其他的命令解释器
  3. #!/usr/bin/python
  4. #!/bin/sed
  5. #!/bin/awk

当在文本中添加#!/bin/bash,显示的文件类型:
image.png

2.执行脚本

方法1:直接运行,需要添加x权限(用于系统执行脚本)

  1. chmod +x test.sh #设置权限
  2. ./test.sh #运行脚本 通过当前路径
  3. /root/home/test.sh #运行脚本 通过绝对路径

方法2:使用sh运行

  1. #不用给权限,使用命令直接运行
  2. bash test.sh #运行脚本

方法3:source或 . 替你在当前环境中 执行1次脚本

source或 . 应用场景:

  • 让环境变量生效 source /etc/profile
  • 类似include功能:把放在其他位置的配置文件包含进主配置文件中
  1. source /etc/profile #刷新配置文件,其实该文件就是shell脚本
  2. . test.sh #运行脚本
  3. vim test1.sh #创建脚本,写入以下内容
  4. #!/bin/bash
  5. source /etc/init.d/functions #这里就类似将functions中的代码拉入到该脚本中,然后就可以使用了
  6. action "web is ok" /bin/true #是因为source了funtions才能使用action

image.png

1.4 Shell的编程习惯

  • 书写脚本加上第一行的命令解释器
  • 版权说明:作者,创建时间,作用,版本号
  • 不要添加中文,容易出问题
  • 成对的符号() {} [] '' ""一定要提前写好
  • 脚本文件名尽量不要包含文件服务名称,并且要一眼就能看出来脚本作用

    1.5 案例编写流程

  1. 找出操作步骤
  2. 找出对应的命令
  3. 测试命令
  4. 最后书写脚本

1.6 Shell中的变量

1.变量

用一个固定的字符串(英文),替代更多复杂的内容。可以用来放数值,时间等。当你写一个变量就在内存中划分一块区域,你可以使用变量名来调用其中的内容。

2.变量命名规则

  • 不能以数字开头
  • 要做到看见变量名就知道变量的作用
  • 建议使用’_’来连接字符liao_mysql_isntall.sh
  • 使用驼峰命令法liaoInstallLnmp.sh

    3.变量分类

  • 环境变量(全局变量) 常是系统创建,也可以自己创建 global var

  • 普通变量(局部变量)
  • 特殊变量 匹配脚本 参数服务装填 特殊替换

4.环境变量

  1. env #查看环境变量
  2. declare
  3. export #三条命令都是查看环境变量
  4. /etc/profile #在其中直接添加
  5. unset #删除环境变量
  • 常见环境变量 | 环境变量 | 含义 | Shell中的用法 | | —- | —- | —- | | LANG language | 记录系统字符集 语言 |
    | | PS1 | 命令行格式 [\u@\h \W]\$ |
    | | PATH | 环境变量,执行命令/系统会在PATH中查找 |
    | | UID | 记录用户的UID信息 | 脚本中判断用户是否是root | | HOSTNAME | 主机名 |
    | | 历史命令相关 |
    |
    | | HISTSIZE | history命令记录最多条数 | 在生产环境设置的小一些 | | HISTFILESIZE | history文件记录最多条数 |
    | | HISTFILE | 指定历史记录文件的位置 | /root/.bash_history | | TMOUT | 设置离开多久不操作自动断开时间 |
    | | HISTCONTROL | 控制history命令是否记录以空格开头的命令 | HISTCONTROL=ignoredups | | PROMPT_COMMAND | 存放的命令/脚本会在下一个命令执行前运行 |
    |

5.普通变量

  1. week=3
  2. echo $week
  3. 3
  4. echo $weekday
  5. echo $week.day
  6. 3.day
  7. echo $week_day
  8. echo ${week}_day
  9. 3_day

6.环境变量有关的文件

  1. [root@localhost ~]# ll -d /etc/profile /etc/bashrc ~/.bashrc ~/.bash_profile /etc/profile.d/
  2. -rw-r--r--. 1 root root 2853 4 1 2020 /etc/bashrc
  3. -rw-r--r--. 1 root root 1819 4 1 2020 /etc/profile
  4. drwxr-xr-x. 2 root root 280 2 15 11:12 /etc/profile.d/
  5. -rw-r--r--. 1 root root 176 12 29 2013 /root/.bash_profile
  6. -rw-r--r--. 1 root root 176 12 29 2013 /root/.bashrc
文件名称 存放内容 生效范围
/etc/profile 存放环境变量 别名 函数 全局范围
/etc/bashrc 官方说法中存放别名 函数 全局范围
~/.bashrc 当前用户的别名 局部生效
~/.bashrc_profile 当前用户的环境变量 局部生效
/etc/profile.d/ 用户登录系统后,执行这个目录下以.sh结尾的脚本 自己写个跳板机,跳板机脚本放这里

2.特殊变量

应用场景:

  • 提高书写脚本执行效率
  • 可以更加简单方便判断服务状态,文件目录状态,进程状态。
符号 含义 应用场景
$0 脚本的名称 脚本执行错误给出错误提示或者使用帮助
$n(数字) 脚本的第n个参数 命令中参数传递给脚本,在脚本中使用
$# 脚本参数的个数,一共有多少个参数 脚本开头 判断参数个数是否正确
$* 取出所有的参数 加上双引号,相当于是1个整体1个参数
$@ 取出所有的参数 加上双引号 就是每个都是独立的

1.脚本名字 $0

常在脚本出错或者提供帮助时使用

  1. vim 01_test.sh
  2. #!/bin/bash
  3. echo "使用方法: $0 {start/stop/reload}"
  4. sh 01_test.sh
  5. echo "使用方法: 01_test.sh {start/stop/reload}"

image.png

2.位置参数 $n

命令行传参,启动脚本时后面跟着的参数

  • 特殊情况:
    • $n >= 10 时会出现错误,需要使用${10} ${11}。
    • 并且$1test 是可用的 $11会被判定成2个$1 ```bash vim 02_test.sh

!/bin/bash

echo $1 $2 $3 $4

sh 02_test.sh 10 100 1000 10000 10 100 1000 10000

  1. <a name="cXtes"></a>
  2. ## 3.脚本参数个数 $#
  3. 脚本参数的个数
  4. ```bash
  5. vim 03_test.sh
  6. #!/bin/bash
  7. if [ $# -eq 0 ]; then
  8. echo "Usage:$0 [stop|start|reload]"
  9. sh 03_test.sh
  10. echo "Usage:$0 [stop|start|reload]"
  11. sh 03_test.sh

image.png

4.取出脚本参数 $* $@

符号 含义 区别
$* 取出所有参数 加了双引号所有的参数是 一个整体
$@ 取出所有参数 加了双引号所有的参数是 独立的

3.特殊变量-返回值

符号 含义 常用/用法
$? 上一条命令的返回值
为0时正常
非0失败
判断各种东西是否执行,服务是否成功,用来配置判断
$$ 当前运行脚本的pid 在脚本中把pid记录到文件 方便后面进行kill
$! 上一个脚本的的pid 在脚本中把pid记录到文件 方便后面进行kill
$_ 上一个命令或脚本的最后一个参数 _环境变量

1.查看命令的返回值 $?

上一条命令的返回值,0为正常,非0有错误

案例
检查域名是否可以ping通,通了就显示正常
2>&1:将错误和标准输出都输入到文件

  1. #ping 百度是否可通信
  2. ping -c4 www.baidu.com
  3. #判断命令执行情况
  4. #!通过if判断$?是否成功
  5. #优化脚本
  6. #!此时的脚本会显示ping的情况,我们将内容重定向
  7. #!/bin/bash
  8. ping -c5 www.baidu.com > /dev/null 2>&1 #如果脚本没有任何提示,则执行成功
  9. if [ $? -eq 0 ]; then
  10. echo "baidu.com is ok"
  11. fi

2.当前脚本的pid $$

应用场景:
脚本运行的时候,生成一个pid文件,方便以后kill

  1. vim 09_pid.sh
  2. #!/bin/bash
  3. echo $$ > /var/run/first.pid #查看当前脚本pid
  4. sleep 9999
  5. sh 09_pid.sh & #后台执行

image.png
killcat /var/run/first.log``

3.上一个运行的脚本的pid $!

  1. sh 09_pid.sh &
  2. [1] 3153
  3. echo $!
  4. 3153

image.png

4.上一个命令或者脚本的最后一个参数 $_

相当于按了 esc + .

image.png

案例

制作一个ping命令脚本,能够确定网站是否通信,并且显示域名与成功与否

  1. #!/bin/bash
  2. url=$1
  3. ping -c5 -W0.5 -i0.1 $url > /dev/null 2>&1
  4. if [ $? -eq 0 ]; then
  5. echo "$url is ok"
  6. else
  7. echo "$url is field"
  8. fi

4.变量子串

  • 格式:${}
  • 主要作用:变量子串效率比使用相应的命令,效率更高
  • 提前说明:parameter变量名字

    1.基础用法

  • ${变量} 引用变量

  • ${#变量} 查看变量的字符数(非常高效)

time显示脚本执行使用时间
image.png

案例

一个英语句子,显示这串字符中 单词字符大于6的单词。

  • 流程分析
    • 一句话,进行分割
    • 分割后的单词如果大于6
    • 则进行显示 ```shell

!/bin/bash

text=echo I am liao jiang,I like my family.| tr '.,' ' '

for i in $text do if [ ${#i} -gt 3 ]; then echo $i fi done

  1. **AWK实现**<br />-F 指定输入文件折分隔符<br />-RS 记录分隔符(默认是一个换行符)<br />-v 赋值一个用户定义变量。
  2. ```shell
  3. echo I am liao jiang,I like my family. | awk -F '[,. ]' '{
  4. for(i=1;i<=NF;i++)
  5. if(length($i)>3)
  6. print $i
  7. }'
  8. echo I am liao jiang,I like my family. | awk -vRS="[,. ]" 'length()>3'

2.字符截取 切片

${变量:起点} 从起点显示到结束
${变量:起点:长度} 从起点开始显示最多长度个字符

  1. name=liao996.icu
  2. echo ${name:4}
  3. #996.icu
  4. echo ${name:2}
  5. #ao996.icu
  6. echo ${name:2:6}
  7. #ao996.

3.删除

${变量#字符} 左边开始(开头)
${变量%字符} 右边开始(结尾)
*从只查询开头变为从开头查询全部的第一个
两个##或%% 从开头或者末尾查询全部

  1. echo ${test#I} #纯纯的只判断开头
  2. #am oldboy teacher
  3. echo ${test#am} #因为只判断开头,所以这里什么都没修改
  4. #I am oldboy teacher
  5. echo ${test#*o}
  6. #ldboy teacher
  7. echo ${test##*a}
  8. #cher
  9. echo ${test%a*} #从末尾删除到该行出现的第一个a
  10. #I am oldboy te
  11. echo ${test%%a*} #从末尾删除到该行出现的最后一个a
  12. #I

案例

获得一个路径的时候,想要获取文件名和路径文件夹

  1. name=/etc/systemctl/ifconfig/123.txt
  2. echo ${name##*/}
  3. #123.txt
  4. echo ${name%/*}
  5. #/etc/systemctl/ifconfig

4.字符串替换

${变量/字符/想要替换的字符}
//多加一个 “/“ 就是变成全部替换

  1. echo ${name/123.txt/qwe.txt}
  2. #/etc/systemctl/ifconfig/qwe.txt
  3. echo ${name//s/_} #替换全部的字符
  4. /etc/_y_temctl/ifconfig/123.txt
  5. echo ${var//[a-z]/.} #使用正则,将所有的字母设置为...
  6. 123...

5.变量扩展

  • 给变量设置默认值 | 内容 | 含义 | 简单理解 | | —- | —- | —- | | ${p:-word} | 如果p没有被赋值或者值为空,word就作为他的值 | 没内容就用我的 | | ${p:=word} | 如果p没有被赋值或者值为空,word就作为他的值,并且将word赋值给p | 没内容就赋值为我的值 | | ${p:?word} | 如果p没有被赋值或者其值为空,那么就把word作为错误输出,否则显示p内容 | 没内容就把我当做报错输出,有内容就显示变量内容 | | ${p:+word} | 如果p没有被赋值或者其值为空,就什么都不做,否则用word替换变量内容 | 没内容啥都不做,有内容就用我的 |

6.变量赋值与read

  • 直接赋值:name=liao,url=$1
  • 引用其他命令结果进行赋值:ip=’hostname -I | awk ‘{print $2}’’
  • 交互式变量赋值:read -s 不显示输入信息,-t 超时时间, -p指定输出/提示
  • 脚本传参:shell表示位置的特殊变量 $1 $2 $3

read交互式赋值
-p 显示设置的提示
-s 不显示输入的内容,可以用来设置密码
-t 设置超时时间,单位为秒

-p 参数一定要在最后

  1. read -p "input url or ip: " addr
  2. #input url or ip: 192.168.1.1
  3. echo $addr
  4. 192.168.1.1

7.运算

案例:

  1. echo $RANDOM #取随机数,0-2的15次方
  2. echo $(($RANDOM%10)) #取10的余数,就是0-9的随机数

image.png

7.1运算方式

$(())

在$(())中运算变量,并且无法直接计算小数

  1. echo $(1+1)
  2. echo 1+1
  3. echo (1+1)
  4. #上面这些操作都无法运算
  5. echo $((1+1))
  6. 2
  7. echo $((2**2))
  8. 4
  9. echo $((100%10))
  10. 0
  11. n1=111
  12. n2=222
  13. echo $((n1+n2))
  14. 333
  15. n3='echo $((n1+n2))'
  16. echo $n3
  17. 333
  18. ((n4=n1+n2+n3))
  19. echo $4
  20. 666

Let

赋值并运算,并且支持++,—

  1. n1=111
  2. n2=234
  3. let n3=n1+n2
  4. echo $n3
  5. 345

expr

还可以判断两个字符是否是数字,并且 需要反斜杠转义 \

  1. expr 1+1 #必须要加 空格
  2. 1+1
  3. expr 1 + 1 #此时才能正确运算
  4. 2
  5. expr 5 * 5 #运算乘法需要添加转义符
  6. expr: syntax error
  7. expr 5 \* 5
  8. 25
  9. expr 5 '*' 5
  10. 25
  11. expr 1 + 1
  12. echo $?
  13. 0
  14. expr aa + 1
  15. echo $?
  16. 2

bc

bc是一个计算器工具
yum install -y bc 安装需要的bc工具
可以浮点运算,平方次方运算

  1. echo 1+1 | bc
  2. 2
  3. echo $n1*$n2 | bc
  4. 28782
  5. echo 2^10 | bc
  6. 1024
  7. #也可以输入bc进入工具 直接使用
  8. echo "obase=16;16"|bc
  9. 10
  10. echo "obase=16;15"|bc
  11. F
  12. echo "obase=16;14"|bc
  13. E

expr还可以对字符串操作

  1. expr length "string" #获取字符串长度
  2. 6
  3. expr substr "string" 2 5 #截取字符串
  4. tring
  5. expr substr "string" 2 4
  6. trin
  7. expr index "string" ing #获取字符在字符串中出现的位置
  8. 4
  9. expr match "string" s.* #获取字符串开始字符出现的长度
  10. 6
  11. expr match "string" str
  12. 3

$[] ★

也无法直接计算小数

  1. echo $[123+321]
  2. 444
  3. echo $[123**3]
  4. 1860867

awk

awk也可以进行运算,并且显示小数,显示多个运算。

  1. awk 'BEGIN{print 1/3,2**10}'
  2. 0.333333 1024
  3. #想要计算变量,需要特殊操作 -v
  4. x=1
  5. y=3
  6. awk -vn1=1 -vn2=3 'BEGIN{print n1/n2}'
  7. 0.333333
  8. awk -vn1=$x -vn2=$y 'BEGIN{print n1/n2}'
  9. 0.333333

总结

方法 用法
awk ‘BEGIN{}’ 在脚本中进行统计计算 -v
echo+ bc -l 小数,进制化转化
$(()) 进行整数计算
let
expr 检查变量或参数是否为数字
$[]

案例

书写简单的计算器

  • 从命令行输出2个参数 sh cal.sh 10 20
  • 计算他们的加减乘除 ```shell

    1.判断是否全为数字

    2.if判断

    3.使用awk计算

!/bin/bash

expr $1 + $2 >/dev/null 2>&1 if [ $? -eq 0 ]; then awk -vn1=$1 -vn2=$2 “BEGIN{print n1+n2,n1-n2,n1*n2,n1/n2}” else echo “error!” fi

  1. 使用read制作计算器
  2. ```shell
  3. #!/bin/bash
  4. read -p "请输入第一个数字: " a1
  5. read -p "请输入第二个数字: " a2
  6. expr $a1 + $a2 > /dev/null 2>&1
  7. if [ $? -eq 0 ]; then
  8. awk -vn1=$a1 -vn2=$a2 'BEGIN{print n1+n2,n1-n2,n1*n2,n1/n2}'
  9. else
  10. echo "error!!!!!!"
  11. fi

8.条件测试语句

  • 以后用于各种判断中的条件
  • 条件测试语句类型
    • 文件相关表达式
    • 数字对比
    • 字符串对比
    • 逻辑的 与 或 非

条件测试语句格式:

格式 作用
[ <条件> ] 一般情况
[[ <条件> ]] []升级版,支持正则表达式
(( <条件> ))

1.文件相关

名称 含义
-d directory目录是否存在
-f file是否存在
-e exist是否存在,不管是文件,文件夹
-r read文件是否存在,并且是否有r权限
-w write文件是否存在,并且是否有w权限
-x execute文件是否存在,并且是否有x权限
-s 判断文件是否为空,空文件时为false
-L Symlink文件是否存在,是否为软链接
f1 -nt f2 file1 newer than file2 比较创建时间新
f1 -ot f2 file2 older than file2 比较创建时间晚
  1. [ -f /etc/hosts ] && echo 存在 || echo 不存在
  2. #存在
  3. [ /etc/ -nt /root/1.txt ] && echo 'true' || echo 'false'
  4. #false

2.脚本的判断简写

&& cmd1 && cmd2 前一个命令执行成功,再执行后面的命令
|| cmd1 && cmd2 前一个命令执行失败,再执行后面的命令

案例优化

针对前面的计算机脚本的优化

  1. #!/bin/bash
  2. [ $# -ne 2 ] && {
  3. echo "error!!!"
  4. exit 1
  5. }
  6. expr $1 + $2 >/dev/null 2>&1
  7. if [ $? -eq 0 ]; then
  8. awk -vn1=$1 -vn2=$2 "BEGIN{print n1+n2,n1-n2,n1*n2,n1/n2}"
  9. else
  10. echo "error!"
  11. fi

3.字符串的对比

表达式 含义
-n net zero 如果变量/字符串不是空则成立
-z zero 如果变量/字符串是空 则成立
“n1”=”n2” 判断两个字符串/变量内容 是否一致 如果相等则成立
“n1”!=”n2” 判断两个字符串/变量内容 是否一致 如果不相等则成立
  1. name=liao
  2. [ -n "$name" ] && echo "成立" || echo "失败" #判断是否不为空
  3. 成立
  4. [ -z "$test" ] && echo "成立" || echo "失败" #判断是否为空
  5. 成立
  6. name1=liao
  7. [ "$name"="name1" ] && echo "成立" || echo "失败"
  8. 成立
  9. [ "$name" = "$name1" ] && echo "成立" || echo "失败"
  10. 成立
  11. [ "$name" != "$name1" ] && echo "成立" || echo "失败"
  12. 失败

4.常用的shell脚本

1.删除开头注释

  1. egrep -v '#|^$' sshd_config
  2. sed -r '/#|^$/d' sshd_config
  3. #-r 支持正则表达式
  4. #d 表示删除
  5. awk '!/#|^$' sshd_config

2.查看主机名,IPv4地址,版本,内核版本

  1. hostname #查看主机名
  2. hostname -I | awk '{print $1}' #IPv4地址
  3. cat /etc/redhat-release #查看版本
  4. uname -r #查看内核版本

3.获得当前日期,时间,用户名和当前目录

  1. date +%F
  2. date +%T
  3. whoami
  4. pwd

4.将文件中的小写字母转换为大写字母

  1. tr 'a-z' 'A-Z' < passwd
  2. awk '{print toupper($0)}' passwd #$0 整行
  3. sed 's#[a-z]#\U&#g' passwd #& 表示前面匹配到的小写字母 \U 转换为大写

5.检测当前用户是否为root,如果是则用yum安装vsftpd

  1. #查看uid是否为0
  2. #取出当前用户名
  3. #!/bin/bash
  4. if [ $UID -ne 0 ]; then
  5. echo "error!"
  6. exit 1
  7. else
  8. yum install vsftpd -y
  9. fi
  10. #!/bin/bash
  11. name=`whoami`
  12. if [ $name -ne "root" ]; then
  13. echo "error!"
  14. exit 1
  15. else
  16. yum install vsftpd -y
  17. fi

6.设计一个shell程序,在每月第一天备份并压缩/etc目录的所有内容,存放在/root/bak目录中,且文件名为yymmdd_etc。shell程序fileback存放在/usr/bin目录下

  1. #shell+定时任务+备份
  2. #1.检查目录和备份文件是否存在
  3. #2.创建指定的文件夹
  4. #3.打包压缩
  5. #!/bin/bash
  6. dir=/root/bak
  7. time=`date +%y%m%d`
  8. [ -d $dir ] || mkdir -p $dir
  9. tar zcf ${dir}/${time}_etc.tar.gz /etc/
  10. crontab -e
  11. #backup /etc/ by liao at 2022-3-27
  12. 00 00 01 * * /bin/sh /usr/bin/filebak &>/dev/null

7.统计当前Linux系统中可以登录的账户有多少个。

命令用户解释器是/bin/bash的

  1. grep '/bin/bash' /etc/passwd| wc -l
  2. grep -c '/bin/bash' /etc/passwd

8.删除字符串中的所有空格

  1. str='I am liao,I like Linux!!! wuhu~'
  2. echo ${str// /}
  3. echo $str|tr -d ' '
  4. echo $str|sed 's# ##g'

9.在脚本文件中重定向标准输出和标准错误流到log.txt

  1. cmd &> /tmp/log.txt
  2. cmd > /tmp/log.txt 2>&1

9.条件判断

1.if语法

if判断,适用只需要一步判断,条件返回真时执行命令或者条件返回假执行命令。

括号类型 用法
[] 条件判断
[[]] 在条件中使用通配符
(()) 在条件中植入数学表达式
  1. if [ 条件判断 ]
  2. then
  3. commands
  4. fi

如果登录用户是root则提示管理员你好,如果是普通用户则提示guest你好

  1. #!/bin/bash
  2. if [ $USER == 'root' ]
  3. then
  4. echo "管理员,你好"
  5. else
  6. echo "guest,你好"
  7. fi

判断$1 $2两个整数的关系

  1. #!/bin/bash
  2. if [ $1 -eq $2 ]
  3. then
  4. echo "$1=$2"
  5. elif [ $1 -gt $2 ]
  6. then
  7. echo "$1 > $2"
  8. else
  9. echo "$1 < $2"
  10. fi

查询字符串中,那些是以r开头的

  1. #!/bin/bash
  2. for i in rr1 rrr2 rr3 lw fc rr5
  3. do
  4. if [[ $i == r* ]]
  5. then
  6. echo "$i"
  7. fi
  8. done

案例

判断 crond 进程是否运行

  1. #!/bin/bash
  2. NAME=crond
  3. NUM=$(ps -ef|grep $NAME | grep -vc grep)
  4. if [ $NUM -eq 1 ]
  5. then
  6. echo "$NAME is alived"
  7. else
  8. echo "$NAME is not running"
  9. fi

根据发行版不同来安装程序

  1. #!/bin/bash
  2. if [ -e /etc/redhat-release ]
  3. then
  4. yum install -y wget
  5. elif [ $(cat /etc/issue | cut -d'' -f1) == "Ubuntu" ]
  6. then
  7. apt-get install wget -y
  8. else
  9. echo "I don't know"
  10. fi

2.for循环

脚本在执行任务的时候,总会遇到需要循环执行的时候2,比如说我们需要脚本每隔5分钟执行一次ping命令的操作。除了计划任务,我们还可以用脚本来完成。此时就需要循环语句。

  1. for 变量名 in 取值列表; do
  2. 命令
  3. done
  4. for 变量名 in 取值列表
  5. do
  6. 命令
  7. done
  8. for 变量名 in 取值列表
  9. {
  10. 命令
  11. }
  12. #甚至还有一套c语言风格的语法
  13. for ((i=1;i<=5;i++)); do # 也可以 i--
  14. echo $i
  15. done

打印输出1-9

  1. #!/bin/bash
  2. for i in `seq 1 9`
  3. {
  4. echo $i
  5. }

输出输入的字符

  1. #!/bin/bash
  2. for i in "$@" #脚本输入的的参数赋值给$i
  3. {
  4. echo $i
  5. }
  6. #!/bin/bash
  7. for i in 12 13 14 15 16 #自己输入的参数赋值给$i
  8. {
  9. echo $i
  10. }

3.while语句

while 循环用于不断执行一系列命令,也用于从输入文件中读取数据。其语法格式为:

  1. while 条件表达式; do
  2. 命令
  3. done

循环打印1-5

  1. #!/bin/bash
  2. N=0
  3. while [ $N -lt 5 ]
  4. do
  5. let N++ #++ 我们使用let
  6. echo $N
  7. done

无限循环
主要就是将while后的判断为true,或者状态为0。第一个就是为true,第二个使用 : 就是为0。

  1. #!/bin/bash
  2. while true;do
  3. echo "yes"
  4. done
  5. while :; do
  6. echo "yes"
  7. done

while逐行读取文件内容
推荐第二种形式

  1. #!/bin/bash
  2. DIR1=$1
  3. cat $1 | while read LINE; do
  4. echo $LINE
  5. done
  6. #!/bin/bash
  7. DIR1=$1
  8. while read LINE; do
  9. echo $LINE
  10. done < $DIR1

4.until

until 循环执行一系列命令直至条件为 true 时停止。
until 循环与 while 循环在处理方式上刚好相反。
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
until 语法格式:

  1. until condition
  2. do
  3. command
  4. done

condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
以下实例我们使用 until 命令来输出 0 ~ 9 的数字:

  1. #!/bin/bash
  2. a=0
  3. until [ ! $a -lt 10 ]
  4. do
  5. echo $a
  6. a=`expr $a + 1`
  7. done

5.break和continue

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。
break命令允许跳出所有循环(终止执行后面的所有循环)。
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

创建无限循环,当N=1000时停止

  1. #!/bin/bash
  2. N=2
  3. while true; do
  4. let N++
  5. if [ $N -eq 1000 ]
  6. then
  7. echo $N
  8. break
  9. fi
  10. done

1-100打印偶数

  1. #!/bin/bash
  2. for N in {1..100}; do
  3. if (( $N%2 == 0 )); then
  4. echo $N
  5. fi
  6. done

6.case语句

case … esac 为多选择语句,与其他语言中的 switch … case 语句类似,是一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case … esac 语句,esac(就是 case 反过来)作为结束标记。
可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。并且支持正则表达式
case … esac 语法格式如下:

  1. case in
  2. 模式1)
  3. command1
  4. command2
  5. ...
  6. commandN
  7. ;;
  8. 模式2)
  9. command1
  10. command2
  11. ...
  12. commandN
  13. ;;
  14. esac

case 工作方式如上所示,取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

模仿脚本的start restart stop

  1. #!/bin/bash
  2. case $1 in
  3. start)
  4. echo "this jb is starting"
  5. ;;
  6. restart)
  7. echo "this jb is restarting"
  8. ;;
  9. stop)
  10. echo "this jb is stopping"
  11. ;;
  12. *)
  13. echo "Usage: $0 {start|restart|stop}"
  14. esac

7.select语句

select 是一个类似于 for 循环的语句。 在for循环中,我们指定的变量群他会自动逐个赋值循环,而用了select,我们自己从变量群中选择某个字符循环。

  1. #!/bin/bash
  2. select mysql_version in 5.1 5.6; do
  3. echo $mysql_version
  4. done
  5. [root@hecs-215335 ~]# sh 20_select1.sh
  6. 1) 5.1
  7. 2) 5.6
  8. #? 1
  9. 5.1
  10. #? 2
  11. 5.6

用户输入编号会直接赋值给变量 mysql_version。作为菜单用的话,循环第二次后就不再显示菜单 了,并不能满足需求。 在外面加个死循环,每次执行一次 select 就 break 一次,这样就能每次显示菜单了:

  1. #!/bin/bash
  2. while true; do
  3. select mysql_version in 5.1 5.6; do
  4. echo $mysql_version
  5. break
  6. done
  7. done
  8. [root@hecs-215335 ~]# sh 21_select2.sh
  9. 1) 5.1
  10. 2) 5.6
  11. #? 1
  12. 5.1
  13. 1) 5.1
  14. 2) 5.6
  15. #? 2
  16. 5.6
  17. 1) 5.1
  18. 2) 5.6
  19. #? 1
  20. 5.1
  21. 1) 5.1
  22. 2) 5.6
  23. #?

如果再判断对用户输入的编号执行相应的命令,如果用 if 语句多分支的话要复杂许多,用 case 语 句就简单多了。

  1. #!/bin/bash
  2. while true; do
  3. select mysql_version in 5.1 5.6 5.8 quit; do
  4. case $mysql_version in
  5. 5.1)
  6. echo "mysql 5.1"
  7. break
  8. ;;
  9. 5.6)
  10. echo "mysql 5.6"
  11. break
  12. ;;
  13. 5.8)
  14. echo "mysql 5.8"
  15. break
  16. ;;
  17. quit)
  18. echo "exit,Bye~"
  19. exit
  20. ;;
  21. *)
  22. echo "you input error"
  23. break
  24. esac
  25. done
  26. done

10.函数

linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
shell中函数的定义格式如下:

  1. [ function ] funname [()]
  2. {
  3. action;
  4. [return int;]
  5. }

说明:

  • 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
  • 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255) ```shell

    !/bin/bash

    demofunc(){ echo “wuhu” } demofunc

wuhu

  1. <a name="FVgGN"></a>
  2. ## 1.函数返回值
  3. return 在函数中定义状态返回值,返回并终止函数,但返回的只能是 0-255 的数字,类似于 exit。
  4. ```shell
  5. #!/bin/bash
  6. func() {
  7. VAR=$((1000+1234))
  8. return $VAR
  9. echo "this is a function"
  10. }
  11. func
  12. echo $?
  13. 186

2.函数传参

通过 Shell 位置参数给函数传参。

  1. #!/bin/bash
  2. funtest(){
  3. echo $1
  4. echo $2
  5. echo $3
  6. echo $*
  7. }
  8. funtest 1 23 2323
  9. 1
  10. 23
  11. 2323
  12. 1 23 2323

并且还支持递归

11.数组

数组是相同类型的元素按一定顺序排列的集合。 数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与 PHP 类似)。与大部分编程语言类似,数组元素的下标由 0 开始。

Shell 数组用括号来表示,元素用”空格”符号分割开,语法格式如下:

  1. array_name=(value1 value2 ... valuen)

定义方法:

定义方法 1:初始化数组 array=(a b c) 定义方法 2:新建数组并添加元素 array[下标]=元素 定义方法 3:将命令输出作为数组元素 array=($(command))

创建并获取数组

  1. #!/bin/bash
  2. my_arrys=("abc" "1231" "bsvf" 666)
  3. echo ${my_arrys[0]}
  4. echo ${my_arrys[1]}
  5. echo ${my_arrys[2]}
  6. echo ${my_arrys[3]}
  7. echo ${my_arrys[*]}
  8. echo ${my_arrys[@]}
  9. abc
  10. 1231
  11. bsvf
  12. 666
  13. abc 1231 bsvf 666
  14. abc 1231 bsvf 666

获取数组长度

  1. #!/bin/bash
  2. my_arrys=("abc" "1231" "bsvf" 666)
  3. echo ${#my_arrys[@]}
  4. echo ${#my_arrys[*]}
  5. 4
  6. 4

数组的添加与删除

  1. my_arrys=("abc" "1231" "bsvf" 666)
  2. my_arrys[4]="qwq" #添加一个元素
  3. echo ${my_arrys}
  4. abc 1231 bsvf 666 qwq
  5. my_arrys+=("avc" "ghz" "casd") #添加多个元素
  6. echo ${my_arrys}
  7. abc 1231 bsvf 666 qwq avc ghz casd
  8. unset my_arrys[2] #删除第三个元素
  9. echo ${my_arrys}
  10. abc 1231 666 qwq avc ghz casd
  11. unset my_arrys #删除数组

生成指定数量的随机数放入到数组中

  1. #!/bin/bash
  2. for ((i=1;i<=$1;i++)); do
  3. array[i]=$((RANDOM%10))
  4. done
  5. echo ${array[*]}