Files
wpa_hashcat/interactive_menu.sh
2025-07-26 03:48:37 +08:00

388 lines
11 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 交互式菜单函数(关键修复)
interactive_menu() {
trap 'cleanup' EXIT
local -n options_ref=$1
local title=${2:-"请选择一个选项:"}
local selected=0
local num_options=${#options_ref[@]}
# 初始化终端
local OLD_TERM=$(stty -g)
stty -echo -icanon time 0 min 0
tput smcup >/dev/tty
tput civis >/dev/tty
clear >/dev/tty
# 颜色定义
local COLOR_NORMAL=$(tput sgr0)
local COLOR_HIGHLIGHT=$(tput rev)
local COLOR_TITLE=$(tput setaf 4)
local COLOR_SELECTION=$(tput setaf 2)
# 显示菜单(修复污染源)
show_menu() {
tput cup 0 0 >/dev/tty
printf "${COLOR_TITLE}%s${COLOR_NORMAL}\n\n" "$title" >/dev/tty
for ((i=0; i<num_options; i++)); do
if ((i == selected)); then
printf "${COLOR_HIGHLIGHT}${COLOR_SELECTION}➔ %s${COLOR_NORMAL}\n" "${options_ref[$i]}" >/dev/tty
else
printf " %s\n" "${options_ref[$i]}" | expand -t4 >/dev/tty
fi
done
tput ed >/dev/tty
}
# 事件处理
local buffer=""
local return_index=-1
while true; do
show_menu
read -rsn1 input
buffer+="$input"
case "$buffer" in
$'\x1B[A') # 上箭头
((selected = (selected - 1 + num_options) % num_options))
buffer=""
;;
$'\x1B[B') # 下箭头
((selected = (selected + 1) % num_options))
buffer=""
;;
"") # 回车
return_index=$selected
break
;;
[qQ]) # 退出
break
;;
*)
[[ ! "$buffer" =~ ^$'\x1B' ]] && buffer=""
;;
esac
done
# 清理终端
cleanup() {
tput sgr0 2>/dev/null >/dev/tty
stty "$OLD_TERM" 2>/dev/null
tput cnorm >/dev/tty
tput rmcup >/dev/tty
}
cleanup
# 关键修复:确保纯净数字输出
if (( return_index >= 0 )); then
printf "%d" "$return_index" # 使用printf避免换行符[4](@ref)
return 0
else
return 1
fi
}
# # ============== 调用示例 ============== #
# source ./interactive_menu.sh
# menu_items=("查看CPU信息" "检查磁盘空间" "监控网络状态" "返回上级菜单")
#
# # 捕获纯净数字索引
# choice_index=$(interactive_menu menu_items "系统管理主菜单")
# interactive_menu_exit=$?
#
# # 安全获取选项(避免空值)
# if [[ -n "$choice_index" ]] && [[ "$choice_index" =~ ^[0-9]+$ ]]; then
# selected_item="${menu_items[$choice_index]}"
# else
# selected_item="无效选择"
# fi
#
# case $interactive_menu_exit in
# 0) echo "用户选择: $selected_item (索引: $choice_index)" ;;
# 1) echo "用户取消选择" ;;
# esac
export -f interactive_menu
interactive_menu_csv() {
trap 'cleanup' EXIT
local csv_data="$1"
local select_tip=${2:-"请选择一个选项:"}
local selected=0
local return_index=-1
# 解析CSV数据
IFS=$'\n' read -d '' -r -a lines <<< "$csv_data"
local header="${lines[0]}"
local -a options=("${lines[@]:1}")
local num_options=${#options[@]}
# 字符宽度映射表 - 针对常见中文字符优化
declare -A char_width_map
# 计算字符串的显示宽度(考虑全角字符)
str_display_width() {
local str="$1"
local width=0
local char full_char hex_bytes codepoint
# 按字符遍历字符串
while IFS= read -r -n1 char; do
[[ -z "$char" ]] && continue # 跳过空字符
# 读取完整UTF-8字符
full_char="$char"
local first_byte=$(printf "%d" "'$char")
# 1字节字符(ASCII
if (( first_byte < 128 )); then
((width++))
continue
fi
# 多字节字符处理
if (( first_byte >= 194 && first_byte <= 223 )); then # 2字节
read -r -n1 char; full_char+="$char"
elif (( first_byte >= 224 && first_byte <= 239 )); then # 3字节
read -r -n1 char; full_char+="$char"
read -r -n1 char; full_char+="$char"
elif (( first_byte >= 240 && first_byte <= 244 )); then # 4字节
read -r -n1 char; full_char+="$char"
read -r -n1 char; full_char+="$char"
read -r -n1 char; full_char+="$char"
fi
# 获取UTF-8字节的十六进制表示
hex_bytes=$(echo -n "$full_char" | xxd -p)
# 转换UTF-8到Unicode码点
case ${#hex_bytes} in
4) # 2字节
codepoint=$(((0x${hex_bytes:0:2} & 0x1F) << 6 | (0x${hex_bytes:2:2} & 0x3F))) ;;
6) # 3字节
codepoint=$(((0x${hex_bytes:0:2} & 0x0F) << 12 | (0x${hex_bytes:2:2} & 0x3F) << 6 | (0x${hex_bytes:4:2} & 0x3F))) ;;
8) # 4字节
codepoint=$(((0x${hex_bytes:0:2} & 0x07) << 18 | (0x${hex_bytes:2:2} & 0x3F) << 12 | (0x${hex_bytes:4:2} & 0x3F) << 6 | (0x${hex_bytes:6:2} & 0x3F))) ;;
*) # 其他情况
codepoint=0 ;;
esac
# 判断宽字符(添加韩文字母范围 U+3130–U+318F
if (( codepoint >= 0x4E00 && codepoint <= 0x9FFF || codepoint >= 0x3400 && codepoint <= 0x4DBF || codepoint >= 0x3040 && codepoint <= 0x309F || codepoint >= 0x30A0 && codepoint <= 0x30FF || codepoint >= 0x3130 && codepoint <= 0x318F || codepoint >= 0xAC00 && codepoint <= 0xD7AF || codepoint >= 0xFF00 && codepoint <= 0xFFEF || codepoint >= 0x3000 && codepoint <= 0x303F )); then
((width += 2))
else
((width++))
fi
done <<< "$str"
echo "$width"
}
# 确定每列的最大宽度
local -a max_widths
IFS=',' read -r -a headers <<< "$header"
for ((i=0; i<${#headers[@]}; i++)); do
max_widths[$i]=$(str_display_width "${headers[$i]}")
done
# 计算每列的最大宽度(包括选项)
for line in "${options[@]}"; do
IFS=',' read -r -a fields <<< "$line"
for ((i=0; i<${#fields[@]}; i++)); do
local width=$(str_display_width "${fields[$i]}")
if (( width > max_widths[i] )); then
max_widths[$i]=$width
fi
done
done
# 列间隔设置
local MIN_SPACING=3
local MAX_SPACING=5
local num_columns=${#max_widths[@]}
# 计算总内容宽度
local total_content_width=0
for width in "${max_widths[@]}"; do
((total_content_width += width))
done
# 获取终端宽度
local term_width=$(tput cols)
local available_space=$((term_width - total_content_width))
# 计算最佳间隔
if (( num_columns > 1 )); then
local ideal_spacing=$((available_space / (num_columns - 1)))
if (( ideal_spacing < MIN_SPACING )); then
spacing=$MIN_SPACING
elif (( ideal_spacing > MAX_SPACING )); then
spacing=$MAX_SPACING
else
spacing=$ideal_spacing
fi
else
spacing=$MIN_SPACING
fi
# 格式化行数据
format_row() {
local -a fields=("$@")
local formatted=""
for ((i=0; i<${#fields[@]}; i++)); do
local field="${fields[$i]}"
local field_width=$(str_display_width "$field")
local padding=$((max_widths[i] - field_width))
formatted+="${field}"
# 添加填充空格
for ((j=0; j<padding; j++)); do
formatted+=" "
done
# 添加列间隔,最后一列除外
if (( i < ${#fields[@]} - 1 )); then
for ((j=0; j<spacing; j++)); do
formatted+=" "
done
fi
done
echo "$formatted"
}
# 格式化表头
IFS=',' read -r -a header_fields <<< "$header"
formatted_header=$(format_row "${header_fields[@]}")
# 格式化选项
local -a formatted_options
for line in "${options[@]}"; do
IFS=',' read -r -a fields <<< "$line"
formatted_options+=("$(format_row "${fields[@]}")")
done
# 初始化终端
local OLD_TERM=$(stty -g)
stty -echo -icanon time 0 min 0
tput smcup >/dev/tty
tput civis >/dev/tty
clear >/dev/tty
# 颜色定义
local COLOR_NORMAL=$(tput sgr0)
local COLOR_HIGHLIGHT=$(tput rev)
local COLOR_select_tip=$(tput setaf 4) # 蓝色标题
local COLOR_HEADER=$(tput setaf 6) # 青色表头
local COLOR_SELECTION=$(tput setaf 2) # 绿色选中项
local COLOR_PROMPT=$(tput setaf 3) # 黄色提示
# 高亮整行(包括间隔)
highlight_line() {
local line="$1"
local highlighted="${COLOR_HIGHLIGHT}${COLOR_SELECTION}${line}${COLOR_NORMAL}"
echo "$highlighted"
}
# 显示菜单
show_menu() {
tput cup 0 0 >/dev/tty
# 显示表头
printf " ${COLOR_HEADER}%s${COLOR_NORMAL}\n\n" "$formatted_header" >/dev/tty
# 显示选项
for ((i=0; i<num_options; i++)); do
if ((i == selected)); then
printf "%s\n" "$(highlight_line "${formatted_options[$i]}")" >/dev/tty
else
printf " %s\n" "${formatted_options[$i]}" >/dev/tty
fi
done
# 计算提示信息应该显示的位置
local prompt_row=$((num_options + 3))
tput cup $prompt_row 0 >/dev/tty
tput el >/dev/tty
printf "${COLOR_PROMPT}%s${COLOR_NORMAL}\n" "$select_tip" >/dev/tty
printf "${COLOR_PROMPT}↑↓: 选择 Enter: 确认 Q: 退出${COLOR_NORMAL}\n" >/dev/tty
# 清除屏幕剩余部分
tput ed >/dev/tty
}
# 事件处理
local buffer=""
while true; do
show_menu
read -rsn1 input
buffer+="$input"
case "$buffer" in
$'\x1B[A') # 上箭头
((selected = (selected - 1 + num_options) % num_options))
buffer=""
;;
$'\x1B[B') # 下箭头
((selected = (selected + 1) % num_options))
buffer=""
;;
"") # 回车
return_index=$selected
break
;;
[qQ]) # 退出
break
;;
*)
[[ ! "$buffer" =~ ^$'\x1B' ]] && buffer=""
;;
esac
done
# 清理终端
cleanup() {
tput sgr0 2>/dev/null >/dev/tty
stty "$OLD_TERM" 2>/dev/null
tput cnorm >/dev/tty
tput rmcup >/dev/tty
}
cleanup
# 返回选择结果
if (( return_index >= 0 )); then
printf "%d" "$return_index"
return 0
else
return 1
fi
}
# # ============== 调用示例 ============== #
# source ./interactive_menu_csv.sh
# csv="SSID名称,MAC地址,加密类型,握手信息
# 好日子-WiFi-5G,11:22:33:AA:BB:CC,WPA2,2 handshake
# 303-4g,22:55:44:22:EE:DD,WPA2,1 handshake
# 303,55:66:77:88:99:AA,WPA2,1 handshake"
#
# selected=$(interactive_menu_csv "$csv" "请选择一个WiFi网络:")
# if [ $? -eq 0 ]; then
# echo "你选择了选项 $selected"
# else
# echo "已取消选择"
# fi
export -f interactive_menu_csv