script 是"脚本、剧本"的意思。Shell Script 就是利用 Shell 的功能所写的一个程序,可以简单地理解为一个批处理文件。它由 Shell 命令和工具命令组成,不需要编译,直接解释执行。
Shell Script 和编程语言类似,有变量、条件判断、循环等要素,但它是面向命令行的,擅长处理文件操作、系统管理和自动化任务。
在编写 Shell Script 时,需要注意以下规则:
1. 命令从上而下、从左而右执行,多个空格会被忽略
2. 空白行和 Tab 键产生的空白都会被忽略
3. 读取到 Enter 符号(CR)就开始执行该行命令
4. 一行太长可用 \ 延伸到下一行
5. # 后面的内容都是注释,会被忽略
三、第一个 Shell Script(Hello World)
先创建存放脚本的目录,然后用 vim 创建第一个脚本文件:
[root@Server01 ~]# cd; mkdir /root/scripts; cd /root/scripts
[root@Server01 scripts]# vim sh01.sh
#!/bin/bash
# Program:
# This program shows "Hello World!" in your screen.
# History:
# 2021/08/23 Bobby First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0
脚本结构分析:
第1行#!/bin/bash — 声明使用 bash 解释器
第2-5行 — 注释区,描述程序功能、版本、历史记录
第6-7行 — 声明主要环境变量 PATH
第8行 — 主程序,输出 Hello World
第9行 — exit 0 退出并回传 0 给系统
执行方法有两种:
方法1(直接执行):需要脚本有可读可执行权限(rx)
绝对路径:/home/dmtsai/shell.sh
相对路径:./shell.sh
方法2(bash执行):bash shell.sh 或 sh shell.sh
建议养成良好习惯:在每个脚本开头写上功能说明、版本信息、作者、版权声明、历史记录,并对特殊命令使用绝对路径。
read 命令可以让脚本接收用户输入。来看一个例子:让用户输入姓名,然后输出全名。
#!/bin/bash
read -p "Please input your first name: " firstname
read -p "Please input your last name: " lastname
echo -e "\nYour full name is: $firstname $lastname"
知识点:read -p 中的 -p 参数表示在等待输入的同时显示提示文字。变量引用时使用 $变量名 的格式。
bash shell 默认只支持整数运算。推荐使用 $(( 计算式 )) 的格式:
read -p "first number: " firstnu
read -p "second number: " secnu
total=$(($firstnu * $secnu))
echo -e "\nThe result is ==> $total"
建议:数值运算统一使用 var=$((运算内容)),清晰可靠。
这是一个非常重要但容易被忽视的知识点:
直接执行(sh script.sh):系统会开启一个子进程 bash来运行脚本。脚本中的变量在子进程中有效,脚本结束后子进程销毁,变量不会传回父进程。
source 执行(source script.sh):脚本在当前父进程中运行,所有变量和操作都会在原来的 bash 环境中生效。
验证示例:
[root@Server01 scripts]# echo $firstname $lastname
# 输出为空,变量不存在
[root@Server01 scripts]# sh sh02.sh
Please input your first name: Bobby
Please input your last name: Yang
Your full name is: Bobby Yang
[root@Server01 scripts]# echo $firstname $lastname
# 仍然为空!变量没有传回
[root@Server01 scripts]# source sh02.sh
Your full name is: Bobby Yang
[root@Server01 scripts]# echo $firstname $lastname
Bobby Yang # 变量还在!
test 命令用于检测文件是否存在或判断其属性,是条件判断的基础。
[root@Server01 scripts]# test -e /dmtsai && echo "exist" || echo "Not exist"
Not exist
test 常用选项(文件类型判断):
除了 test 命令外,还可以用中括号 [ ] 来进行条件判断。例如判断 HOME 变量是否为空:
[root@Server01 scripts]# [ -z "$HOME" ]; echo $?
注意三个要点:
1. 中括号内每个组件之间都必须有空格
2. 中括号内的变量最好用双引号括起来
3. 中括号内的常数最好用单引号或双引号括起来
实战案例:Y/N 选择判断
read -p "Please input (Y/N): " yn
[ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue" && exit 0
[ "$yn" == "N" -o "$yn" == "n" ] && echo "Oh, interrupt!" && exit 0
echo "I don't know what your choice is" && exit 0
其中 -o 表示"或"(or),-a 表示"且"(and),&& 表示前面命令成功才执行后面,|| 表示前面命令失败才执行后面。
1. 单层条件判断
if [ 条件判断式 ]; then
当条件成立时执行的命令
fi
2. 多重条件判断(if...elif...else)
if [ 条件判断式一 ]; then
条件一成立时执行的命令
elif [ 条件判断式二 ]; then
条件二成立时执行的命令
else
条件都不成立时执行的命令
fi
实战:根据参数 $1 判断输出
if [ "$1" == "hello" ]; then
echo "Hello, how are you ?"
elif [ "$1" == "" ]; then
echo "You MUST input parameters"
else
echo "Usage: $0 {hello}"
fi
$1 表示执行脚本时传入的第一个参数,$0 表示脚本文件名本身。
十、case...in...esac — 多分支匹配
当有多个固定选项需要判断时,case 比 if...elif 更简洁:
case $1 in
"hello")
echo "Hello, how are you ?"
;;
"")
echo "You MUST input parameters"
;;
*)
echo "Usage $0 {hello}"
;;
esac
语法要点:每个分支用 ;; 结束,最后的 *) 相当于通配符,匹配所有其他情况。esac 是 case 反过来写,表示结束。
case 中 $变量 的两种获取方式:
直接执行式:script.sh variable 直接用 $1 接收参数
互动式:通过 read 命令让用户输入
十一、循环 — while / until / for
Shell 中有三种循环:while(当条件为真时执行)、until(直到条件为真时停止)、for(已知次数的固定循环)。
# 一直提示直到用户输入 yes 或 YES
while [ "$yn" != "yes" -a "$yn" != "YES" ]
do
read -p "Please input yes/YES to stop: " yn
done
echo "OK! you input the correct answer."
s=0
i=0
while [ "$i" != "100" ]
do
i=$(($i+1))
s=$(($s+$i))
done
echo "The result is ==> $s"
运行结果:The result of '1+2+3+...+100' is ==> 5050
until 循环 — 条件为假时执行,直到条件为真
until 和 while 正好相反。上面的例子用 until 改写:
until [ "$yn" == "yes" -o "$yn" == "YES" ]
do
read -p "Please input yes/YES to stop: " yn
done
区别:while 是"条件为真时循环",until 是"条件为假时循环,直到条件为真"。上面两个例子效果相同,只是写法不同。
写法一:遍历列表
for animal in dog cat elephant
do
echo "There are ${animal}s... "
done
写法二:C语言风格(数值范围)
for (( i=1; i<=$nu; i=i+1 ))
do
s=$(($s+$i))
done
network="192.168.10"
for sitenu in $(seq 1 100)
do
ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1
if [ "$result" == 0 ]; then
echo "Server ${network}.${sitenu} is UP."
else
echo "Server ${network}.${sitenu} is DOWN."
fi
done
知识点:seq 1 100 生成从1到100的数字序列。&> /dev/null 将输出重定向到黑洞设备,不显示在屏幕上。
脚本出错时,用 sh 的参数来进行调试:
示例:用 -x 查看脚本执行过程
[root@Server01 scripts]# sh -x sh15.sh
+ PATH=/bin:/sbin:/usr/bin:...
+ export PATH
+ for animal in dog cat elephant
+ echo 'There are dogs... '
There are dogs...
+ for animal in dog cat elephant
+ echo 'There are cats... '
There are cats...
+ for animal in dog cat elephant
+ echo 'There are elephants... '
There are elephants...
-x 是最常用的调试参数,每条命令前有 + 号,执行结果紧随其后,非常直观。格式:sh [-nvx] script.sh
本文核心知识点回顾:
1. Shell Script 的 结构和编写规则(#!/bin/bash、注释、PATH声明)
2. 运行方式:sh 直接执行(子进程)vs source 执行(父进程)
3. 交互输入:read 命令接收用户输入
4. 数值运算:$((计算式)) 的格式
5. 条件判断:test 命令和 [ ] 判断符号(注意空格!)
6. if...then...elif...else 条件判断式
7. case...in...esac 多分支匹配
8. 循环:while(条件真时循环)、until(条件假时循环)、for(固定次数)
9. 调试:sh -x 是最实用的调试技巧
题目一:编写脚本,让用户输入一个数字,计算 1+2+3+...+该数字 的结果。
题目二:创建一个脚本 /root/batchusers,能从包含用户名列表的文件中批量创建用户,要求:
1. 接收一个参数(用户名列表文件)
2. 没有参数时提示 Usage: /root/batchusers
3. 文件不存在时提示 input file not found
4. 创建的登录 Shell 为 /bin/false
5. 设置默认密码为 123456
如果觉得有用,欢迎转发给需要的朋友
-- END --