shell编程实战
if 条件语句的知识与实践(接上),Shell 函数的知识与实践,case 条件语句的应用实践,while 循环和 until 循环的应用实践,for 循环语句的应用实践
if 条件语句的知识与实践
if 条件语句实践
示例1:
[root@server ~ 09:39:45]# cd bin
[root@server bin 09:40:05]# vim ssh_ctl_if_1.sh
[root@server bin 09:48:42]# cat ssh_ctl_if_1.sh
#!/bin/bash
systemctl is-active sshd &>/dev/null
return_count=$?
if (( return_count!=0 ));then
echo "Sshd is not running."
echo -n "Starting sshd ... "
systemctl start sshd && echo Success || echo Fail
else
echo "Sshd is running."
#echo -n "Stopping sshd ... "
#systemctl stop sshd && echo Success || echo Fail
fi
[root@server bin 09:48:46]# chmod +x ssh_ctl_if_1.sh
[root@server bin 09:48:59]# ./ssh_ctl_if_1.sh
Sshd is running.
#对比
[root@server bin 09:49:12]# vim ssh_ctl_if_2.sh
[root@server bin 09:49:42]# cat ssh_ctl_if_2.sh
#!/bin/bash
status="$(systemctl is-active sshd)"
if (( status=="inactive" ));then
echo "Sshd is not running."
echo -n "Starting sshd ... "
systemctl start sshd && echo Success || echo Fail
else
echo "Sshd is running."
#echo -n "Stopping sshd ... "
#systemctl stop sshd && echo Success || echo Fail
fi
[root@server bin 09:49:50]# chmod +x ssh_ctl_if_2.sh
[root@server bin 09:49:58]# ./ssh_ctl_if_2.sh
Sshd is not running.
Starting sshd ... Success
示例2:通过传参控制sshd服务。
[root@server bin 09:50:07]# vim ssh_ctl_if_3.sh
[root@server bin 10:21:47]# cat ssh_ctl_if_3.sh
#!/bin/bash
if [ "$1" = "start" ];then
systemctl $1 sshd
elif [ "$1" = "stop" ];then
systemctl $1 sshd
elif [ "$1" = "status" ];then
systemctl $1 sshd
else
echo "Usage: $0 start|stop|status"
fi
[root@server bin 10:21:50]# chmod +x ssh_ctl_if_3.sh
#验证
[root@server bin 10:21:57]# ./ssh_ctl_if_3.sh
Usage: ./ssh_ctl_if_3.sh start|stop|status
[root@server bin 10:22:04]# ./ssh_ctl_if_3.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since 四 2025-10-09 17:26:16 CST; 16h ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 2787 (sshd)
CGroup: /system.slice/sshd.service
└─2787 /usr/sbin/sshd -D
... ...
[root@server bin 10:22:10]# ./ssh_ctl_if_3.sh stop
[root@server bin 10:22:17]# ./ssh_ctl_if_3.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: inactive (dead) since 五 2025-10-10 10:22:17 CST; 1s ago
Docs: man:sshd(8)
man:sshd_config(5)
Process: 2787 ExecStart=/usr/sbin/sshd -D $OPTIONS (code=exited, status=0/SUCCESS)
Main PID: 2787 (code=exited, status=0/SUCCESS)
... ...
[root@server bin 10:22:18]# ./ssh_ctl_if_3.sh start
[root@server bin 10:22:22]# ./ssh_ctl_if_3.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since 五 2025-10-10 10:22:22 CST; 1s ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 3042 (sshd)
CGroup: /system.slice/sshd.service
└─3042 /usr/sbin/sshd -D
... ...
#缩写代码
[root@server bin 10:22:24]# vim ssh_ctl_if_4.sh
[root@server bin 10:22:44]# cat ssh_ctl_if_4.sh
#!/bin/bash
if [ "$1" = "start" -o "$1" = "stop" -o "$1" = "status" ];then
systemctl $1 sshd
else
echo "Usage: $0 start|stop|status"
fi
[root@server bin 10:22:46]# chmod +x ssh_ctl_if_4.sh
#验证
[root@server bin 10:22:52]# ./ssh_ctl_if_4.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since 五 2025-10-10 10:22:22 CST; 39s ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 3042 (sshd)
CGroup: /system.slice/sshd.service
└─3042 /usr/sbin/sshd -D
... ...
[root@server bin 10:23:01]# ./ssh_ctl_if_4.sh stop
[root@server bin 10:23:09]# ./ssh_ctl_if_4.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: inactive (dead) since 五 2025-10-10 10:23:09 CST; 1s ago
Docs: man:sshd(8)
man:sshd_config(5)
Process: 3042 ExecStart=/usr/sbin/sshd -D $OPTIONS (code=exited, status=0/SUCCESS)
Main PID: 3042 (code=exited, status=0/SUCCESS)
... ...
[root@server bin 10:23:11]# ./ssh_ctl_if_4.sh start
[root@server bin 10:23:15]# ./ssh_ctl_if_4.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since 五 2025-10-10 10:23:15 CST; 1s ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 3067 (sshd)
CGroup: /system.slice/sshd.service
└─3067 /usr/sbin/sshd -D
... ...
示例3:
[root@server bin 10:58:27]# free
total used free shared buff/cache available
Mem: 4026124 272444 3464708 12128 288972 3523216
Swap: 2097148 0 2097148
[root@server bin 10:48:22]# vim check_memory.sh
[root@server bin 10:49:00]# cat check_memory.sh
#!/bin/bash
memory_available_size=$(free -m | awk '/Mem/ {print $NF}')
memory_min_size=10000
if ((memory_free_size<memory_min_size));then
echo "Current available memory size is ${memory_available_size}" | mail -s "memory size is lower" root
fi
[root@server bin 10:52:01]# crontab -e
[root@server bin 10:53:51]# crontab -l
* * * * * /root/bin/check_memory.sh
[root@server ~ 11:04:07]# tail -f /var/spool/mail/root
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <HOME=/root>
X-Cron-Env: <PATH=/usr/bin:/bin>
X-Cron-Env: <LOGNAME=root>
X-Cron-Env: <USER=root>
Message-Id: <20251010030401.3B0D8601601F@server.localdomain>
Date: Fri, 10 Oct 2025 11:04:01 +0800 (CST)
From root@server.localdomain Fri Oct 10 11:05:01 2025
Return-Path: <root@server.localdomain>
X-Original-To: root
Delivered-To: root@server.localdomain
Received: by server.localdomain (Postfix, from userid 0)
id 470FA601601F; Fri, 10 Oct 2025 11:05:01 +0800 (CST)
Date: Fri, 10 Oct 2025 11:05:01 +0800
To: root@server.localdomain
Subject: memory size is lower
User-Agent: Heirloom mailx 12.5 7/5/10
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-Id: <20251010030501.470FA601601F@server.localdomain>
From: root@server.localdomain (root)
Current available memory size is 3436
^C
您在 /var/spool/mail/root 中有邮件
[root@server bin 11:05:07]# mailx
Heirloom Mail version 12.5 7/5/10. Type ? for help.
"/var/spool/mail/root": 11 messages 1 new 11 unread
>N 11 root Fri Oct 10 11:05 18/631 "memor"
& 11
Message 11:
From root@server.localdomain Fri Oct 10 11:05:01 2025
Return-Path: <root@server.localdomain>
X-Original-To: root
Delivered-To: root@server.localdomain
Date: Fri, 10 Oct 2025 11:05:01 +0800
To: root@server.localdomain
Subject: memory size is lower
User-Agent: Heirloom mailx 12.5 7/5/10
Content-Type: text/plain; charset=us-ascii
From: root@server.localdomain (root)
Status: R
Current available memory size is 3436
Shell 函数的知识与实践
Shell 函数介绍
在讲解Shell 函数之前,先来回顾Linux系统中 alias
的作用
[root@server bin 11:20:53]# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
[root@server bin 11:24:27]# ls -l --color=auto /home
总用量 0
drwx------. 3 dyx dyx 110 10月 9 17:20 dyx
drwx------ 2 zhangsan zhangsan 62 10月 9 14:20 zhangsan
函数也有类似于别名的作用,例如可简化程序的代码量,让程序更易读、易改、易用。
简单地说,函数的作用就是将程序里多次被调用的相同代码组合起来(函数体),并为其取一个名字(即函数名),其他所有想重复调用这部分代码的地方都只需要调用这个名字就可以了。当需要修改这部分重复代码时,只需要改变函数体内的一份代码即可实现对所有调用的修改,也可以把函数独立地写到文件里,当需要调用函数时,再加载进来使用。
使用 Shell 函数的优势整理如下:
- 把相同的程序段定义成函数,可以减少整个程序的代码量,提升开发效率。
- 增加程序的可读性、易读性,提升管理效率。
- 可以实现程序功能模块化,使得程序具备通用性(可移植性)。
对于Shell来说,Linux系统里的近2000个命令可以说都是Shell的函数,所以,Shell的函数也是很多的,这一点需要读者注意。
Shell 函数的语法
下面是Shell 函数的常见语法格式。
标准写法:
function 函数名 () {
指令...
return n
}
简化写法1:不写()
function 函数名 {
指令...
return n
}
简化写法2:不写function
函数名 () {
指令...
return n
}
Shell 函数的执行
Shell的函数分为最基本的函数和可以传参的函数两种,其执行方式分别说明如下。
-
执行不带参数的函数时,直接输人函数名即可(注意不带小括号)。
格式如下:
函数名
有关执行函数的重要说明:
- 执行Shell 函数时,函数名前的function和函数后的小括号都不要带。
- 函数的定义必须在要执行的程序前面定义或加载。
- Shell执行系统中各种程序的执行顺序为:系统别名->函数->系统命令->可执行文件。
- 函数执行时,会和调用它的脚本共用变量,也可以为函数设定局部变量及特殊位置参数。
- 在Shell 函数里面,return命令的功能与exit类似,return的作用是退出函数,而exit是退出脚本文件。
- return语句会返回一个退出值(即返回值)给调用函数的当前程序,而exit会返回一个退出值(即返回值)给执行程序的当前Shell。
- 如果将函数存放在独立的文件中,被脚本加载使用时,需要使用source或来加载。
- 在函数内一般使用local定义局部变量,这些变量离开函数后就会消失。
-
带参数的函数执行方法,格式如下:
函数名 参数1 参数2
函数后接参数的说明:
- Shell 的位置参数($1、 2 … 、 2…、 2…、#、 ∗ 、 *、 ∗、?及$@)都可以作为函数的参数来使用。
- 此时父脚本的参数临时地被函数参数所掩盖或隐藏。
- $0 比较特殊,它仍然是父脚本的名称。
- 当函数执行完成时,原来的命令行脚本的参数即可恢复。
- 函数的参数变量是在函数体里面定义的。
Shell 函数的基础实践
示例1:hello函数
[root@server bin 11:10:51]# vim fun1.sh
[root@server bin 11:14:31]# cat fun1.sh
#!/bin/bash
function hello () {
echo "Hello World ! "
}
hello
[root@server bin 11:14:35]# bash fun1.sh
Hello World !
#函数先定义在调用
[root@server bin 11:14:46]# vim fun2.sh
[root@server bin 11:15:17]# cat fun2.sh
#!/bin/bash
hello
function hello () {
echo "Hello World !"
}
[root@server bin 11:15:19]# bash fun2.sh
fun2.sh:行2: hello: 未找到命令
示例2:调用外部函数
[root@server bin 11:16:36]# cat >> mylib << 'efo'
> function hello () {
> echo "Hello World !"
> }
> efo
[root@server bin 11:15:25]# vim fun3.sh
[root@server bin 11:16:28]# cat fun3.sh
#!/bin/bash
if [ -r mylib ];then
source mylib
hello
else
echo mylib is not exist
exit 1
fi
[root@server bin 11:17:32]# bash fun3.sh
Hello World !
[root@server bin 11:32:22]# source mylib
[root@server bin 11:33:22]# hello
Hello World !
示例3:带参数的函数
[root@server bin 11:17:35]# vim fun4.sh
[root@server bin 11:18:05]# cat fun4.sh
#!/bin/bash
function print () {
if [ "$1" == "PASS" ];then
echo -e '\033[1;32mPASS\033[0;39m'
elif [ "$1" == "FAIL" ];then
echo -e '\033[1;31mFAIL\033[0;39m'
elif [ "$1" == "DONE" ];then
echo -e '\033[1;35mDONE\033[0;39m'
else
echo "Usage: print PASS|FAIL|DONE"
fi
}
read -p "请输入你想要打印的内容:" str
print $str
[root@server bin 11:18:10]# bash fun4.sh
请输入你想要打印的内容:PASS
PASS
[root@server bin 11:18:23]# bash fun4.sh
请输入你想要打印的内容:hello
Usage: print PASS|FAIL|DONE
示例4:hello函数
[root@server bin 11:26:21]# cat fun5.sh
#!/bin/bash
function print () {
if [ "$1" == "PASS" ];then
echo -e '\033[1;32mPASS\033[0;39m'
elif [ "$1" == "FAIL" ];then
echo -e '\033[1;31mFAIL\033[0;39m'
elif [ "$1" == "DONE" ];then
echo -e '\033[1;35mDONE\033[0;39m'
else
echo "Usage: $0 PASS|FAIL|DONE"
fi
}
str=$2
print $str
# 结果表明,脚本的第一个参数并没有传递给脚本内部函数。
[root@server bin 11:26:24]# bash fun5.sh PASS FAIL
FAIL
利用 Shell 函数开发企业级URL检测脚本
[root@server bin 11:33:25]# vim check_url.sh
[root@server bin 11:44:20]# cat check_url.sh
#!/bin/bash
function usage(){
echo "usage: $0 url"
exit 1
}
function check_url (){
wget --spider -q -o /dev/null --tries=1 -T 5 $1
[ $? -eq 0 ] && echo $1 is accessable || echo $1 is not accessable
}
function main(){
[ $# -ne 1 ] && usage
check_url $1
}
main $@
#main $*
#验证
[root@server bin 11:44:23]# bash check_url.sh baidu.com
baidu.com is accessable
[root@server bin 11:44:37]# bash check_url.sh dyx.com
dyx.com is not accessable
#运行过程
[root@server bin 11:44:44]# bash -x check_url.sh baidu.com
+ main baidu.com
+ '[' 1 -ne 1 ']'
+ check_url baidu.com
+ wget --spider -q -o /dev/null --tries=1 -T 5 baidu.com
+ '[' 0 -eq 0 ']'
+ echo baidu.com is accessable
baidu.com is accessable
[root@server bin 11:46:34]# bash -x check_url.sh dyx.com
+ main dyx.com
+ '[' 1 -ne 1 ']'
+ check_url dyx.com
+ wget --spider -q -o /dev/null --tries=1 -T 5 dyx.com
+ '[' 4 -eq 0 ']'
+ echo dyx.com is not accessable
dyx.com is not accessable
函数的递归调用
示例1:求1+2+3+…+10 的和
[root@server bin 14:10:31]# vim sum.sh
[root@server bin 14:12:32]# cat sum.sh
#!/bin/bash
function sum (){
if (($1==1));then
echo 1
else
echo $[ $1 + $(sum $[ $1 - 1 ]) ]
fi
}
[root@server bin 14:11:30]# source sum.sh
[root@server bin 14:11:35]# sum 1
1
[root@server bin 14:11:40]# sum 2
3
[root@server bin 14:11:43]# sum 6
21
示例2:求1*2*3*…*10 的阶乘
[root@server bin 13:48:50]# vim c.sh
[root@server bin 14:13:14]# cat c.sh
#!/bin/bash
function c(){
if (($1==1));then
echo 1
else
echo $[ $1 * $(c $[ $1 - 1 ]) ]
fi
}
[root@server bin 14:12:11]# source c.sh
[root@server bin 14:12:16]# c 3
6
[root@server bin 14:12:28]# c 10
3628800
示例3:fork 炸弹
[root@server bin 14:09:28]# :(){ :|:&};:
... ...
[root@server bin 14:10:21]# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 15632
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 15632
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
解释如下:
:() # 定义函数,函数名为":",即每当输入":"时就会自动调用{}内代码
{ # ":"函数起始字元
: # 用递归方式调用":"函数本身
| # 使用管道一次产生两个函数调用
: # 另一次递归调用的":"函数
& # 放后台运行,以便产生更多的子进程
} # ":"函数终止
; # ":"函数定义结束后将要进行的操作...
: # 调用":"函数,"引爆"fork炸弹
fork 炸弹原因:无限制的启用新进程,直到消耗完所有计算资源。
解决办法:限制用户进程数量。
case 条件语句的应用实践
case 条件语句相当于多分支的if/elif/else条件语句,但是它比这些条件语句看起来更规范更工整,常被应用于实现系统服务启动脚本等企业应用场景中。
在case语句中,程序会将case获取的变量的值与表达式部分的值1、值2、值3等逐个进行比较:
- 如果获取的变量值和某个值(例如值1)相匹配,就会执行值(例如值1)后面对应的指令(例如指令1,其可能是一组指令),直到执行到双分号(;;)才停止,然后再跳出case语句主体,执行case语句(即esac字符)后面的其他命令。
- 如果没有找到匹配变量的任何值,则执行“*)”后面的指令(通常是给使用者的使用提示),直到遇到双分号(此处的双分号可以省略)或esac结束,这部分相当于if多分支语句中最后的else语句部分。
- 另外,case语句中表达式对应值的部分,还可以使用管道等更多功能来匹配。
case 条件语句的语法
case 条件语句的语法格式为:
case "变量值" in
值1)
指令1...
;;
值2)
指令2...
;;
*)
指令3...
;;
esac
为了便于大家记忆,下面是某女生写的case 条件语句的中文形象描述:
case "找老公条件" in
资产大于1千万)
嫁给你
;;
资产介于1百万和1千万之间)
再考虑以下
;;
资产小于1百万)
再见
;;
其他情况)
视情况而定
;;
esac
case 条件语句的执行流程逻辑图如下:
case 条件语句实践
示例1:控制sshd服务
[root@server bin 14:29:40]# vim ssh_ctl_case.sh
[root@server bin 14:34:11]# cat ssh_ctl_case.sh
#!/bin/bash
case $1 in
start)
systemctl $1 sshd
;;
stop)
systemctl $1 sshd
;;
status)
systemctl $1 sshd
;;
*)
echo "Usage: $0 start|stop|status"
;;
esac
#验证
[root@server bin 14:34:15]# bash ssh_ctl_case.sh
Usage: ssh_ctl_case.sh start|stop|status
[root@server bin 14:34:30]# bash ssh_ctl_case.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since 五 2025-10-10 10:23:15 CST; 4h 11min ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 3067 (sshd)
CGroup: /system.slice/sshd.service
└─3067 /usr/sbin/sshd -D
... ...
[root@server bin 14:34:40]# bash ssh_ctl_case.sh stop
[root@server bin 14:34:51]# bash ssh_ctl_case.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: inactive (dead) since 五 2025-10-10 14:34:51 CST; 5s ago
Docs: man:sshd(8)
man:sshd_config(5)
Process: 3067 ExecStart=/usr/sbin/sshd -D $OPTIONS (code=exited, status=0/SUCCESS)
Main PID: 3067 (code=exited, status=0/SUCCESS)
... ...
[root@server bin 14:34:56]# bash ssh_ctl_case.sh start
[root@server bin 14:35:01]# bash ssh_ctl_case.sh status
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since 五 2025-10-10 14:35:01 CST; 1s ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 3561 (sshd)
CGroup: /system.slice/sshd.service
└─3561 /usr/sbin/sshd -D
... ...
示例2:管理用户
通过传参的方式往 /etc/users 里添加用户,具体要求如下。
-
命令用法为:
Usage: user-mgr [ [-add|-a ] | [ -d|-del ] | [ -s|-search ] ] username
-
传参要求为:
- 参数为-add|-a时,表示添加后面接的用户名。如果有同名的用户, 则不能添加。
- 参数为-del|-d时,表示删除后面接的用户名。如果用户不存在,提示用户不存在。
- 参数为-search|-s时,表示查找后面接的用户名。 如果用户不存在,提示用户不存在。
- 没有用户时应给出明确提示。
-
/etc/users 保存用户清单,格式如下:
username: dyx username: dyx2
-
/etc/users 不能被外部其他程序直接删除及修改。
[root@server ~ 15:06:14]# vim user-mgr.sh
[root@server ~ 15:08:08]# cat user-mgr.sh
#!/bin/bash
# run as root
[ $UID -ne 0 ] && echo 'Please run as root' && exit 1
# create users file
users_info_file=/etc/users
[ -f ${users_info_file} ] || touch ${users_info_file}
# provides two args
if [ $# -ne 2 ];then
echo "Usage: user-mgr [ [-add|-a ] | [ -d|-del ] | [ -s|-search ] ] username"
exit 2
fi
# get arg value
action=$1
username=$2
# manager user
case $action in
-s|-search)
if grep -q "username: $username" ${users_info_file};then
echo "$username is exist."
else
echo "$username is not exist."
fi
;;
-a|-add)
if grep -q "username: $username" ${users_info_file};then
echo "$username is exist."
else
chattr -i ${users_info_file}
echo "username: $username" >> ${users_info_file}
echo "$username has been added."
chattr +i ${users_info_file}
fi
;;
-d|-del)
if grep -q "username: $username" ${users_info_file};then
chattr -i ${users_info_file}
sed -i "/username: $username/d" ${users_info_file}
chattr +i ${users_info_file}
echo "$username has been deleted."
else
echo "$username is not exist."
fi
;;
*)
echo "Usage: user-mgr [ [-add|-a ] | [ -d|-del ] | [ -s|-search ] ] username"
;;
esac
验证
[root@server ~ 15:06:55]# bash user-mgr.sh -s dyx
dyx is not exist.
[root@server ~ 15:07:01]# bash user-mgr.sh -a dyx
dyx has been added.
[root@server ~ 15:07:20]# bash user-mgr.sh -s dyx
dyx is exist.
[root@server ~ 15:07:27]# cat /etc/users
username: dyx
[root@server ~ 15:07:35]# bash user-mgr.sh -a dyx
dyx is exist.
[root@server ~ 15:07:46]# bash user-mgr.sh -d dyx
dyx has been deleted.
[root@server ~ 15:07:54]# cat /etc/users
while 循环和 until 循环的应用实践
循环语句命令常用于重复执行一条指令或一组指令,直到条件不再满足时停止,Shell脚本语言的循环语句常见的有while、until、for及select循环语句。
while 循环语句主要用来重复执行一组命令或语句,在企业实际应用中,常用于守护进程或持续运行的程序,除此以外,大多数循环都会用后文即将讲解的for循环语句。
当型和直到型循环语法
while 循环语句
while 循环语句的基本语法为:
while <条件表达式>
do
指令...
done
提示:注意代码缩进。
while 循环语句会对紧跟在while命令后的条件表达式进行判断:
- 如果该条件表达式成立,则执行while 循环体里的命令或语句(即语法中do和done之间的指令),每一次执行到done时就会重新判断while条件表达式是否成立,直到条件表达式不成立时才会跳出while 循环体。
- 如果一开始条件表达式就不成立,那么程序就不会进入循环体(即语法中do和done之间的部分)中执行命令了。
while 循环执行流程对应的逻辑图如下:
示例1:
[root@server bin 15:20:23]# vim make_target.sh
[root@server bin 15:20:46]# cat make_target.sh
#!/bin/bash
money=0
target=100000000
#money<target的时候,运行
while [ $money -lt $target ]
do
echo -n "I'll working hard ... total money:"
money=$[ money + 10000000 ]
echo $money
sleep 0.5
done
#验证,到达目标后停止
[root@server bin 15:20:40]# bash make_target.sh
I'll working hard ... total money:10000000
I'll working hard ... total money:20000000
I'll working hard ... total money:30000000
I'll working hard ... total money:40000000
I'll working hard ... total money:50000000
I'll working hard ... total money:60000000
I'll working hard ... total money:70000000
I'll working hard ... total money:80000000
I'll working hard ... total money:90000000
I'll working hard ... total money:100000000
示例2:
需求:
-
后台监视sshd服务,一旦发现sshd服务未运行,就start sshd。
-
每隔3秒检测一次。不允许使用计划任务(cron)。
[root@server bin 15:39:01]# vim monitor.sh
[root@server bin 15:42:52]# cat monitor.sh
#!/bin/bash
#true 一直循环
while true
do
systemctl is-active sshd &>/dev/null
return_count=$?
#查验sshd服务是否运行
if((return_count!=0));then
systemctl start sshd
else
#间隔三秒
sleep 3
fi
done
[root@server bin 15:42:05]# systemctl status sshd
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since 五 2025-10-10 14:35:01 CST; 1h 7min ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 3561 (sshd)
CGroup: /system.slice/sshd.service
└─3561 /usr/sbin/sshd -D
... ...
#脚本放在后台
[root@server bin 15:42:15]# bash monitor.sh &
[1] 3709
[root@server bin 15:42:31]# ps
PID TTY TIME CMD
3593 pts/1 00:00:00 bash
3709 pts/1 00:00:00 bash
3713 pts/1 00:00:00 sleep
3714 pts/1 00:00:00 ps
#关闭
[root@server bin 15:42:34]# systemctl stop sshd
[root@server bin 15:42:47]# systemctl status sshd
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
Active: active (running) since 五 2025-10-10 15:42:49 CST; 3s ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 3738 (sshd)
CGroup: /system.slice/sshd.service
└─3738 /usr/sbin/sshd -D
... ...
示例3:
猴子吃桃。
- 猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。
- 第二天早上又将第一天剩下的桃子吃掉一半,又多吃了一个。
- 以后每天早上都吃了前一天剩下的一半零一个。
- 到第 10 天早上想再吃时,发现只剩下一个桃子了。
问:一共有多少个桃子?
while 循环9次
[root@server bin 15:45:38]# vim peach.sh
[root@server bin 16:02:45]# cat peach.sh
#!/bin/bash
peach=1
i=1
day=$1
while((i<$1))
do
peach=$[ (peach+1)*2 ]
let i++
#((i++))
#i=$[ i+1 ]
echo "第$i 天peach: $peach"
done
[root@server bin 16:02:49]# bash peach.sh 5
第2 天peach: 4
第3 天peach: 10
第4 天peach: 22
第5 天peach: 46
[root@server bin 16:03:13]# bash peach.sh 10
第2 天peach: 4
第3 天peach: 10
第4 天peach: 22
第5 天peach: 46
第6 天peach: 94
第7 天peach: 190
第8 天peach: 382
第9 天peach: 766
第10 天peach: 1534
until 循环语句
until 循环语句的语法为:
until <条件表达式>
do
指令...
done
until 循环语句的用法与while 循环语句的用法类似,区别是until会在条件表达式不成立时,进入循环执行指令;条件表达式成立时,终止循环。
为了便于大家记忆,下面是某女生写的until条件语句的中文形象描述:
# 如果男朋友不努力工作,就不继续相处
until 男朋友不努力工作
do
继续相处
done
until 循环执行流程对应的逻辑图如下:
示例:
[root@server bin 17:04:16]# vim print_num.sh
[root@server bin 17:04:40]# cat print_num.sh
#1/bin/bash
i=5
until ((i==10))
do
let i++
echo $i
done
[root@server bin 17:04:35]# bash print_num.sh
5
6
7
8
9
10
for循环语句的应用实践
for循环语句和while循环语句类似,但for循环语句主要用于执行次数有限的循环,而不是用于守护进程及无限循环。for循环语句常见的语法有两种,下面将在不同的语法中对for循环语句进行详尽的讲解。
for 循环语法结构
变量取值型
第一种for循环语句语法结构如下:
for 变量名 in 变量取值列表
do
指令...
done
在此结构中“in 变量取值列表”可以省略,省略时相当于in "$@"
,也就是使用 for i
就相当于使用for i in "$@"
。
这种for循环语句执行过程如下:
- for关键字后面会有一个“变量名”,变量名依次获取in关键字后面的变量取值列表内容(以空格分隔)。
- 每次仅取一个,然后进入循环(do和done之间的部分)执行循环内的所有指令,当执行到done时结束本次循环。
- 之后,“变量名”再继续获取变量列表里的下一个变量值,继续执行循环内的所有指令,当执行到done时结束返回。
- 以此类推,直到取完变量列表里的最后一个值并进入循环执行到done结束为止。
C 语言型
第二种for循环语句称为C 语言型for循环,语法结构如下:
for ((expl;exp2;exp3))
do
指令...
done
此种循环语句和while循环语句类似,但语法结构比while循环更规范、工整。
这种for循环语句说明如下:
-
for关键字后的双括号内是三个表达式:
-
第一个是变量初始化(例如:i=0)
-
第二个为变量的范围(例如:i<100)
-
第三个为变量自增或自减(例如:i++)。
-
-
当第一个表达式的初始值符合第二个变量的范围时,就进入循环执行;条件不满足时就退出循环。
for循环结构执行流程对应的逻辑图如下:
for 循环语句的基础实践
示例1:竖向升序打印
[root@server bin 16:03:15]# vim for1.sh
[root@server bin 16:18:10]# cat for1.sh
#!/bin/bash
for i in {1..5}
do
echo $i
done
[root@server bin 16:18:14]# bash for1.sh
1
2
3
4
5
[root@server bin 16:18:19]# vim for2.sh
[root@server bin 16:19:19]# cat for2.sh
#!/bin/bash
for i in $(seq 5)
do
echo $i
done
[root@server bin 16:19:26]# bash for2.sh
1
2
3
4
5
[root@server bin 16:21:08]# vim for3.sh
[root@server bin 16:21:36]# cat for3.sh
#!/bin/bash
for ((i=1;i<=5;i++))
do
echo $i
done
[root@server bin 16:21:39]# bash for3.sh
1
2
3
4
5
示例2:竖向降序打印
[root@server bin 16:21:42]# vim for4.sh
[root@server bin 16:25:37]# cat for4.sh
#!/bin/bash
for i in {5..1}
do
echo $i
done
[root@server bin 16:25:41]# bash for4.sh
5
4
3
2
1
示例3:求和、求乘积
[root@server bin 16:25:44]# vim sum.sh
[root@server bin 16:42:55]# cat sum.sh
#!/bin/bash
sum=0
for i in {1..10}
do
sum=$[ sum+i ]
done
echo $(seq -s '+' 10)=$sum
[root@server bin 16:42:59]# bash sum.sh
1+2+3+4+5+6+7+8+9+10=55
[root@server bin 16:45:27]# vim c.sh
[root@server bin 16:45:41]# cat c.sh
#!/bin/bash
sum=1
for i in {1..10}
do
sum=$[ sum*i ]
done
echo $(seq -s '*' 10)=$sum
[root@server bin 16:45:44]# bash c.sh
1*2*3*4*5*6*7*8*9*10=3628800
示例4:求水鲜花数
水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身。
[root@server bin 16:52:11]# vim waterflowers.sh
[root@server bin 16:52:35]# cat waterflowers.sh
#!/bin/bash
for num in {100..999}
do
n1=$[num/100]
n2=$[num%100/10]
n3=$[num%10]
if [ $[n1**3 + n2**3 + n3**3 ] -eq $num ] ;then
echo "$n1^3 + $n2^3 + $n3^3 = $n1$n2$n3"
fi
done
[root@server bin 16:52:32]# bash waterflowers.sh
1^3 + 5^3 + 3^3 = 153
3^3 + 7^3 + 0^3 = 370
3^3 + 7^3 + 1^3 = 371
4^3 + 0^3 + 7^3 = 407
示例5:九九乘法表
[root@server bin 16:55:36]# vim 99.sh
[root@server bin 16:56:06]# cat 99.sh
#!/bin/bash
for ((i=1;i<=9;i++))
do
for((j=1;j<=i;j++))
do
echo -en "$i*$j=$[i*j]\t"
done
echo
done
[root@server bin 16:56:15]# bash 99.sh
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81