Shell 传递参数(长文讲解)

Shell 传递参数:从入门到实战

在日常开发中,我们常常需要通过命令行运行脚本,并传入一些动态数据。比如,启动一个服务时指定端口号,或者批量处理文件时传入文件路径。这些动态数据,正是通过 Shell 传递参数来实现的。掌握这一技能,是迈向自动化运维和脚本开发的重要一步。

想象一下,你有一个“快递打包机”——它能自动把包裹装箱、贴标签、发运。但每次打包的包裹内容不同,你必须告诉机器“这次要寄的是书”或“这次是手机”。Shell 传递参数,就是这个“告诉机器”的过程。它让脚本不再死板,而是变得灵活、可复用。


Shell 传递参数的基本语法

在 Shell 脚本中,可以通过位置参数(Positional Parameters)来接收传入的参数。这些参数以 $1, $2, $3 … 的形式引用,分别代表第一个、第二个、第三个参数。

#!/bin/bash

echo "第一个参数是: $1"

echo "第二个参数是: $2"

echo "所有参数是: $*"

说明

  • $1 表示第一个传入的参数,$2 是第二个,依此类推。
  • $* 表示将所有参数视为一个整体字符串(用空格分隔)。
  • $@$* 类似,但更适用于参数中包含空格的场景(稍后详解)。

保存为 greet.sh,然后运行:

bash greet.sh 你好 世界

输出结果:

第一个参数是: 你好
第二个参数是: 世界
所有参数是: 你好 世界

这个例子中,你好世界 就是传入的参数,Shell 通过 $1$2 把它们“抓”进了脚本。


特殊参数变量:$0、$# 和 $@

除了 $1, $2 等,Shell 还提供了一些特殊变量,用于获取脚本本身或参数的元信息。

变量 说明
$0 脚本文件名(包括路径)
$# 参数个数
$@ 所有参数,保留原格式(推荐用于循环)
$* 所有参数合并成一个字符串(注意空格处理)

实际案例:参数校验脚本

#!/bin/bash

echo "脚本名称: $0"

echo "参数个数: $#"

if [ $# -eq 0 ]; then
    echo "用法: $0 <姓名> <年龄>"
    exit 1
fi

echo "参数列表:"
for arg in "$@"; do
    echo "  - $arg"
done

运行:

bash param_check.sh 张三 25

输出:

脚本名称: param_check.sh
参数个数: 2
参数列表:
  - 张三
  - 25

提示"$@"$* 更安全,尤其当参数中包含空格时。例如 bash script.sh "张三 伟""$@" 会将其视为一个参数,而 $* 会拆成两个。


使用 shift 命令处理参数序列

当你需要处理多个参数,且按顺序处理时,shift 命令非常有用。它会将所有参数向前移动一位,让 $2 变成 $1$3 变成 $2,以此类推。

#!/bin/bash

echo "开始处理参数..."

while [ $# -gt 0 ]; do
    echo "当前参数: $1"
    shift  # 移动参数指针,下一次循环 $1 变为原来 $2
done

echo "所有参数已处理完毕。"

运行:

bash shift_demo.sh 项目A 1.0.0 dev

输出:

开始处理参数...
当前参数: 项目A
当前参数: 1.0.0
当前参数: dev
所有参数已处理完毕。

比喻:你可以把 shift 想象成“向前一步走”的指令。每走一步,当前“第一个”就变成“下一个”,直到没有更多参数可走。


参数传入方式:命令行直接传参

最常见的方式就是在运行脚本时直接传参,这是最简单、最直接的 Shell 传递参数方法。

bash my_script.sh arg1 arg2 "arg with space"

注意:

  • 如果参数中包含空格,必须用引号包裹(单引号或双引号)。
  • 未加引号的参数会被 Shell 按空格分割,可能导致参数错乱。

错误示例(不加引号):

bash script.sh 项目1.0.0

如果脚本中这样写:

echo "版本号: $1"

输出会是:版本号: 项目1.0.0 —— 没问题。

但如果写成:

echo "版本号: $1"  # 传入 "项目1.0.0"

结果正确。但如果参数是 项目 1.0.0(中间有空格),不加引号就会变成两个参数,导致 $1 是“项目”,$2 是“1.0.0”。

所以,养成习惯:含空格的参数必须用引号包裹


高级用法:使用 getopts 解析选项参数

当脚本需要支持类似 -v--help 这样的命令行选项时,getopts 是首选工具。它能自动解析短选项(如 -v)和长选项(如 --verbose)。

示例:支持 -h-v 选项的脚本

#!/bin/bash

verbose=false
help=false

while getopts "hv" opt; do
    case $opt in
        h)
            help=true
            ;;
        v)
            verbose=true
            ;;
        *)
            echo "用法: $0 [-h] [-v]"
            exit 1
            ;;
    esac
done

if [ "$help" = true ]; then
    echo "用法: $0 [-h] [-v]"
    echo "选项:"
    echo "  -h  显示帮助信息"
    echo "  -v  启用详细模式"
    exit 0
fi

if [ "$verbose" = true ]; then
    echo "详细模式已启用。"
fi

if [ $# -gt 0 ]; then
    echo "额外参数: $@"
fi

运行测试:

bash demo.sh -h

输出帮助信息。

bash demo.sh -v 任务1

输出:

详细模式已启用。
额外参数: 任务1

说明getopts "hv" 中的 hv 表示支持的选项,v 后面加冒号 : 表示该选项需要参数(如 -v 1),这里未使用,所以仅支持 -h-v 无参。


Shell 传递参数的常见陷阱与最佳实践

虽然 Shell 传递参数看似简单,但初学者容易踩坑。以下是几个典型问题和应对建议:

1. 参数为空或未传入

echo "用户名: $1"

username="${1:-未知用户}"
echo "用户名: $username"

使用 ${var:-default} 可以设置默认值,避免空值导致脚本出错。

2. 参数中包含特殊字符(如 *, ?, $

这些字符在 Shell 中有特殊含义,需用引号包裹或转义。

bash script.sh "文件*1.txt"

bash script.sh 文件*1.txt  # 可能被扩展成多个文件名

3. 避免硬编码路径

不要写 cd /home/user/script,而是用 $0dirname $0 动态获取脚本所在目录。

script_dir=$(dirname "$0")
cd "$script_dir"

总结:Shell 传递参数是脚本灵活化的基石

Shell 传递参数,是连接外部输入与脚本逻辑的桥梁。无论是简单的参数打印,还是复杂的命令行选项解析,掌握这些技巧,都能让你的脚本更加健壮、可维护。

$1getopts,从 shift"$@",每一步都是脚本能力的升级。当你能熟练运用 Shell 传递参数时,就真正迈入了自动化与运维开发的大门。

记住:参数不是“死”的,而是“活”的。你赋予它意义,它就能帮你完成复杂任务。多写、多试、多调试,你会发现,Shell 脚本远比你想象的更强大。