Skip to content

Shell编程基础语法

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进程解释直接在当前进程执行变量永久生效
bash
# 示例脚本 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会导致当前终端退出!

权限管理

bash
# 查看当前权限
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)

shell
cat << EOF
这是多行文本输入
直到遇到 "EOF" 结束
EOF

# 追加输入到文件中
cat >> filename << EOF
这是多行文本输入
直到遇到 "EOF" 结束
EOF

/dev/null(丢弃输出)

shell
command > /dev/null 2>&1  # 丢弃所有输出(静默执行)

重定向标准

类型设备文件文件描述符编号默认设备
标准输入 (stdin)/dev/stdin0键盘
标准输出 (stdout)/dev/stdout1显示器
标准错误输出 (stderr)/dev/stderr2显示器

重定向符号

  • >1> :重定向标准输出
  • 2> :重定向标准错误
  • &> :重定向标准输出和错误

重定向输入

< 标准输入

bash
[root@localhost ~]# cat 1.txt 
123456
123456
[root@localhost ~]# passwd tom < 1.txt 
更改用户 tom 的密码
新的密码: 无效的密码: 密码少于 8 个字符
重新输入新的密码: passwd:所有的身份验证令牌已经成功更新。
# 临时禁用 SELinux
setenforce 0

Shell 变量

概念

变量用来存放系统或用户需要使用的特定参数或者值,变量的值可以根据用户设定或者系统环境变化而相应变化,在Shell脚本中使用变量,可使脚本更加灵活,适应性更强。

变量命名规则

  • 字母/数字/下划线组成
  • 不能以数字开头
  • 区分大小写(varVar
  • 避免使用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.shbash
$1-$9脚本/函数的位置参数(第1到第9个参数)调用脚本时传递的参数
$#传递给脚本/函数的参数个数若执行./a.sh a b,则$#=2
$@所有位置参数的列表(每个参数作为独立字符串)"$1" "$2" "$3"...
$*所有位置参数的组合(单个字符串,默认用空格分隔)"$1 $2 $3..."
$?上一条命令的退出状态码(0表示成功)127(命令未找到)
$$当前Shell进程的PID12345
$!最后一个后台进程的PID12346
$_上一个命令的最后一个参数执行ls /etc后,$_=/etc
$RANDOM0-32767的随机整数17562
$SECONDS脚本已运行的秒数10(脚本运行10秒后)
$BASHBash二进制文件路径/bin/bash
$BASH_VERSIONBash版本信息5.1.16(1)-release

进程相关变量

变量名描述
PPID父进程的PID
UID/EUID当前用户的真实/有效用户ID(数字)
GROUPS当前用户所属的组ID列表
HOSTNAME系统主机名
HOSTTYPE主机架构类型
MACHTYPE机器类型的全称

终端与会话变量

变量名描述
COLUMNS终端宽度(列数)
LINES终端高度(行数)
DISPLAYX11服务器的显示位置(GUI环境)
SSH_CLIENTSSH连接信息(格式:客户端IP 客户端端口 服务端端口
SESSION_MANAGER桌面会话管理器地址(如GNOME)

注意事项

  1. 变量名区分大小写$PATH$path
  2. 自定义变量不要覆盖系统变量(如PATHHOME等)
  3. 敏感信息:避免在环境变量中存储密码(可用read -s交互式输入)
  4. 作用域:环境变量对子进程可见,普通Shell变量仅当前进程有效

变量作用域

作用域类型声明方式生命周期可见范围
全局var=value脚本/Shell会话结束整个脚本
局部local var函数/代码块结束仅声明范围内
环境export var所有子进程结束当前及子进程
只读readonly var同声明位置的作用域同声明位置的作用域

全局变量 (Global Variables)

特点

  • 默认声明方式
  • 在整个脚本中可见
  • 会被函数内部修改

示例

shell
global_var="初始值"

function modify_global() {
    global_var="被修改"
}

modify_global
echo $global_var  # 输出"被修改"

注意事项

  1. 在函数内未声明local的变量会修改全局变量
  2. 子Shell中无法修改父Shell的全局变量

局部变量 (Local Variables)

特点

  • 使用local关键字声明
  • 仅在函数/代码块内有效
  • 不会影响外部同名变量

示例

shell
var="全局值"

function test_local() {
    local var="局部值"
    echo "函数内: $var"  # 输出"局部值"
}

test_local
echo "函数外: $var"  # 输出"全局值"

特殊形式

shell
# 代码块局部变量(Bash 4.4+)
{
    local - block_var="临时值"
    echo $block_var  # 有效
}
echo $block_var  # 报错:未找到变量

环境变量 (Environment Variables)

特点

  • 使用export声明
  • 被子进程继承
  • 常用于配置程序运行环境

示例

shell
export DB_HOST="localhost"

# 子进程可见验证
bash -c 'echo $DB_HOST'  # 输出"localhost"

重要环境变量

变量用途
PATH命令搜索路径
HOME用户家目录
USER当前用户名
PWD当前工作目录
SHELL默认Shell程序路径

只读变量 (Readonly Variables)

特点

  • 禁止修改
  • 根据声明位置确定作用域
  • 常用于定义常量

示例

shell
readonly MAX_RETRIES=3
MAX_RETRIES=5  # 报错:只读变量

function setup() {
    readonly LOCAL_CONST=1
}
setup
echo $LOCAL_CONST  # 报错:局部只读变量外部不可见

销毁变量

shell
unset 变量名

变量输入

shell
read [-p "提示信息"] 变量名

# 使用案例
read -p "请输入您的姓名:" Name
read -p "请输入您的密码:" Password

变量的算数运算

expr

变量的数值运算多用于脚本程序的过程控制,只能进行简单的整数运算,不支持小数运算,整数值的运算主要通过内部命令expr进行。

格式

shell
expr 变量1 运算符 变量2 [运算符 变量3]……

# 求精度计算如小数计算
expr 变量1 运算符 变量2 | bc

示例

bash
[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 % $Y

let

let 是 Bash 和其它现代 Shell 中用于执行算术运算的内置命令。它允许你直接在 Shell 脚本中进行数学计算,而无需调用外部程序。

主要特点

  1. 直接修改变量值:运算结果会自动赋值给变量
  2. 支持多种运算符:包括基本的算术、位运算和逻辑运算
  3. 不需要变量前缀:在表达式中可以直接使用变量名,不需要加$
  4. 返回状态码:如果计算结果为0,返回1(假);非0则返回0(真)

使用示例

bash
let "sum = 5 + 3"  # sum=8
let "mod = 10 % 3" # mod=1
# 结果赋值
let run=let "sum = 5 + 3"
echo $run

$(( ))

使用示例

shell
# $(( ))需要显式赋值
a=$((5 + 3))

浮点数计算

bash
# 基本运算
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:小数点精度控制

性能比较

方法示例特点
letlet "a=a+1"最快,内置命令
$(( ))a=$((a+1))几乎同let,更符合POSIX
expra=$(expr $a + 1)最慢,外部程序

经典案例

系统信息收集脚本

shell
#!/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)"

字符串

双引号

能识别变量和优先执行。

shell
a=12
echo "$a"
echo "$(a)"

# 结果输出
12
12

单引号

强引用,不识别一切符号

shell
a=12
echo '$a'
# 结果输出$a

不加引号

所有的符号都可以识别。

shell
a=12

echo $a
echo $(a)

# 结果输出
12
12

字符串操作

默认值设置

shell
echo ${undefined_var:-"默认值"}  # 变量未定义时输出"默认值"

变量长度

shell
str="hello"
echo ${#str}  # 输出5

字符串截取

shell
str="hello"
echo ${str:1:3}  # 输出ell(从索引1开始取3个字符)

数组

在Bash中,数组是存储多个元素的有序集合,支持索引访问和灵活操作。

数组声明与初始化

shell
# 索引数组(默认)
fruits=("apple" "banana" "orange")  # 自动索引0开始
declare -a colors=("red" "green")   # 显式声明

# 关联数组(需Bash 4+)
declare -A user=([name]="Alice" [age]=25)

元素访问与修改

shell
echo ${fruits[1]}    # 输出:banana(索引从0开始)
fruits[2]="kiwi"      # 修改第三个元素

# 获取所有元素
echo "All items: ${fruits[@]}"

常用操作

shell
# 获取数组长度
echo ${#fruits[@]}    # 输出元素个数

# 遍历数组
for fruit in "${fruits[@]}"; do
    echo "Fruit: $fruit"
done

# 添加元素
fruits+=("grape")     # 尾部追加
场景需求推荐语法
遍历数组保留元素完整性"${array[@]}"
需要拼接为单个字符串"${array[*]}"
获取数组长度${#array[@]}
获取第N个元素"${array[N]}"

注意事项

  1. 版本差异
    • 关联数组需要Bash 4.0+(通过bash --version确认)
    • 旧版Bash仅支持索引数组

if条件语句

Shell 条件判断主要通过 test 命令([ ] 语法)和 [[ ]] 关键字实现,用于文件测试、字符串比较和数值比较。结合 if/then/else 结构实现流程控制,是 Shell 脚本编程的基础逻辑组件。

语法格式

shell
# 基本形式
if [ 条件表达式 ]; then
    # 条件为真时执行的命令
elif [ 其他条件 ]; then
    # 其他条件为真时执行的命令
else
    # 所有条件都为假时执行的命令
fi

# 现代推荐写法
if [[ 条件表达式 ]]; then
    # 条件为真时执行的命令
elif [[ 其他条件 ]]; then
    # 其他条件为真时执行的命令
else
    # 所有条件都为假时执行的命令
fi

# (())
if (( 条件表达式 )); then
    # 条件为真时执行的命令
elif (( 其他条件 )); then
    # 其他条件为真时执行的命令
else
    # 所有条件都为假时执行的命令
fi

三种条件测试方式

shell
# 1. test命令形式
if test "$var" = "value"; then...

# 2. 方括号形式(最常用)
if [ "$var" -eq 10]; then...

# 3. 双方括号形式(支持更多特性)
if [[ $var == pattern ]]; then...

# 4.小括号模式
if (( "$var" == 10 )); then...

文件测试

shell
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 file2file1比file2新[ file1 -nt file2 ]
file1 -ot file2file1比file2旧[ file1 -ot file2 ]

数值比较

shell
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 ]]

字符串比较

shell
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" ]]

逻辑运算符

bash
# 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 ]]
``

经典案例

shell
#!/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

注意事项

  1. [ ] 是命令,内部各元素必须用空格分隔,== 仅在 [[ ]] 中支持模式匹配
  2. 变量引用建议加双引号,如 [ -n "$var" ] 防止空变量导致语法错误
  3. 算术比较推荐使用 (( )),如 (( count > 10 ))
  4. [[ ]] 支持 &&|| 逻辑运算符,[ ] 需要用 -a-o 连接
  5. 检查命令是否存在应使用 command -v 而非 which

循环语句

for循环

语法结构

标准数值循环

shell
for (( 初始值; 条件; 步进 )); do
    # 循环体
done

# 示例:打印1-5
for (( i=1; i<=5; i++ )); do
    echo "计数: $i"
done

列表循环

shell
for 变量 in 项目1 项目2 项目3; do
    # 循环体
done

# 示例:处理多个文件
for file in *.txt; do
    echo "处理文件: $file"
done

用{}序列取值

shell
for 变量 in {1..10}; do
    # 循环体
done

从命令取值

shell
for 变量 in `命令`; do
    # 循环体
done


# 案例
for i in `ls /root`; do
    echo "$i"
done

for i in `seq $1`; do
	useradd user$i
done

循环控制技巧

中断与继续

shell
# 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

嵌套循环

shell
for dir in /etc /var /tmp; do
    echo "扫描目录: $dir"
    for file in "$dir"/*; do
        if [[ -f "$file" ]]; then
            echo "  发现文件: ${file##*/}"
        fi
    done
done

经典案例

九九乘法表

shell
for (( i=1;i<10;i++ ));do
    for ((j=1;j<=$i;j++));do
	    echo -en "$i*$j=$((i*j)) \t"
    done
    echo ""
done

while循环

重复测试某个条件,只要条件成立则反复执行

语法结构

shell
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

经典案例

九九乘法表

shell
#!/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

批量创建用户

shell
#!/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++
done

case语句

case是shell中强大的模式匹配工具,比多层if-elif更简洁高效,特别适合多分支条件判断。

语法结构

shell
case $变量 in
  模式1)
    命令组1
    ;;
  模式2)
    命令组2
    ;;
  *)
    默认命令组
    ;;
esac

经典案例

用户输入处理

shell
#!/bin/bash

read -p "输入y/n: " choice
case "$choice" in
  [Yy]|[Yy][Ee][Ss])
    echo "你选择了是"
    ;;
  [Nn]|[Nn][Oo])
    echo "你选择了否"
    ;;
  *)
    echo "无效输入"
    ;;
esac

服务控制脚本

shell
#!/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

注意事项

  1. 模式匹配规则
    • *) 匹配任意情况(相当于default)
    • 模式支持通配符:* ? []
    • 模式区分大小写(除非使用[aA]等形式)
  2. 语法雷区
    • 每个分支必须以;;结束(类似break)
    • 右括号)后不能有空格
    • 整个结构以esac反向闭合

function 函数

在Shell脚本中,函数是可复用的代码块,用于封装特定功能。

语法格式

shell
# 定义方式1
function_name() {
    commands
}

# 定义方式2(兼容性更好)
function function_name {
    commands
}

参数传递

shell
greet() {
    echo "Hello, $1!"  # $1表示第一个参数
}
greet "Alice"  # 输出:Hello, Alice!

返回值

shell
# 通过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)

经典案例

日志函数

shell
#!/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"

注意事项

  1. 函数必须先定义后调用
  2. 使用local声明局部变量避免污染全局空间
  3. 通过$?获取return返回值,用$(func)捕获echo输出
  4. 函数内exit会终止整个脚本

脚本程序控制

  • exit: 退出整个脚本
  • return : 退出的是整个函数
  • break : 退出循环
  • continue : 跳过本次循环

sh - 命令解释器

简介

sh 是 Unix/Linux 系统中最基础的 shell 解释器,全称为 Bourne Shell。作为系统默认的命令行解释器,它负责解析用户输入的命令并与操作系统内核交互。现代系统中通常将 sh 符号链接到兼容性更好的 shell(如 bash 或 dash)。

语法格式

bash
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

经典案例

bash
# 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

注意事项

  1. 现代系统中 /bin/sh 通常是符号链接,实际可能是 bashdash 等兼容 shell
  2. 使用 -e 选项可避免错误继续执行,适合关键任务脚本
  3. -x 调试输出会显示变量展开后的实际值
  4. 不同系统的 sh 实现可能有细微差异,重要脚本应指定完整路径
  5. 避免使用未定义的变量,建议配合 -u 选项使用