函数参数 #

一、位置参数 #

1.1 基本用法 #

bash
#!/bin/bash

show_params() {
    echo "第一个参数: $1"
    echo "第二个参数: $2"
    echo "第三个参数: $3"
    echo "所有参数: $@"
    echo "参数个数: $#"
}

show_params "apple" "banana" "cherry"

1.2 参数访问 #

bash
#!/bin/bash

access_params() {
    # 访问单个参数
    echo "第一个: $1"
    echo "第二个: $2"
    
    # 访问所有参数
    echo "所有参数: $*"
    echo "所有参数: $@"
    
    # 参数个数
    echo "参数数量: $#"
    
    # 脚本名(在函数中仍然是脚本名)
    echo "脚本名: $0"
}

access_params "one" "two" "three"

1.3 $* 与 $@ 的区别 #

bash
#!/bin/bash

test_difference() {
    echo "使用 \$* 遍历:"
    for arg in "$*"; do
        echo "  参数: [$arg]"
    done
    
    echo "使用 \$@ 遍历:"
    for arg in "$@"; do
        echo "  参数: [$arg]"
    done
}

test_difference "a b" "c d" "e"

二、参数处理 #

2.1 默认参数 #

bash
#!/bin/bash

greet() {
    local name="${1:-匿名}"
    local greeting="${2:-你好}"
    
    echo "$greeting, $name!"
}

greet "张三" "早上好"
greet "李四"
greet

2.2 使用shift处理参数 #

bash
#!/bin/bash

process_options() {
    local verbose=false
    local output=""
    
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -v|--verbose)
                verbose=true
                shift
                ;;
            -o|--output)
                output="$2"
                shift 2
                ;;
            -h|--help)
                echo "用法: 函数名 [选项]"
                echo "  -v, --verbose   详细输出"
                echo "  -o, --output    输出文件"
                return 0
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "未知选项: $1"
                shift
                ;;
        esac
    done
    
    echo "详细模式: $verbose"
    echo "输出文件: ${output:-标准输出}"
    echo "剩余参数: $@"
}

process_options -v -o result.txt file1 file2

2.3 参数数组 #

bash
#!/bin/bash

# 传递数组
process_array() {
    local arr=("$@")
    
    echo "数组元素:"
    for item in "${arr[@]}"; do
        echo "  $item"
    done
    
    echo "数组长度: ${#arr[@]}"
}

fruits=("苹果" "香蕉" "橙子")
process_array "${fruits[@]}"

# 返回数组
get_numbers() {
    local result=(1 2 3 4 5)
    echo "${result[@]}"
}

numbers=($(get_numbers))
echo "获取的数组: ${numbers[@]}"

三、参数验证 #

3.1 基本验证 #

bash
#!/bin/bash

validate_basic() {
    # 检查参数数量
    if [[ $# -lt 1 ]]; then
        echo "错误: 至少需要一个参数" >&2
        return 1
    fi
    
    # 检查参数非空
    if [[ -z "$1" ]]; then
        echo "错误: 参数不能为空" >&2
        return 1
    fi
    
    echo "参数验证通过: $1"
    return 0
}

validate_basic "test"
validate_basic ""
validate_basic

3.2 类型验证 #

bash
#!/bin/bash

# 验证数字
is_number() {
    [[ "$1" =~ ^[0-9]+$ ]]
}

# 验证整数
is_integer() {
    [[ "$1" =~ ^-?[0-9]+$ ]]
}

# 验证浮点数
is_float() {
    [[ "$1" =~ ^-?[0-9]+(\.[0-9]+)?$ ]]
}

# 验证邮箱
is_email() {
    [[ "$1" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]
}

# 使用验证
validate_input() {
    local input="$1"
    local type="$2"
    
    case "$type" in
        number)
            if ! is_number "$input"; then
                echo "错误: $input 不是有效数字" >&2
                return 1
            fi
            ;;
        integer)
            if ! is_integer "$input"; then
                echo "错误: $input 不是有效整数" >&2
                return 1
            fi
            ;;
        float)
            if ! is_float "$input"; then
                echo "错误: $input 不是有效浮点数" >&2
                return 1
            fi
            ;;
        email)
            if ! is_email "$input"; then
                echo "错误: $input 不是有效邮箱" >&2
                return 1
            fi
            ;;
        *)
            echo "错误: 未知验证类型 $type" >&2
            return 1
            ;;
    esac
    
    echo "验证通过"
    return 0
}

validate_input "123" "number"
validate_input "3.14" "float"
validate_input "test@example.com" "email"

3.3 范围验证 #

bash
#!/bin/bash

validate_range() {
    local value="$1"
    local min="$2"
    local max="$3"
    
    # 验证是否为数字
    if ! [[ "$value" =~ ^-?[0-9]+$ ]]; then
        echo "错误: $value 不是有效数字" >&2
        return 1
    fi
    
    # 验证范围
    if (( value < min || value > max )); then
        echo "错误: $value 不在范围 [$min, $max] 内" >&2
        return 1
    fi
    
    echo "验证通过: $value"
    return 0
}

validate_range 50 1 100
validate_range 150 1 100

四、高级参数处理 #

4.1 命名参数 #

bash
#!/bin/bash

# 使用命名参数
process_named_args() {
    local name=""
    local age=""
    local city=""
    
    while [[ $# -gt 0 ]]; do
        case "$1" in
            name=*)
                name="${1#name=}"
                shift
                ;;
            age=*)
                age="${1#age=}"
                shift
                ;;
            city=*)
                city="${1#city=}"
                shift
                ;;
            *)
                echo "未知参数: $1"
                shift
                ;;
        esac
    done
    
    echo "姓名: ${name:-未指定}"
    echo "年龄: ${age:-未指定}"
    echo "城市: ${city:-未指定}"
}

process_named_args name=张三 age=25 city=北京

4.2 参数解析函数 #

bash
#!/bin/bash

# 通用参数解析
parse_args() {
    local -n result=$1  # 使用名称引用(Bash 4.3+)
    shift
    
    while [[ $# -gt 0 ]]; do
        local key="${1%%=*}"
        local value="${1#*=}"
        
        if [[ "$1" == *=* ]]; then
            result["$key"]="$value"
        else
            result["_args"]+=" $1"
        fi
        
        shift
    done
}

# 使用示例
declare -A args
parse_args args name=张三 age=25 file.txt

echo "姓名: ${args[name]}"
echo "年龄: ${args[age]}"
echo "其他参数: ${args[_args]}"

4.3 参数传递给其他函数 #

bash
#!/bin/bash

# 包装函数
wrapper() {
    echo "包装函数处理..."
    inner "$@"
}

inner() {
    echo "内部函数收到参数: $@"
}

wrapper "a" "b" "c"

# 过滤参数后传递
filter_and_pass() {
    local filtered=()
    
    while [[ $# -gt 0 ]]; do
        if [[ "$1" != "--skip" ]]; then
            filtered+=("$1")
        fi
        shift
    done
    
    process "${filtered[@]}"
}

process() {
    echo "处理: $@"
}

filter_and_pass "a" "--skip" "b" "c"

五、实战示例 #

5.1 配置文件解析 #

bash
#!/bin/bash

parse_config() {
    local config_file="$1"
    local -n config_ref=$2
    
    if [[ ! -f "$config_file" ]]; then
        echo "错误: 配置文件不存在" >&2
        return 1
    fi
    
    while IFS='=' read -r key value || [[ -n "$key" ]]; do
        # 跳过注释和空行
        [[ "$key" =~ ^#.*$ ]] && continue
        [[ -z "$key" ]] && continue
        
        # 去除前后空格
        key=$(echo "$key" | xargs)
        value=$(echo "$value" | xargs)
        
        config_ref["$key"]="$value"
    done < "$config_file"
}

# 使用示例
declare -A config
parse_config "app.conf" config

echo "数据库: ${config[database]}"
echo "端口: ${config[port]}"

5.2 命令行工具函数 #

bash
#!/bin/bash

# 命令行参数解析
parse_cli() {
    local action=""
    local files=()
    local options=()
    
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -h|--help)
                show_help
                exit 0
                ;;
            -v|--version)
                echo "版本: 1.0.0"
                exit 0
                ;;
            -o=*|--option=*)
                options+=("${1#*=}")
                shift
                ;;
            -*)
                echo "未知选项: $1" >&2
                exit 1
                ;;
            *)
                if [[ -z "$action" ]]; then
                    action="$1"
                else
                    files+=("$1")
                fi
                shift
                ;;
        esac
    done
    
    # 返回解析结果
    ACTION="$action"
    FILES=("${files[@]}")
    OPTIONS=("${options[@]}")
}

show_help() {
    cat << EOF
用法: $0 [选项] <动作> [文件...]

选项:
    -h, --help      显示帮助
    -v, --version   显示版本
    -o, --option    设置选项

动作:
    process    处理文件
    validate   验证文件
    convert    转换文件
EOF
}

# 使用示例
parse_cli "$@"
echo "动作: $ACTION"
echo "文件: ${FILES[@]}"
echo "选项: ${OPTIONS[@]}"

5.3 参数验证工具 #

bash
#!/bin/bash

# 参数验证器
validate_args() {
    local rules=("$@")
    local errors=()
    
    for rule in "${rules[@]}"; do
        local param="${rule%%:*}"
        local type="${rule#*:}"
        local value="${!param}"
        
        case "$type" in
            required)
                if [[ -z "$value" ]]; then
                    errors+=("$param 是必需的")
                fi
                ;;
            number)
                if [[ -n "$value" ]] && ! [[ "$value" =~ ^[0-9]+$ ]]; then
                    errors+=("$param 必须是数字")
                fi
                ;;
            email)
                if [[ -n "$value" ]] && ! [[ "$value" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
                    errors+=("$param 必须是有效邮箱")
                fi
                ;;
            file)
                if [[ -n "$value" ]] && [[ ! -f "$value" ]]; then
                    errors+=("$param 文件不存在")
                fi
                ;;
        esac
    done
    
    if [[ ${#errors[@]} -gt 0 ]]; then
        for error in "${errors[@]}"; do
            echo "错误: $error" >&2
        done
        return 1
    fi
    
    return 0
}

# 使用示例
NAME="张三"
AGE="25"
EMAIL="test@example.com"

validate_args \
    "NAME:required" \
    "AGE:number" \
    "EMAIL:email"

六、参数处理最佳实践 #

6.1 使用帮助函数 #

bash
#!/bin/bash

show_usage() {
    cat << EOF
用法: ${FUNCNAME[1]} [选项] <参数>

参数:
    参数1    第一个参数说明
    参数2    第二个参数说明

选项:
    -h       显示帮助
    -v       详细模式

示例:
    ${FUNCNAME[1]} -v arg1 arg2
EOF
}

my_function() {
    if [[ "$1" == "-h" ]]; then
        show_usage
        return 0
    fi
    
    # 函数逻辑
}

6.2 参数文档化 #

bash
#!/bin/bash

# 函数:处理文件
#
# 参数:
#   $1 - 文件路径(必需)
#   $2 - 处理模式(可选,默认:read)
#   $3 - 输出文件(可选)
#
# 返回:
#   0 - 成功
#   1 - 失败
#
# 示例:
#   process_file "/path/to/file" "write" "output.txt"
#
process_file() {
    local file="${1:?错误: 文件路径是必需的}"
    local mode="${2:-read}"
    local output="${3:-}"
    
    # 函数实现
    echo "处理文件: $file"
    echo "模式: $mode"
    [[ -n "$output" ]] && echo "输出: $output"
}

七、总结 #

7.1 参数处理要点 #

要点 说明
位置参数 $1, $2, …, $@, $#
默认值 $
参数验证 检查数量、类型、范围
shift 移动参数位置
命名参数 key=value 格式

7.2 下一步 #

你已经掌握了函数参数,接下来让我们学习 函数返回值,了解函数的返回值处理!

最后更新:2026-03-27