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

323 lines
11 KiB
Bash
Raw 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
file_selector() {
# 保存原始终端状态
local OLD_TERM
OLD_TERM=$(stty -g 2>/dev/null)
# 保存当前进程ID(用于本地化信号处理)
local TOP_PID=$$
# 清理函数 - 完全恢复原始状态
cleanup() {
# 恢复主屏幕缓冲区
echo -en "\033[?1049l" >&2
# 恢复光标
tput cnorm >&2
# 重置文本样式
tput sgr0 >&2
# 恢复终端设置(仅在OLD_TERM有值时)
if [[ -n "$OLD_TERM" ]]; then
stty "$OLD_TERM" >&2
fi
}
normalize_path() {
local path="$1"
# 替换任意数量连续斜杠为单斜杠
path=$(echo "$path" | sed 's|//\+|/|g')
# 特殊处理根目录的异常形式
[[ "$path" == "//" ]] && path="/"
echo "$path"
}
# 信号捕获
trap 'cleanup; exit 1' EXIT INT TERM
# 设置起始目录(支持可选参数)
local start_dir="${1:-$(pwd)}"
local restrict_mode=0
local custom_prompt
local file_exts=()
# 修复参数解析逻辑
if [[ "$2" == "-r" || "$2" == "--restrict" ]]; then
restrict_mode=1
custom_prompt="${3:-请选择一个文件:}"
if [[ -n "${4:-}" ]]; then
IFS=',' read -ra file_exts <<< "${4// /}" # 移除空格并分割
fi
else
# 当没有使用-r选项时,第二个参数是提示
custom_prompt="${2:-请选择一个文件:}"
if [[ -n "${3:-}" ]]; then
IFS=',' read -ra file_exts <<< "${3// /}" # 移除空格并分割
fi
fi
local current_dir="$start_dir"
current_dir=$(normalize_path "$current_dir")
local dir_stack=("$current_dir")
local selected=0 current_page=0 selected_file=""
# 定义颜色(局部变量)
local COLOR_NORMAL=$(tput sgr0)
local COLOR_HIGHLIGHT=$(tput rev)
local COLOR_TITLE=$(tput setaf 14)
local COLOR_SELECTION=$(tput setaf 2)
local COLOR_DIR=$(tput setaf 6)
local COLOR_BACK=$(tput setaf 3)
local COLOR_PAGE=$(tput setaf 5)
# 启用备用屏幕缓冲区
echo -en "\033[?1049h" >&2
# 隐藏光标
tput civis >&2
# 获取目录内容
get_directory_contents() {
local dir="$1"
local contents=()
# 添加上级目录选项
[[ "$dir" != "/" && ( "$restrict_mode" -eq 0 || "$current_dir" != "$start_dir" ) ]] && contents+=("..")
# 获取目录(始终显示)
while IFS= read -r -d $'\0' item; do
[[ -d "$item" ]] && contents+=("${item#$dir/}/")
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null)
# 获取文件(支持多种后缀过滤)
if (( ${#file_exts[@]} > 0 )); then
# 修复:构建正确的find查询条件
local find_args=()
for ext in "${file_exts[@]}"; do
# 移除可能的空格
ext="${ext// /}"
[[ -n "$ext" ]] && find_args+=(-name "*.${ext}" -o)
done
# 移除最后一个多余的 -o
if (( ${#find_args[@]} > 0 )); then
unset 'find_args[${#find_args[@]}-1]'
# 执行带后缀过滤的查询
while IFS= read -r -d $'\0' item; do
contents+=("${item#$dir/}")
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f \( "${find_args[@]}" \) -print0 2>/dev/null)
fi
else
# 无后缀过滤时获取所有文件
while IFS= read -r -d $'\0' item; do
[[ -f "$item" ]] && contents+=("${item#$dir/}")
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f -print0 2>/dev/null)
fi
echo "${contents[@]}"
}
# 显示菜单(输出到 STDERR
show_menu() {
local terminal_rows=$(tput lines)
local terminal_cols=$(tput cols)
((items_per_page = terminal_rows - 6))
tput clear >&2
tput cup 0 0 >&2
# 显示标题
if [[ "$restrict_mode" -eq 0 ]]; then
echo -e "${COLOR_TITLE}当前目录: $current_dir" >&2
else
if [ "$current_dir" == "$start_dir" ]; then
echo -e "${COLOR_TITLE}当前目录 (锁定): $current_dir" >&2
else
echo -e "${COLOR_TITLE}当前目录: $current_dir" >&2
fi
fi
echo -e "${custom_prompt}${COLOR_NORMAL}" >&2
echo >&2
# 获取内容
local MENU_OPTIONS=($(get_directory_contents "$current_dir"))
local num_options=${#MENU_OPTIONS[@]}
((total_pages = (num_options + items_per_page - 1) / items_per_page))
((start_index = current_page * items_per_page))
((end_index = start_index + items_per_page))
((end_index > num_options)) && end_index=$num_options
# 显示选项
for ((i = start_index; i < end_index; i++)); do
local item="${MENU_OPTIONS[$i]}"
local display_item="$item"
local display_index=$((i - start_index))
if [[ "$item" == ".." ]]; then
display_item="${COLOR_BACK}[返回上级]${COLOR_NORMAL}"
elif [[ "$item" == */ ]]; then
display_item="${COLOR_DIR}[目录] ${item%/}${COLOR_NORMAL}"
fi
if [[ $display_index -eq $selected ]]; then
echo -e "${COLOR_HIGHLIGHT}${COLOR_SELECTION}$display_item${COLOR_NORMAL}" >&2
else
echo -e " $display_item" >&2
fi
done
# 显示页码
tput cup $((terminal_rows - 2)) 0 >&2
if ((total_pages > 1)); then
echo -e "${COLOR_PAGE}页码: $((current_page + 1))/$total_pages" >&2
echo -en "↑/↓ 导航 ←/→ 翻页 Enter 确认 B 返回上级 Q 退出${COLOR_NORMAL}" >&2
else
echo -en "${COLOR_PAGE}↑/↓ 导航 Enter 确认 B 返回上级 Q 退出${COLOR_NORMAL}" >&2
fi
tput ed >&2
}
# 主循环
show_menu
local key
while true; do
# 读取单个键
IFS= read -rsn1 key
# 处理方向键
if [[ "$key" == $'\x1B' ]]; then
# 读取额外的2个字节(方向键)
read -rsn2 -t 0.01 key
case "$key" in
'[A') # 上箭头
if (( selected > 0 )); then
((selected--))
elif (( current_page > 0 )); then
((current_page--))
# 计算上一页的项目数
local prev_items=$(( (terminal_rows - 6) ))
((selected = prev_items - 1))
fi
show_menu
;;
'[B') # 下箭头
local terminal_rows=$(tput lines)
((items_per_page = terminal_rows - 6))
local MENU_OPTIONS=($(get_directory_contents "$current_dir"))
local num_options=${#MENU_OPTIONS[@]}
((items_on_page = items_per_page))
(( items_on_page > num_options - current_page * items_per_page )) && \
items_on_page=$(( num_options - current_page * items_per_page ))
if (( selected < items_on_page - 1 )); then
((selected++))
elif (( current_page < total_pages - 1 )); then
((current_page++))
selected=0
fi
show_menu
;;
'[D') # 左箭头
if (( current_page > 0 )); then
((current_page--))
selected=0
show_menu
fi
;;
'[C') # 右箭头
local terminal_rows=$(tput lines)
((items_per_page = terminal_rows - 6))
local MENU_OPTIONS=($(get_directory_contents "$current_dir"))
local num_options=${#MENU_OPTIONS[@]}
((total_pages = (num_options + items_per_page - 1) / items_per_page))
if (( current_page < total_pages - 1 )); then
((current_page++))
selected=0
show_menu
fi
;;
esac
else
# 处理普通键
case "$key" in
'') # 回车
local terminal_rows=$(tput lines)
((items_per_page = terminal_rows - 6))
local MENU_OPTIONS=($(get_directory_contents "$current_dir"))
local num_options=${#MENU_OPTIONS[@]}
((global_index = current_page * items_per_page + selected))
if (( global_index < num_options )); then
local selected_item="${MENU_OPTIONS[$global_index]}"
if [[ "$selected_item" == ".." ]]; then
if [[ "$restrict_mode" -eq 1 ]] && [ "$current_dir" == "$start_dir" ]; then
continue
fi
[[ "$current_dir" != "/" ]] && current_dir=$(dirname "$current_dir")
[[ "$current_dir" == "//" ]] && current_dir="/"
current_dir=$(normalize_path "$current_dir")
dir_stack+=("$current_dir")
current_page=0
selected=0
show_menu
elif [[ "$selected_item" == */ ]]; then
local new_dir=$(normalize_path "$current_dir/${selected_item%/}")
dir_stack+=("$new_dir")
current_dir="$new_dir"
current_page=0
selected=0
show_menu
else
selected_file=$(normalize_path "${current_dir%/}/$selected_item")
break
fi
fi
;;
[bB]) # 返回上级
if [[ "$restrict_mode" -eq 1 ]] && [ "$current_dir" == "$start_dir" ]; then
continue
fi
[[ "$current_dir" != "/" ]] && current_dir=$(dirname "$current_dir")
current_dir=$(normalize_path "$current_dir")
dir_stack+=("$current_dir")
current_page=0
selected=0
show_menu
;;
[qQ]) # 退出
selected_file=""
break
;;
esac
fi
done
cleanup
echo "$selected_file" # 输出结果到 STDOUT
[[ -n "$selected_file" ]] && return 0 || return 1
}
# 使用示例
# source ./file_selector.sh
# echo "==== 文件选择器演示 ===="
# echo "当前目录内容:"
# ls -l
#
# selected_path=$(file_selector "$HOME" -r "请选择文本文件:" "txt")
#
# if [[ -n "$selected_path" ]]; then
# echo "您选择了: $selected_path"
# echo "文件内容:"
# head -n 5 "$selected_path" 2>/dev/null || echo "(目录内容无法显示)"
# else
# echo "未选择文件"
# fi
#
# echo "==== 脚本继续执行 ===="
export -f file_selector