Shell脚本中各类参数传递以及获取
Linux Shell 参数传递与获取完全指南
一、基础参数传递方式
1. 位置参数(Positional Parameters)
#!/bin/bash
调用方式:./script.sh param1 "param 2" param3
echo "脚本名称: $0" # 脚本路径
echo "第一个参数: $1" # param1
echo "第二个参数: $2" # param 2 (保留空格)
echo "参数总数: $#" # 3
echo "所有参数: $*" # "param1 param 2 param3"(合并为单字符串)
echo "所有参数: $@" # "param1" "param 2" "param3"(保持独立)
2. 特殊参数变量
变量 | 描述 | 示例 |
---|---|---|
$# | 参数个数 | if [ $# -eq 0 ]; then... |
$* | 所有参数合并为单字符串 | for arg in "$*"; do... |
$@ | 所有参数独立保留 | for arg in "$@"; do... |
$? | 上条命令退出状态 | if [ $? -ne 0 ]; then... |
$$ | 当前进程PID | echo "PID: $$" |
$! | 最后一个后台进程PID | firefox & echo "Firefox PID: $!" |
二、参数获取高级技巧
1. 参数遍历方法
方法1:直接遍历$@
for param in "$@"; do
echo "参数: $param"
done
方法2:使用shift逐项处理
while [ "$1" != "" ]; do
case $1 in
-f|--file) shift; file=$1 ;;
-v|--verbose) verbose=1 ;;
*) echo "未知参数: $1"; exit 1 ;;
esac
shift # 移除已处理的参数
done
2. 默认值设置
如果未传参则使用默认值
username=${1:-"guest"}
port=${2:-8080}
冒号语法(变量为空时也触发)
logfile=${LOG_FILE:?"必须设置LOG_FILE环境变量"}
三、命令行选项解析
1. getopts(标准Unix方式)
#!/bin/bash
while getopts ":a:b:c" opt; do
case $opt in
a) arg_a="$OPTARG" ;;
b) arg_b="$OPTARG" ;;
c) flag_c=1 ;;
\?) echo "无效选项: -$OPTARG" >&2 ;;
:) echo "选项 -$OPTARG 需要参数" >&2 ;;
esac
done
shift $((OPTIND-1)) # 移除已解析选项
调用示例:./script.sh -a value -b "test" -c file.txt
2. getopt(增强版,支持长选项)
#!/bin/bash
TEMP=$(getopt -o ab:c:: --long alpha,beta:,gamma:: -n '示例' -- "$@")
eval set -- "$TEMP"
while true; do
case "$1" in
-a|--alpha) alpha=1; shift ;;
-b|--beta) beta="$2"; shift 2 ;;
-c|--gamma)
case "$2" in
"") gamma="default"; shift 2 ;;
*) gamma="$2"; shift 2 ;;
esac ;;
--) shift; break ;;
*) echo "内部错误"; exit 1 ;;
esac
done
调用示例:./script.sh --alpha --beta "value" --gamma=opt
四、参数验证与安全
1. 必填参数检查
required_params=("host" "port")
missing_params=()
for param in "${required_params[@]}"; do
if [ -z "${!param}" ]; then # 间接引用变量
missing_params+=("$param")
fi
done
if [ ${#missing_params[@]} -gt 0 ]; then
echo "缺少必要参数: ${missing_params[*]}"
exit 1
fi
2. 类型校验
数字校验
if ! [[ "$port" =~ ^[0-9]+$ ]]; then
echo "端口必须是数字"
exit 1
fi
文件存在检查
if ! [ -f "$input_file" ]; then
echo "输入文件不存在: $input_file"
exit 1
fi
IP地址校验
if ! [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
echo "无效IP地址"
exit 1
fi
五、实用模式示例
1. 子命令模式(类似git)
case "$1" in
start)
shift # 移除子命令参数
echo "启动服务,参数: $@" ;;
stop)
shift
echo "停止服务,参数: $@" ;;
*)
echo "用法: $0 {start|stop} [options]"
exit 1 ;;
esac
调用示例:./service.sh start --port 8080
2. 参数注入防护
危险!可能执行任意命令
eval "$1"
安全做法:白名单验证
allowed_commands=("start" "stop" "restart")
if [[ " ${allowed_commands[*]} " =~ " $1 " ]]; then
case "$1" in
start) ./start_service.sh ;;
stop) ./stop_service.sh ;;
esac
else
echo "无效命令"
fi
六、性能优化技巧
1. 参数缓存处理
缓存解析结果
parse_args() {
local args=("$@")
ARG_CACHE=""
for arg in "${args[@]}"; do
ARG_CACHE+="$(printf '%q' "$arg") "
done
}
后续使用缓存
eval "set -- $ARG_CACHE"
2. 批量参数处理
使用关联数组存储参数
declare -A params
while IFS='=' read -r key value; do
params["$key"]="$value"
done < <(printf '%s\n' "$@")
访问示例
echo "用户名: ${params["user"]}"
echo "端口: ${params["port"]}"
七、跨脚本参数传递
1. 导出到环境变量
主脚本
export DB_HOST="db.example.com"
export DB_PORT=3306
./subscript.sh
subscript.sh
echo "连接数据库: ${DB_HOST}:${DB_PORT}"
2. 通过文件传递
生成参数文件
cat > params.conf <<EOF
input_file=/data/sample.txt
output_dir=/var/reports
max_records=1000
EOF
读取参数文件
source params.conf
echo "处理文件: $input_file"
最佳实践总结
- 命名规范:使用有意义的参数名(如
--input-file
优于-i
) - 错误处理:对非法参数立即退出并显示帮助
- 安全防护:始终验证和清理输入参数
- 文档注释:在脚本头部添加参数说明
示例帮助文档:
usage() {
cat <<EOF
用法: ${0##*/} [选项] 文件...
选项:
-h, --help 显示此帮助信息
-v, --verbose 显示详细输出
-o 文件 指定输出文件
--version 显示版本信息
示例:
${0##*/} -o result.txt input*.log
EOF
exit ${1:-0}
}