Bash中基于关联数组生成动态标记表格的简化方案问询
问题描述
现有一个Bash关联数组输入:
declare -A componentMatrix=( ["Component 1" ]='debug,sysinstall,install,configure,deploy' ["Component 2" ]='install,configure,smoketest,sysuninstall' ["Component 1234" ]='install,debug,dummy,showlogs' ["Component 32" ]='install,sysuninstall,showlogs' ["Component 666" ]='sysinstall,configure,showlogs' )
需要生成自动计算列宽、星号居中且支持水平偏移的动态表格:
| showlogs | dummy | debug | smoketest | install | configure | deploy | sysinstall | sysuninstall | -----------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+ Component 32 | * | | | | * | | | | * | -----------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+ Component 2 | | | | * | * | * | | | * | -----------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+ Component 1 | | | * | | * | * | * | * | | -----------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+ Component 1234 | * | * | * | | * | | | | | -----------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+ Component 666 | * | | | | * | * | | * | | -----------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+--------------+
当前实现代码较为复杂,希望得到更简洁的Bash写法、无需依赖Python/Perl的独立工具,以及可简化代码的Bash特性。
现有复杂实现代码:
#!/usr/bin/env bash declare -A componentMatrix=( ["Component 1" ]='debug,sysinstall,install,configure,deploy' ["Component 2" ]='install,configure,smoketest,sysuninstall' ["Component 1234" ]='install,debug,dummy,showlogs' ["Component 32" ]='install,sysuninstall,showlogs' ["Component 666" ]='sysinstall,configure,showlogs' ) show_component_table() { declare -A unique_cmds # 记录唯一命令的关联数组 local offset=${1:?} # 表格水平偏移量变量 local max_component_length=0 # 组件名称最大长度 local max_cmd_length=0 # 命令最大长度 local horizontal_line_char="-" # 水平线字符 local vertical_char="|" # 垂直线字符 local intersection_char="+" # 交叉点字符 local asterisk_char="*" # 命令存在标记字符 local component cmds cmd generate_horizontal_line() { printf "%*s" $offset for ((i=0; i<${first_col_width}; i++)); do printf "$horizontal_line_char" done printf "$intersection_char" for ((i=0; i<${#cmdlist[@]}; i++)); do for ((j=0; j<$generic_col_width; j++)); do printf "$horizontal_line_char" done printf "$intersection_char" done echo "" } for component in "${!componentMatrix[@]}"; do if (( ${#component} > max_component_length )); then max_component_length=${#component} fi cmds=${componentMatrix[$component]} for cmd in ${cmds//,/ }; do unique_cmds[$cmd]=1 done done local cmdlist=("${!unique_cmds[@]}") for cmd in "${cmdlist[@]}"; do if (( ${#cmd} > max_cmd_length )); then max_cmd_length=${#cmd} fi done local generic_col_width=$((max_cmd_length + 2)) local first_col_width=$((max_component_length + 2)) local mid_component=$(((generic_col_width) / 2)) local rest_component=$((generic_col_width - mid_component)) printf "%*s" $offset printf "%-${first_col_width}s|" "" for cmd in "${cmdlist[@]}"; do printf " %-${max_cmd_length}s ${vertical_char}" "$cmd" done echo "" generate_horizontal_line for component in "${!componentMatrix[@]}"; do printf "%*s" $offset printf "%-${first_col_width}s|" "$component" cmds=${componentMatrix[$component]} for cmd in "${cmdlist[@]}"; do if [[ $cmds =~ $cmd ]]; then printf " %${mid_component}s%${rest_component}s" "$asterisk_char" "${vertical_char}" else printf "%-${generic_col_width}s%s" "" "${vertical_char}" fi done echo "" generate_horizontal_line done } show_component_table 10
优化方案
一、更简洁的Bash实现
通过简化循环逻辑、利用printf的重复输出特性、优化字符串匹配,可大幅精简代码:
#!/usr/bin/env bash declare -A componentMatrix=( ["Component 1"]='debug,sysinstall,install,configure,deploy' ["Component 2"]='install,configure,smoketest,sysuninstall' ["Component 1234"]='install,debug,dummy,showlogs' ["Component 32"]='install,sysuninstall,showlogs' ["Component 666"]='sysinstall,configure,showlogs' ) show_component_table() { local offset=${1:?} local -A cmd_map=() local max_comp_len=0 max_cmd_len=0 # 收集命令并计算最大长度 for comp in "${!componentMatrix[@]}"; do (( ${#comp} > max_comp_len )) && max_comp_len=${#comp} IFS=',' read -ra cmds <<< "${componentMatrix[$comp]}" for cmd in "${cmds[@]}"; do cmd_map["$cmd"]=1 (( ${#cmd} > max_cmd_len )) && max_cmd_len=${#cmd} done done local -a cmd_list=("${!cmd_map[@]}") local col_width=$((max_cmd_len + 2)) local first_col=$((max_comp_len + 2)) # 预先生成水平线模板 local hline=$(printf "%${first_col}s" | tr ' ' '-') hline+=$(printf "%s" "${cmd_list[@]/#/+$(printf "%${col_width}s" | tr ' ' '-')}") # 打印表头 printf "%*s" "$offset" "" printf "%-${first_col}s|" "" for cmd in "${cmd_list[@]}"; do printf " %-${max_cmd_len}s |" "$cmd" done echo -e "\n%*s%s" "$offset" "" "$hline" # 打印组件数据行 for comp in "${!componentMatrix[@]}"; do printf "%*s" "$offset" "" printf "%-${first_col}s|" "$comp" IFS=',' read -ra cmds <<< "${componentMatrix[$comp]}" local -A comp_cmds=() for cmd in "${cmds[@]}"; do comp_cmds["$cmd"]=1; done for cmd in "${cmd_list[@]}"; do if [[ -n ${comp_cmds["$cmd"]} ]]; then printf " %$((col_width/2))s%$((col_width - col_width/2))s" "*" "|" else printf "%-${col_width}s|" "" fi done echo -e "\n%*s%s" "$offset" "" "$hline" done } show_component_table 10
精简点说明:
- 用
IFS=',' read -ra安全拆分命令字符串,避免全局替换的潜在问题 - 用
printf + tr生成重复横线,替代嵌套循环 - 预先生成完整水平线,避免重复计算
- 用组件专属关联数组存储命令,提升匹配效率
二、无需依赖外部脚本语言的独立工具
利用系统自带的column命令,结合Bash预处理生成TSV(制表符分隔值)数据,可快速生成整齐表格:
#!/usr/bin/env bash declare -A componentMatrix=( ["Component 1"]='debug,sysinstall,install,configure,deploy' ["Component 2"]='install,configure,smoketest,sysuninstall' ["Component 1234"]='install,debug,dummy,showlogs' ["Component 32"]='install,sysuninstall,showlogs' ["Component 666"]='sysinstall,configure,showlogs' ) generate_tsv() { local -A cmd_map=() # 收集所有唯一命令 for comp in "${!componentMatrix[@]}"; do IFS=',' read -ra cmds <<< "${componentMatrix[$comp]}" for cmd in "${cmds[@]}"; do cmd_map["$cmd"]=1; done done local -a cmd_list=("${!cmd_map[@]}") # 打印表头 echo -e "Component\t${cmd_list[*]/%/\t}" | tr ' ' '\t' # 打印数据行 for comp in "${!componentMatrix[@]}"; do local line="$comp" IFS=',' read -ra cmds <<< "${componentMatrix[$comp]}" local -A comp_cmds=() for cmd in "${cmds[@]}"; do comp_cmds["$cmd"]=1; done for cmd in "${cmd_list[@]}"; do line+=$'\t'"${comp_cmds[$cmd]:- }" done echo -e "${line// /\*}" done } # 生成TSV后用column格式化,添加水平偏移 generate_tsv | column -t -s $'\t' | sed "s/^/$(printf "%10s" "")/"
column是Linux发行版默认自带的工具,擅长文本对齐,配合TSV数据可自动处理列宽,无需手动计算。
三、可简化代码的Bash特性
- 关联数组:用于快速去重和存在性检查,比如
cmd_map存储所有唯一命令,comp_cmds存储当前组件的命令集合 - printf高级格式化:利用
%*s动态指定宽度、tr生成重复字符,替代循环输出 - IFS拆分字符串:
IFS=',' read -ra安全拆分逗号分隔的命令列表,避免全局替换的副作用 - 参数扩展:比如
${cmd_list[@]/#/+...}批量给数组元素添加前缀,快速构建水平线 - 局部变量:用
local限定变量作用域,避免全局污染,提升代码可读性
内容的提问来源于stack exchange,提问作者ikaerom
相关产品推荐
相关产品推荐

