Shell之echo遇到的困惑

背景

最近用AI写了个脚本,在进行验证时,报了个错:

./scripts/xxx.sh:行67: 32m[2025-09-25:未找到命令

报错的位置是这样的,大致就是调用另外一个脚本,获取脚本的返回值来处理:

... ...
    log "提取内核: $kernel_version"

    local extract_result
    if ! extract_result=$(bash "$SCRIPT_DIR/kernel-extractor.sh" prepare "$kernel_version"); then
        error "内核提取失败"
        return 1
    fi

... ...

这里另外一个脚本使用echo来返回字符串或数组。

尝试用AI修复了几次,没有成功,只能自己去研究研究。后面发现就是这个echo使用的问题。

根本原因及解决办法

根本原因

在使用echo返回数据的函数中,又使用了echo来打印日志。这样在获取函数返回值时打印的日志会被命令替换$(...)捕获,从而被错误当成返回值被处理。这时,你也会发现,在使用echo返回结果的函数中,使用echo来打印的日志也不会输出。
所以在Shell函数中使用echo返回结果时,如果需要打印日志,需要将日志信息输出到标准错误(stderr),而不是标准输出(stdout)。

解决办法

最直接的解决办法就是使用 >&2 将日志输出到标准错误,类似:

#!/bin/bash

my_function() {
    local input="$1"
    
    # 打印日志到标准错误
    echo "DEBUG: 函数开始执行,输入参数: $input" >&2
    echo "INFO: 正在处理数据..." >&2
    
    # 业务逻辑
    local result=$(echo "$input" | tr '[:lower:]' '[:upper:]')
    
    echo "DEBUG: 处理完成,结果: $result" >&2
    
    # 返回结果到标准输出
    echo "$result"
}

# 调用函数
result=$(my_function "hello world")
echo "函数返回结果: $result"

使用 echo返回数据

我们再来看看使用echo返回数据的几种常用方式:

  • 使用 echo 返回字符串或任意数据
    #!/bin/bash
    
    # 函数返回字符串
    get_string() {
        local result="Hello World"
        echo "$result"
    }
    
    # 通过命令替换获取返回值
    return_value=$(get_string)
    echo "函数返回值: $return_value"
  • 返回数组
    #!/bin/bash
    
    # 返回数组
    get_array() {
        local arr=("apple" "banana" "cherry")
        echo "${arr[@]}"
    }
    
    # 获取返回的数组
    result_array=($(get_array))
    
    # 遍历数组
    echo "数组元素:"
    for item in "${result_array[@]}"; do
        echo "  $item"
    done
    
    echo "数组长度: ${#result_array[@]}"
  • 使用关联数组返回多个命名值:
    #!/bin/bash
    
    # 返回关联数组(需要bash 4.0+)
    get_user_info() {
        declare -A user_info
        user_info["name"]="张三"
        user_info["age"]="25"
        user_info["city"]="北京"
        
        # 将关联数组转换为字符串返回
        for key in "${!user_info[@]}"; do
            echo "$key:${user_info[$key]}"
        done
    }
    
    # 解析返回的关联数组
    declare -A result_array
    while IFS=: read -r key value; do
        result_array["$key"]="$value"
    done < <(get_user_info)
    
    # 使用返回值
    echo "姓名: ${result_array["name"]}"
    echo "年龄: ${result_array["age"]}"
    echo "城市: ${result_array["city"]}"
  • return vs echo
    • return:只能返回0-255的整数,通过 $? 获取
    • echo:可以返回任意字符串,通过命令替换 $(…) 获取