Shell编程基础语法
Shell脚本的概念
将要执行的命令按顺序保存到一个文本文件
给该文件可执行权限,便可运行
可结合各种Shell控制语句以完成更复杂的操作
Shell脚本应用场景
重复性操作
批量事务处理
自动化运维
服务运行状态监控
定时任务执行
脚本基础结构
文件头声明
#!/bin/bash # Shebang声明解释器,声明脚本的为bash解析器执行
#!/usr/bin/python3 # 声明脚本为python3解析器执行
# 这是单行注释 # 注释说明
# Author: YourName # 脚本作者
# Date: 2023-08-01 # 创建日期
# Version: 1.0 # 版本号
<<COMMENT
这是
多行注释
COMMENT命名规则
- 脚本文件:
backup_web.sh(全小写+下划线) - 变量名:
MAX_RETRIES=5(全大写+下划线) - 函数名:
validate_input()(小写+下划线)
Shell脚本执行
| 执行方式 | 命令示例 | 特点说明 |
|---|---|---|
| 1. 解释器直接执行 | bash script.sh | 不需要执行权限,使用指定解释器 |
| 2. 相对路径执行 | ./script.sh | 需要可执行权限(chmod +x),依赖Shebang |
| 3. 绝对路径执行 | /home/user/script.sh | 需要可执行权限,明确路径 |
| 4. source命令执行 | source script.sh | 在当前Shell环境执行(会改变当前环境变量) |
| 5. 点号快捷方式 | . script.sh | 同source,POSIX标准写法 |
bash和source区别
使用bash执行脚本内export定义的变量终端不保留,使用source执行的终端会保留脚本内定义的变量。
| 执行方式 | 解释器环境 | 进程模型 | 变量作用域 |
|---|---|---|---|
bash script.sh | 新建子shell进程 | 子进程独立执行 | 变量不传递到父进程 |
source script.sh | 当前shell进程解释 | 直接在当前进程执行 | 变量永久生效 |
# 示例脚本 test.sh
#!/bin/bash
export VAR="test"
cd /tmp
# 方式1:使用bash执行
bash test.sh
echo $VAR # 输出为空(变量未保留)
pwd # 仍在原目录(目录未改变)
# 方式2:使用source执行
source test.sh
echo $VAR # 输出"test"
pwd # 显示/tmp(目录已改变)注意事项
环境污染风险:
source会修改当前shell环境,可能覆盖PATH等重要变量路径解析差异:
bash script.sh按照脚本内shebang选择解释器source强制使用当前shell解释器(如zsh/bash差异)
特殊符号处理:
bash# 在脚本中使用exit时的不同表现: bash test.sh # exit只会终止子进程 source test.sh # exit会导致当前终端退出!
权限管理
# 查看当前权限
ls -l script.sh
# 输出示例:-rw-r--r-- 1 user group 285 Aug 1 10:00 script.sh
# 添加执行权限
chmod u+x script.sh # 仅给所有者
chmod +x script.sh # 给所有用户
# 验证权限
ls -l script.sh
# 期望输出:-rwxr-xr-x 1 user group 285 Aug 1 10:00 script.sh重定向操作
重定向分类
| 类型 | 操作符 | 用途 |
|---|---|---|
| 重定向输入 | < | 从指定的文件读取数据(代替键盘输入) |
| 追加输入重定向 | << | 使用 Here Document 方式输入多行文本(常用于脚本内输入) |
| 重定向输出 | > | 将 标准输出(stdout) 保存到指定文件(覆盖原有内容) |
| 追加输出重定向 | >> | 将 标准输出(stdout) 追加到指定文件(不覆盖原有内容) |
| 标准错误输出重定向 | 2> | 将 标准错误(stderr) 保存到指定文件(覆盖原有内容) |
| 标准错误输出追加重定向 | 2>> | 将 标准错误(stderr) 追加到指定文件(不覆盖原有内容) |
| 混合输出重定向 | &> | 将 标准输出(stdout)和标准错误(stderr) 同时保存到同一文件(覆盖) |
| 混合输出追加重定向 | &>> | 将 标准输出(stdout)和标准错误(stderr) 同时追加到同一文件(不覆盖) |
> 和 >> 的区别
>会 覆盖 目标文件(如果文件已存在,内容会被清空)。>>会 追加 到目标文件(保留原有内容,新内容加在末尾)。
2> 和 2>> 的区别
2>用于 覆盖 错误日志文件。2>>用于 追加 错误日志文件
&> 和 &>>(Bash 4+ 支持)
&>同时重定向 stdout 和 stderr 并覆盖目标文件。&>>同时重定向 stdout 和 stderr 并追加到目标文件
<< 输入追加(Here Document)
cat << EOF
这是多行文本输入
直到遇到 "EOF" 结束
EOF
# 追加输入到文件中
cat >> filename << EOF
这是多行文本输入
直到遇到 "EOF" 结束
EOF/dev/null(丢弃输出)
command > /dev/null 2>&1 # 丢弃所有输出(静默执行)重定向标准
| 类型 | 设备文件 | 文件描述符编号 | 默认设备 |
|---|---|---|---|
| 标准输入 (stdin) | /dev/stdin | 0 | 键盘 |
| 标准输出 (stdout) | /dev/stdout | 1 | 显示器 |
| 标准错误输出 (stderr) | /dev/stderr | 2 | 显示器 |
重定向符号
>或1>:重定向标准输出2>:重定向标准错误&>:重定向标准输出和错误
重定向输入
< 标准输入
[root@localhost ~]# cat 1.txt
123456
123456
[root@localhost ~]# passwd tom < 1.txt
更改用户 tom 的密码 。
新的密码: 无效的密码: 密码少于 8 个字符
重新输入新的密码: passwd:所有的身份验证令牌已经成功更新。
# 临时禁用 SELinux
setenforce 0Shell 变量
概念
变量用来存放系统或用户需要使用的特定参数或者值,变量的值可以根据用户设定或者系统环境变化而相应变化,在Shell脚本中使用变量,可使脚本更加灵活,适应性更强。
变量命名规则
- 字母/数字/下划线组成
- 不能以数字开头
- 区分大小写(
var≠Var) - 避免使用Shell关键字(如
if,then等)
变量类型
| 类型 | 示例 | 说明 |
|---|---|---|
| 字符串 | str="hello" | 默认类型 |
| 整型 | num=100 | 实际仍为字符串 |
| 数组 | arr=(a b c) | 索引从0开始 |
| 只读变量 | readonly MAX=100 | 不可修改 |
| 环境变量 | export PATH=... | 可被子进程继承 |
变量分类
| 分类 | 描述 |
|---|---|
| 自定义变量 | 由用户自己定义、修改和使用 |
| 环境变量(系统变量) | 由系统维护,用于设置工作环境 |
| 预定义变量 | Bash中内置的一类变量,不能直接修改 |
PATH环境变量
用于设置可执行程序的默认搜索路径。可以修改全局变量文件/etc/profile 或修改某用户家目录下的~/.bash_profile文件永久改变环境变量。
| 变量名 | 描述 | 示例值 |
|---|---|---|
PATH | 可执行文件搜索路径 | /usr/bin:/bin:/usr/local/bin |
HOME | 当前用户家目录 | /home/username |
USER/LOGNAME | 当前用户名 | root |
SHELL | 默认Shell路径 | /bin/bash |
PWD | 当前工作目录(由cd命令自动更新) | /var/log |
OLDPWD | 上一次的工作目录 | /etc |
LANG | 系统语言/地区设置 | en_US.UTF-8 |
TERM | 终端类型 | xterm-256color |
EDITOR | 默认文本编辑器 | vim |
TMPDIR | 临时目录路径(默认为/tmp) | /tmp |
LD_LIBRARY_PATH | 动态链接库搜索路径 | /usr/local/lib |
Shell内置变量
| 变量名 | 描述 | 示例/说明 |
|---|---|---|
$0 | 当前脚本/命令的名称 | ./test.sh 或 bash |
$1-$9 | 脚本/函数的位置参数(第1到第9个参数) | 调用脚本时传递的参数 |
$# | 传递给脚本/函数的参数个数 | 若执行./a.sh a b,则$#=2 |
$@ | 所有位置参数的列表(每个参数作为独立字符串) | "$1" "$2" "$3"... |
$* | 所有位置参数的组合(单个字符串,默认用空格分隔) | "$1 $2 $3..." |
$? | 上一条命令的退出状态码(0表示成功) | 127(命令未找到) |
$$ | 当前Shell进程的PID | 12345 |
$! | 最后一个后台进程的PID | 12346 |
$_ | 上一个命令的最后一个参数 | 执行ls /etc后,$_=/etc |
$RANDOM | 0-32767的随机整数 | 17562 |
$SECONDS | 脚本已运行的秒数 | 10(脚本运行10秒后) |
$BASH | Bash二进制文件路径 | /bin/bash |
$BASH_VERSION | Bash版本信息 | 5.1.16(1)-release |
进程相关变量
| 变量名 | 描述 |
|---|---|
PPID | 父进程的PID |
UID/EUID | 当前用户的真实/有效用户ID(数字) |
GROUPS | 当前用户所属的组ID列表 |
HOSTNAME | 系统主机名 |
HOSTTYPE | 主机架构类型 |
MACHTYPE | 机器类型的全称 |
终端与会话变量
| 变量名 | 描述 |
|---|---|
COLUMNS | 终端宽度(列数) |
LINES | 终端高度(行数) |
DISPLAY | X11服务器的显示位置(GUI环境) |
SSH_CLIENT | SSH连接信息(格式:客户端IP 客户端端口 服务端端口) |
SESSION_MANAGER | 桌面会话管理器地址(如GNOME) |
注意事项
- 变量名区分大小写:
$PATH≠$path - 自定义变量不要覆盖系统变量(如
PATH、HOME等) - 敏感信息:避免在环境变量中存储密码(可用
read -s交互式输入) - 作用域:环境变量对子进程可见,普通Shell变量仅当前进程有效
变量作用域
| 作用域类型 | 声明方式 | 生命周期 | 可见范围 |
|---|---|---|---|
| 全局 | var=value | 脚本/Shell会话结束 | 整个脚本 |
| 局部 | local var | 函数/代码块结束 | 仅声明范围内 |
| 环境 | export var | 所有子进程结束 | 当前及子进程 |
| 只读 | readonly var | 同声明位置的作用域 | 同声明位置的作用域 |
全局变量 (Global Variables)
特点
- 默认声明方式
- 在整个脚本中可见
- 会被函数内部修改
示例
global_var="初始值"
function modify_global() {
global_var="被修改"
}
modify_global
echo $global_var # 输出"被修改"注意事项
- 在函数内未声明
local的变量会修改全局变量 - 子Shell中无法修改父Shell的全局变量
局部变量 (Local Variables)
特点
- 使用
local关键字声明 - 仅在函数/代码块内有效
- 不会影响外部同名变量
示例
var="全局值"
function test_local() {
local var="局部值"
echo "函数内: $var" # 输出"局部值"
}
test_local
echo "函数外: $var" # 输出"全局值"特殊形式
# 代码块局部变量(Bash 4.4+)
{
local - block_var="临时值"
echo $block_var # 有效
}
echo $block_var # 报错:未找到变量环境变量 (Environment Variables)
特点
- 使用
export声明 - 被子进程继承
- 常用于配置程序运行环境
示例
export DB_HOST="localhost"
# 子进程可见验证
bash -c 'echo $DB_HOST' # 输出"localhost"重要环境变量
| 变量 | 用途 |
|---|---|
| PATH | 命令搜索路径 |
| HOME | 用户家目录 |
| USER | 当前用户名 |
| PWD | 当前工作目录 |
| SHELL | 默认Shell程序路径 |
只读变量 (Readonly Variables)
特点
- 禁止修改
- 根据声明位置确定作用域
- 常用于定义常量
示例
readonly MAX_RETRIES=3
MAX_RETRIES=5 # 报错:只读变量
function setup() {
readonly LOCAL_CONST=1
}
setup
echo $LOCAL_CONST # 报错:局部只读变量外部不可见销毁变量
unset 变量名变量输入
read [-p "提示信息"] 变量名
# 使用案例
read -p "请输入您的姓名:" Name
read -p "请输入您的密码:" Password变量的算数运算
expr
变量的数值运算多用于脚本程序的过程控制,只能进行简单的整数运算,不支持小数运算,整数值的运算主要通过内部命令expr进行。
格式
expr 变量1 运算符 变量2 [运算符 变量3]……
# 求精度计算如小数计算
expr 变量1 运算符 变量2 | bc示例
[root@shell ~]# X=111
[root@shell ~]# Y=123
+(加):[root@shell ~]# expr $X + $Y
-(减):[root@shell ~]# expr $X - $Y
\*(乘):[root@shell ~]# expr $X \* $Y
/(除):[root@shell ~]# expr $X / $Y
%(取余):[root@shell ~]# expr $X % $Ylet
let 是 Bash 和其它现代 Shell 中用于执行算术运算的内置命令。它允许你直接在 Shell 脚本中进行数学计算,而无需调用外部程序。
主要特点
- 直接修改变量值:运算结果会自动赋值给变量
- 支持多种运算符:包括基本的算术、位运算和逻辑运算
- 不需要变量前缀:在表达式中可以直接使用变量名,不需要加
$ - 返回状态码:如果计算结果为0,返回1(假);非0则返回0(真)
使用示例
let "sum = 5 + 3" # sum=8
let "mod = 10 % 3" # mod=1
# 结果赋值
let run=let "sum = 5 + 3"
echo $run$(( ))
使用示例
# $(( ))需要显式赋值
a=$((5 + 3))浮点数计算
# 基本运算
echo "scale=2; 10 / 3" | bc # 输出 3.33
result=$(echo "scale=4; 5.5 * 2.1" | bc) # 变量赋值
# 使用Shell变量
a=3.14
b=1.57
echo "scale=2; $a + $b" | bc # 输出 4.71
# 数学函数(需-l选项)
echo "s(3.14159/2)" | bc -l # 计算sin(π/2)
# bc默认不四舍五入,需结合printf实现
printf "%.2f" $(echo "5.999 + 5.001" | bc) # 输出11.00(四舍五入)- scale:小数点精度控制
性能比较
| 方法 | 示例 | 特点 |
|---|---|---|
let | let "a=a+1" | 最快,内置命令 |
$(( )) | a=$((a+1)) | 几乎同let,更符合POSIX |
expr | a=$(expr $a + 1) | 最慢,外部程序 |
经典案例
系统信息收集脚本
#!/bin/bash
# 系统信息收集脚本
echo "====== System Report ======"
echo "Kernel: $(uname -r)"
echo "Arch: $(uname -m)"
echo "Distribution: $(source /etc/os-release && echo $PRETTY_NAME)"
echo "CPU Cores: $(nproc)"
echo "Memory: $(free -h | awk '/Mem/{print $2}')"
echo "Uptime: $(uptime -p)"字符串
双引号
能识别变量和优先执行。
a=12
echo "$a"
echo "$(a)"
# 结果输出
12
12单引号
强引用,不识别一切符号
a=12
echo '$a'
# 结果输出$a不加引号
所有的符号都可以识别。
a=12
echo $a
echo $(a)
# 结果输出
12
12字符串操作
默认值设置
echo ${undefined_var:-"默认值"} # 变量未定义时输出"默认值"变量长度
str="hello"
echo ${#str} # 输出5字符串截取
str="hello"
echo ${str:1:3} # 输出ell(从索引1开始取3个字符)数组
在Bash中,数组是存储多个元素的有序集合,支持索引访问和灵活操作。
数组声明与初始化
# 索引数组(默认)
fruits=("apple" "banana" "orange") # 自动索引0开始
declare -a colors=("red" "green") # 显式声明
# 关联数组(需Bash 4+)
declare -A user=([name]="Alice" [age]=25)元素访问与修改
echo ${fruits[1]} # 输出:banana(索引从0开始)
fruits[2]="kiwi" # 修改第三个元素
# 获取所有元素
echo "All items: ${fruits[@]}"常用操作
# 获取数组长度
echo ${#fruits[@]} # 输出元素个数
# 遍历数组
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# 添加元素
fruits+=("grape") # 尾部追加| 场景需求 | 推荐语法 |
|---|---|
| 遍历数组保留元素完整性 | "${array[@]}" |
| 需要拼接为单个字符串 | "${array[*]}" |
| 获取数组长度 | ${#array[@]} |
| 获取第N个元素 | "${array[N]}" |
注意事项
- 版本差异
- 关联数组需要Bash 4.0+(通过
bash --version确认) - 旧版Bash仅支持索引数组
- 关联数组需要Bash 4.0+(通过
if条件语句
Shell 条件判断主要通过 test 命令([ ] 语法)和 [[ ]] 关键字实现,用于文件测试、字符串比较和数值比较。结合 if/then/else 结构实现流程控制,是 Shell 脚本编程的基础逻辑组件。
语法格式
# 基本形式
if [ 条件表达式 ]; then
# 条件为真时执行的命令
elif [ 其他条件 ]; then
# 其他条件为真时执行的命令
else
# 所有条件都为假时执行的命令
fi
# 现代推荐写法
if [[ 条件表达式 ]]; then
# 条件为真时执行的命令
elif [[ 其他条件 ]]; then
# 其他条件为真时执行的命令
else
# 所有条件都为假时执行的命令
fi
# (())
if (( 条件表达式 )); then
# 条件为真时执行的命令
elif (( 其他条件 )); then
# 其他条件为真时执行的命令
else
# 所有条件都为假时执行的命令
fi三种条件测试方式
# 1. test命令形式
if test "$var" = "value"; then...
# 2. 方括号形式(最常用)
if [ "$var" -eq 10]; then...
# 3. 双方括号形式(支持更多特性)
if [[ $var == pattern ]]; then...
# 4.小括号模式
if (( "$var" == 10 )); then...文件测试
if [ -e "file" ]; then... # 文件/目录存在
if [ -f "file" ]; then... # 是普通文件
if [ -d "dir" ]; then... # 是目录
if [ -r "file" ]; then... # 可读
if [ -w "file" ]; then... # 可写
if [ -x "file" ]; then... # 可执行
if [ -s "file" ]; then... # 文件大小>0
if [ "file1" -nt "file2" ]; then... # file1比file2新
if [ "file1" -ot "file2" ]; then... # file1比file2旧| 测试符 | 说明 | 示例 |
|---|---|---|
-e file | 文件/目录存在 | [ -e /path ] |
-f file | 是普通文件 | [ -f file.txt ] |
-d file | 是目录 | [ -d /tmp ] |
-s file | 文件非空 | [ -s log.txt ] |
-r file | 可读 | [ -r config.cfg ] |
-w file | 可写 | [ -w data.txt ] |
-x file | 可执行 | [ -x script.sh ] |
-L file | 是符号链接 | [ -L /bin/sh ] |
-b file | 是块文件 | [ -b /dev/sr0 ] |
file1 -nt file2 | file1比file2新 | [ file1 -nt file2 ] |
file1 -ot file2 | file1比file2旧 | [ file1 -ot file2 ] |
数值比较
if [ "$a" -eq "$b" ]; then... # 等于 equal
if [ "$a" -ne "$b" ]; then... # 不等于 not equal
if [ "$a" -gt "$b" ]; then... # 大于 greater than
if [ "$a" -lt "$b" ]; then... # 小于 less than
if [ "$a" -ge "$b" ]; then... # 大于等于 greater or equal
if [ "$a" -le "$b" ]; then... # 小于等于 less or equal| 选项 | 描述 | 示例 |
|---|---|---|
num1 -eq num2 | 等于 | [ $n -eq 10 ] |
num1 -ne num2 | 不等于 | [[ $count -ne 0 ]] |
num1 -gt num2 | 大于 | [ $i -gt 5 ] |
num1 -ge num2 | 大于等于 | [[ $age -ge 18 ]] |
num1 -lt num2 | 小于 | [ $val -lt 100 ] |
num1 -le num2 | 小于等于 | [[ $x -le 10 ]] |
字符串比较
if [ "$str1" = "$str2" ]; then... # 等于
if [ "$str1" != "$str2" ]; then... # 不等于
if [ -z "$str" ]; then... # 空字符串
if [ -n "$str" ]; then... # 非空字符串
if [[ "$str" == *pattern* ]]; then... # 模式匹配(需双方括号)| 选项 | 描述 | 示例 |
|---|---|---|
-z str | 字符串为空 | [ -z "$var" ] |
-n str | 字符串非空 | [[ -n $name ]] |
str1 = str2 | 字符串相等 | [ "$a" = "$b" ] |
str1 != str2 | 字符串不等 | [[ $os != "Linux" ]] |
str1 < str2 | 字典序小于 | [[ "a" < "b" ]] |
逻辑运算符
# AND 逻辑与
if [ 条件1 ] && [ 条件2 ]; then...
if [[ 条件1 && 条件2 ]]; then... # 双方括号内写法
# OR 逻辑或
if [ 条件1 ] || [ 条件2 ]; then...
if [[ 条件1 || 条件2 ]]; then...
# NOT 逻辑非
if ! [ 条件 ]; then...
if [[ ! 条件 ]]; then...| 运算符 | 说明 | []示例 | [[]]示例 |
|---|---|---|---|
-a | 与 | [ $a -gt 1 -a $a -lt 5 ] | - |
-o | 或 | [ $x -eq 0 -o $y -eq 0 ] | - |
! | 非 | [ ! -f file ] | [[ ! $flag ]] |
&& | 与 | - | [[ $a > 1 && $a < 5 ]] |
| ` | ` | 或 |
经典案例
#!/bin/bash
# 文件备份检查
if [[ ! -d "/backup" ]]; then
mkdir -p /backup
echo "创建备份目录"
elif [[ ! -w "/backup" ]]; then
echo "错误:备份目录不可写" >&2
exit 1
fi
# 服务状态检查
if systemctl is-active --quiet nginx; then
echo "Nginx 正在运行"
else
systemctl start nginx
fi
# 多条件判断
read -p "输入分数 (0-100): " score
if [[ $score -ge 90 ]]; then
grade="A"
elif [[ $score -ge 80 ]]; then
grade="B"
elif [[ $score -ge 60 ]]; then
grade="C"
else
grade="D"
fi
echo "成绩等级: $grade"
# 正则表达式匹配(需双方括号)
if [[ "$var" =~ ^[0-9]+$ ]]; then
echo "变量只包含数字"
fi
# 算术比较(双括号形式)
if (( $a > $b )); then
echo "$a 大于 $b"
fi
# 命令返回值判断
if grep -q "pattern" file; then
echo "找到匹配内容"
fi注意事项
[ ]是命令,内部各元素必须用空格分隔,==仅在[[ ]]中支持模式匹配- 变量引用建议加双引号,如
[ -n "$var" ]防止空变量导致语法错误 - 算术比较推荐使用
(( )),如(( count > 10 )) [[ ]]支持&&、||逻辑运算符,[ ]需要用-a、-o连接- 检查命令是否存在应使用
command -v而非which
循环语句
for循环
语法结构
标准数值循环
for (( 初始值; 条件; 步进 )); do
# 循环体
done
# 示例:打印1-5
for (( i=1; i<=5; i++ )); do
echo "计数: $i"
done列表循环
for 变量 in 项目1 项目2 项目3; do
# 循环体
done
# 示例:处理多个文件
for file in *.txt; do
echo "处理文件: $file"
done用{}序列取值
for 变量 in {1..10}; do
# 循环体
done从命令取值
for 变量 in `命令`; do
# 循环体
done
# 案例
for i in `ls /root`; do
echo "$i"
done
for i in `seq $1`; do
useradd user$i
done循环控制技巧
中断与继续
# break示例:找到第一个匹配即退出
for user in $(cat /etc/passwd | cut -d: -f1); do
if id "$user" | grep -q admin; then
echo "首个管理员用户: $user"
break
fi
done
# continue示例:跳过特定项目
for num in {1..10}; do
if (( num % 2 == 0 )); then
continue # 跳过偶数
fi
echo "奇数: $num"
done嵌套循环
for dir in /etc /var /tmp; do
echo "扫描目录: $dir"
for file in "$dir"/*; do
if [[ -f "$file" ]]; then
echo " 发现文件: ${file##*/}"
fi
done
done经典案例
九九乘法表
for (( i=1;i<10;i++ ));do
for ((j=1;j<=$i;j++));do
echo -en "$i*$j=$((i*j)) \t"
done
echo ""
donewhile循环
重复测试某个条件,只要条件成立则反复执行
语法结构
while [ 条件判断 ]
do
# 循环体命令
done
#按行读取
# 注通过read命令每次读取一行文件,文件内容有多少行,while循环多少次
while read line
do
命令序列
done > filename
# 死循环
# 危险示范(无终止条件)
while true
do
echo "此循环将永远执行!"
done
# 安全做法应设置退出条件或超时机制:
timeout=60
while ((timeout-- > 0))
do
[ -f "/tmp/flag" ] && break
sleep 1
done经典案例
九九乘法表
#!/bin/bash
# while打印九九乘法表
i=1
while (( i<=9 ))
do
j=1
while (( j<=i ))
do
echo -ne "$i*$j=$(($i*$j)) \t"
let j++
done
echo ""
let i++
done批量创建用户
#!/bin/bash
# 批量添加用户
read -p "请输入用户名称前缀:" QZ
read -p "请输入创建用户个数:" NUM
read -p "请输入用户的失效时间(YYYY-MM-DD):" EXT
i=1
while [ $i -le $NUM ]
do
if [ ! -z $EXT ];then
useradd -e $EXT $QZ$i
PWD=`echo $RANDOM | md5sum |cut -c 1-10`
echo "$PWD" | passwd --stdin $QZ$i &>/dev/null
echo "用户名:$QZ$i ,密码:$PWD" >>userlist.txt
else
useradd $QZ$i
PWD=`echo $RANDOM | md5sum |cut -c 1-10`
echo "$PWD" | passwd --stdin $QZ$i &>/dev/null
echo "用户名:$QZ$i ,密码:$PWD" >>userlist.txt
fi
let i++
donecase语句
case是shell中强大的模式匹配工具,比多层if-elif更简洁高效,特别适合多分支条件判断。
语法结构
case $变量 in
模式1)
命令组1
;;
模式2)
命令组2
;;
*)
默认命令组
;;
esac经典案例
用户输入处理
#!/bin/bash
read -p "输入y/n: " choice
case "$choice" in
[Yy]|[Yy][Ee][Ss])
echo "你选择了是"
;;
[Nn]|[Nn][Oo])
echo "你选择了否"
;;
*)
echo "无效输入"
;;
esac服务控制脚本
#!/bin/bash
case "$1" in
start)
systemctl start nginx
echo "服务已启动"
;;
stop)
systemctl stop nginx
echo "服务已停止"
;;
restart)
systemctl restart nginx
;;
*)
echo "用法: $0 {start|stop|restart}"
exit 1
esac注意事项
- 模式匹配规则:
*)匹配任意情况(相当于default)- 模式支持通配符:
*?[] - 模式区分大小写(除非使用
[aA]等形式)
- 语法雷区:
- 每个分支必须以
;;结束(类似break) - 右括号
)后不能有空格 - 整个结构以
esac反向闭合
- 每个分支必须以
function 函数
在Shell脚本中,函数是可复用的代码块,用于封装特定功能。
语法格式
# 定义方式1
function_name() {
commands
}
# 定义方式2(兼容性更好)
function function_name {
commands
}参数传递
greet() {
echo "Hello, $1!" # $1表示第一个参数
}
greet "Alice" # 输出:Hello, Alice!返回值
# 通过return返回状态码(0-255)
is_even() {
if (( $1 % 2 == 0 )); then
return 0 # 成功
else
return 1 # 失败
fi
}
# 通过echo返回字符串值
get_date() {
echo $(date +%F)
}
today=$(get_date)经典案例
日志函数
#!/bin/bash
LOG_FILE="/var/log/myscript.log"
log() {
local message="$1"
local level="${2:-INFO}" # 默认日志级别
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" | tee -a $LOG_FILE
}
log "Starting backup process"
log "Error detected" "ERROR"注意事项
- 函数必须先定义后调用
- 使用
local声明局部变量避免污染全局空间 - 通过
$?获取return返回值,用$(func)捕获echo输出 - 函数内
exit会终止整个脚本
脚本程序控制
- exit: 退出整个脚本
- return : 退出的是整个函数
- break : 退出循环
- continue : 跳过本次循环
sh - 命令解释器
简介
sh 是 Unix/Linux 系统中最基础的 shell 解释器,全称为 Bourne Shell。作为系统默认的命令行解释器,它负责解析用户输入的命令并与操作系统内核交互。现代系统中通常将 sh 符号链接到兼容性更好的 shell(如 bash 或 dash)。
语法格式
sh [选项] [脚本文件] [参数]选项
| 选项 | 描述 | 示例 |
|---|---|---|
-c 命令 | 执行指定字符串命令 | sh -c "ls -l" |
-n | 检查脚本语法不执行 | sh -n test.sh |
-x | 显示执行的命令及参数 | sh -x script.sh |
-e | 命令失败立即退出 | sh -e backup.sh |
-v | 显示输入的命令行 | sh -v install.sh |
-u | 使用未定义变量时报错 | sh -u config.sh |
经典案例
# 1. 执行单行命令
sh -c "date +%F"
# 2. 调试脚本执行
sh -x /etc/init.d/nginx start
# 3. 带参数执行脚本
sh backup.sh /data /backup
# 4. 检查脚本语法
sh -n deploy.sh && echo "语法检查通过"
# 5. 安全模式运行
sh -eu cleanup.sh注意事项
- 现代系统中
/bin/sh通常是符号链接,实际可能是bash、dash等兼容 shell - 使用
-e选项可避免错误继续执行,适合关键任务脚本 -x调试输出会显示变量展开后的实际值- 不同系统的 sh 实现可能有细微差异,重要脚本应指定完整路径
- 避免使用未定义的变量,建议配合
-u选项使用
