张芷铭的个人博客

bash脚本 传参

Bash 脚本接受参数的写法主要分 3 种场景(从简单到灵活),结合实际需求(如传递普通参数、带选项的参数、可选参数)逐步讲解,每个场景都附完整示例,方便直接套用。

一、基础场景:位置参数(最简单,无选项)

适合参数数量固定、顺序明确的场景(如直接传递 1-2 个路径/数值),通过 $1$2… 直接获取参数($0 是脚本本身名称)。

语法规则

变量含义
$0脚本文件名(如 test.sh
$1第 1 个参数
$2第 2 个参数
$#参数总个数
$*所有参数(作为单个字符串)
$@所有参数(作为独立字符串)
$?上一条命令的执行结果(0=成功)

示例脚本(pos_param.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# 位置参数示例:传递 输入路径、进程数

# 检查参数数量(可选,避免参数缺失)
if [ $# -ne 2 ]; then
    echo "用法:bash $0 <输入路径> <进程数>"
    echo "示例:bash $0 ./test_data 2"
    exit 1  # 退出脚本,状态码 1 表示错误
fi

# 读取参数
input_path=$1  # 第 1 个参数:输入路径
mlp_gpu=$2     # 第 2 个参数:进程数

# 执行逻辑
echo "开始处理任务..."
echo "输入路径:$input_path"
echo "进程数:$mlp_gpu"
# 后续可执行命令(如 python main.py --input $input_path --mlp-gpu $mlp_gpu)

运行方式

1
2
3
4
5
# 正确用法(2 个参数,顺序固定)
bash pos_param.sh ./test_data 2

# 错误用法(参数数量不对)
bash pos_param.sh ./test_data  # 会提示用法并退出

二、常用场景:带选项的参数(最灵活,推荐)

适合参数数量不固定、需要指定选项的场景(如 --input--gpu),通过 while 循环解析参数,支持任意顺序、可选参数,和你之前改造的脚本逻辑一致。

核心语法

while [[ $# -gt 0 ]] 循环遍历所有参数,通过 case 匹配选项:

  • 带值的选项(如 --input ./data):匹配选项后,用 $2 获取值,再 shift 2 跳过这两个参数;
  • 开关选项(如 --clear-queue):无需值,匹配后 shift 1 跳过当前参数;
  • 其他参数(如额外的 --batch-size 32):直接收集,透传给后续命令。

示例脚本(opt_param.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/bin/bash
# 带选项的参数示例:支持 --input、--mlp-gpu、--redis-host 等,可任意顺序

# 1. 设置默认值(可选,避免参数缺失时无默认值)
input_path="./default_data"  # 默认输入路径
mlp_gpu=1                    # 默认进程数
redis_host="10.178.148.233"  # 默认 Redis 地址
clear_queue=0                # 开关选项:0=不清除队列(默认),1=清除

# 2. 解析参数
while [[ $# -gt 0 ]]; do
    case "$1" in
        # 带值的选项:--input 后接路径
        --input)
            input_path="$2"  # 获取选项后的值($2)
            shift 2          # 跳过 $1(--input)和 $2(路径)
            ;;
        # 带值的选项:--mlp-gpu 后接数字
        --mlp-gpu)
            mlp_gpu="$2"
            shift 2
            ;;
        # 带值的选项:--redis-host 后接地址
        --redis-host)
            redis_host="$2"
            shift 2
            ;;
        # 开关选项:--clear-queue 无需值,触发时设为 1
        --clear-queue)
            clear_queue=1
            shift 1  # 仅跳过 $1(--clear-queue)
            ;;
        # 帮助选项:--help 显示用法
        --help)
            echo "用法:bash $0 [选项]"
            echo "选项:"
            echo "  --input <路径>        输入待处理路径(默认:$input_path)"
            echo "  --mlp-gpu <数字>      进程数(默认:$mlp_gpu)"
            echo "  --redis-host <地址>   Redis 地址(默认:$redis_host)"
            echo "  --clear-queue         清除队列(默认:不清除)"
            echo "  --help                显示帮助"
            exit 0
            ;;
        # 未知选项:提示错误并退出
        *)
            echo "错误:未知选项 $1"
            echo "使用 --help 查看支持的选项"
            exit 1
            ;;
    esac
done

# 3. 执行逻辑
echo "========================================"
echo "任务配置:"
echo "输入路径:$input_path"
echo "进程数:$mlp_gpu"
echo "Redis 地址:$redis_host"
echo "是否清除队列:$(if [ $clear_queue -eq 1 ]; then echo "是"; else echo "否"; fi)"
echo "========================================"

# 后续命令(如执行 Python 脚本)
python main.py --input "$input_path" --redis-host "$redis_host" \
               $(if [ $clear_queue -eq 1 ]; then echo "--clear-queue"; fi)

运行方式(灵活组合)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 1. 使用默认配置(所有参数用默认值)
bash opt_param.sh

# 2. 指定输入路径和进程数(任意顺序)
bash opt_param.sh --input ./test_data --mlp-gpu 3
bash opt_param.sh --mlp-gpu 3 --input ./test_data  # 顺序不影响

# 3. 同时指定 Redis 地址和清除队列
bash opt_param.sh --redis-host 10.178.148.234 --clear-queue

# 4. 查看帮助
bash opt_param.sh --help

三、进阶场景:用 getopts 解析短选项(简洁)

适合需要短选项(如 -i 代替 --input-g 代替 --mlp-gpu)的场景,语法更简洁,但仅支持单字符选项(不支持长选项如 --input)。

核心语法

  • getopts "选项字符串" 变量名:循环解析短选项;
  • 选项字符串中:带 : 表示该选项需要值(如 i:g:r: 表示 -i-g-r 需带值);
  • OPTARG:存储选项对应的值;
  • OPTIND:下一个要解析的参数索引。

示例脚本(short_opt.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/bin/bash
# 短选项参数示例:-i(输入路径)、-g(进程数)、-r(Redis地址)、-c(清除队列)

# 1. 设置默认值
input_path="./default_data"
mlp_gpu=1
redis_host="10.178.148.233"
clear_queue=0

# 2. 解析短选项(i:g:r:c 表示 -i/-g/-r 带值,-c 是开关)
while getopts "i:g:r:ch" opt; do
    case "$opt" in
        i) input_path="$OPTARG" ;;  # -i 后接输入路径
        g) mlp_gpu="$OPTARG" ;;     # -g 后接进程数
        r) redis_host="$OPTARG" ;;  # -r 后接 Redis 地址
        c) clear_queue=1 ;;         # -c 触发清除队列
        h)  # -h 显示帮助
            echo "用法:bash $0 [-i 输入路径] [-g 进程数] [-r Redis地址] [-c] [-h]"
            echo "选项:"
            echo "  -i <路径>   输入待处理路径(默认:$input_path)"
            echo "  -g <数字>   进程数(默认:$mlp_gpu)"
            echo "  -r <地址>   Redis 地址(默认:$redis_host)"
            echo "  -c          清除队列(默认:不清除)"
            echo "  -h          显示帮助"
            exit 0
            ;;
        \?)  # 未知选项
            echo "错误:未知选项 -$OPTARG"
            echo "使用 -h 查看支持的选项"
            exit 1
            ;;
        :)  # 选项缺少值
            echo "错误:选项 -$OPTARG 需要指定值"
            exit 1
            ;;
    esac
done

# 3. 执行逻辑(和之前一致)
echo "输入路径:$input_path"
echo "进程数:$mlp_gpu"
echo "Redis 地址:$redis_host"
echo "是否清除队列:$( [ $clear_queue -eq 1 ] && echo "是" || echo "否" )"

运行方式

1
2
3
4
5
6
7
8
# 1. 短选项组合使用
bash short_opt.sh -i ./test_data -g 2 -r 10.178.148.234 -c

# 2. 单个选项
bash short_opt.sh -i ./new_data

# 3. 查看帮助
bash short_opt.sh -h

四、3 种写法的对比与选择建议

写法类型优点缺点适用场景
位置参数语法最简单,无需解析顺序固定、无选项标识参数少(1-2个)、顺序明确的场景
带选项的参数(while+case)支持长选项、任意顺序、可选参数代码稍长大多数场景(推荐,如你的 Python 脚本参数传递)
getopts 短选项语法简洁、支持短选项不支持长选项需要短选项(如 -i -g)的场景

五、实用技巧(避坑指南)

  1. 参数带空格的处理:若参数包含空格(如路径 ./my data),传递时用引号包裹("../my data"),脚本中变量也用引号("$input_path"),避免被拆分成多个参数;
  2. 默认值兜底:关键参数设置默认值,避免用户未传递时脚本报错;
  3. 参数校验:对数字参数(如进程数)校验是否为正整数,路径参数校验是否存在:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    # 校验进程数是否为正整数
    if ! [[ $mlp_gpu =~ ^[1-9][0-9]*$ ]]; then
        echo "错误:进程数必须是正整数"
        exit 1
    fi
    # 校验输入路径是否存在
    if [ ! -d "$input_path" ]; then
        echo "错误:输入路径 $input_path 不存在"
        exit 1
    fi
    
  4. 透传额外参数:若需要将脚本接收的未定义参数透传给其他命令(如 Python 脚本),用 $@ 收集:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    # 脚本中收集额外参数
    extra_args=()
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --input) input_path="$2"; shift 2 ;;
            --mlp-gpu) mlp_gpu="$2"; shift 2 ;;
            *) extra_args+=("$1"); shift 1 ;;  # 收集额外参数
        esac
    done
    # 透传给 Python 脚本
    python main.py --input "$input_path" --mlp-gpu "$mlp_gpu" "${extra_args[@]}"
    

总结

  • 简单场景用「位置参数」,复杂场景用「带选项的参数(while+case)」;
  • 你的需求(传递 Python 脚本的多个参数+进程数)最适合用「带选项的参数」写法,支持长选项、灵活组合,和你之前改造的脚本逻辑一致;
  • 记住核心原则:参数解析后先校验,再执行逻辑,避免因无效参数导致脚本异常。

💬 评论