Bats测试中split_line函数数组赋值失败问题排查
split_line函数的数组传递问题分析
问题场景
需要实现split_line函数:将输入字符串分割为数组,当第二个参数不为1时移除数组首元素,返回可用于数组赋值的结果。但第三个测试用例失败,日志显示赋值后的数组仅包含一个完整字符串,而非预期的分割后元素。
原代码与失败日志
原函数与测试代码
#!/usr/bin/env bats log_debug() { echo "$@" >> /dev/stderr } split_line() { rmfirst=${2:-0} log_debug "args: \"$1\", ${rmfirst}" read -ra out_arr <<< "$1" log_debug "out: $(declare -p out_arr)" if [ $rmfirst -ne 1 ] ; then unset "out_arr[0]" fi echo "${out_arr[@]}" } @test "split line" { line_arr=("$(split_line "GETINFO version")") [ "${line_arr[0]}" = "version" ] } @test "split line rmfirst" { line_arr=("$(split_line "GETINFO version" 0)") [ "${line_arr[0]}" = "version" ] } @test "split line no-rmfirst" { IFS=" " line_arr=("$(split_line "GETINFO version" 1)") [[ "$(declare -p line_arr)" == 'declare -'[aA]* ]] log_debug "$(declare -p line_arr)" [[ "${line_arr[0]}" == "GETINFO" ]] [[ ${#line_arr} -eq 2 ]] [[ "${line_arr[1]}" == "version" ]] }
测试失败日志
✓ split line ✓ split line rmfirst ✗ split line no-rmfirst (in test file tests/foundational.bats, line 39) `[[ "${line_arr[0]}" == "GETINFO" ]]' failed args: "GETINFO version", 1 out: declare -a out_arr=([0]="GETINFO" [1]="version") declare -a line_arr=([0]="GETINFO version") 3 tests, 1 failure
问题根源
数组输出方式错误:
函数最后用echo "${out_arr[@]}"输出数组,会将所有元素用空格拼接成单个字符串。这种方式无法保留数组的结构,当捕获输出并赋值时,无法直接还原为多元素数组。测试赋值方式错误:
测试中line_arr=("$(split_line ...)")的写法,会把命令输出的整个内容当作数组的单个元素——因为引号的存在,即使设置了IFS=" ",Bash也不会分割这个字符串,导致line_arr只有一个元素"GETINFO version"。数组索引不连续(次要问题):
当unset out_arr[0]后,数组的索引会变成不连续状态(比如原[1]元素的索引仍为1),虽然echo "${out_arr[@]}"能输出元素,但数组的长度计算和索引访问会有潜在问题。
修复方案
修复split_line函数
改用换行符分隔每个元素输出,这样能可靠保留数组的元素结构,避免空格拼接的歧义:
split_line() { rmfirst=${2:-0} log_debug "args: \"$1\", ${rmfirst}" read -ra out_arr <<< "$1" log_debug "out: $(declare -p out_arr)" if [ "$rmfirst" -ne 1 ]; then unset "out_arr[0]" # 重新索引数组,让索引从0开始连续 out_arr=("${out_arr[@]}") fi # 每个元素单独一行输出,确保能被正确还原为数组 printf '%s\n' "${out_arr[@]}" }
修复测试用例
使用mapfile读取函数输出,直接将每行内容转为数组元素:
@test "split line no-rmfirst" { mapfile -t line_arr < <(split_line "GETINFO version" 1) [[ "$(declare -p line_arr)" == 'declare -'[aA]* ]] log_debug "$(declare -p line_arr)" [[ "${line_arr[0]}" == "GETINFO" ]] [[ ${#line_arr[@]} -eq 2 ]] [[ "${line_arr[1]}" == "version" ]] }
另外,前两个测试也需要同步修改赋值方式,比如:
@test "split line" { mapfile -t line_arr < <(split_line "GETINFO version") [ "${line_arr[0]}" = "version" ] } @test "split line rmfirst" { mapfile -t line_arr < <(split_line "GETINFO version" 0) [ "${line_arr[0]}" = "version" ] }
内容的提问来源于stack exchange,提问作者mcepl
相关产品推荐
相关产品推荐

