Bash中使用read命令时,如何避免退格导致整行被清除?
解决Bash菜单输入退格导致界面损坏的问题
问题原因
退格删除首个输入字符时,终端的退格操作会继续删除光标左侧的提示文本和菜单边框字符——默认的read -e不会限制光标移动范围,最终导致界面被破坏。
以下针对你的需求逐一给出实现方案:
1. 检测退格到首个字符时重绘菜单
通过逐字符读取输入并跟踪长度,当输入为空且收到退格键时,重新绘制菜单和提示:
#!/bin/bash function menu_display { clear tput cup 0 0; echo " ┌───────────────────────────────────────────────────────────┐" tput cup 1 0; echo " │ │" tput cup 2 0; echo " │ │" tput cup 3 0; echo " │ │" tput cup 4 0; echo " │ │" tput cup 5 0; echo " │ │" tput cup 6 0; echo " │ │" tput cup 7 0; echo " ╘═══════════════════════════════════════════════════════════╛" } function ask_question { local INPUT_TEXT="" local CHAR menu_display tput cup 3 3; echo -n "Type some text here: " local PROMPT_LEN=$(( ${#"Type some text here: "} + 3 )) while true; do read -n 1 -s -r CHAR # 处理回车键结束输入 if [[ "$CHAR" == $'\x0a' || "$CHAR" == $'\x0d' ]]; then echo break fi # 处理退格键(BS: \x08, DEL: \x7f) if [[ "$CHAR" == $'\x08' || "$CHAR" == $'\x7f' ]]; then if [[ -n "$INPUT_TEXT" ]]; then INPUT_TEXT="${INPUT_TEXT%?}" tput cub 1; tput dch 1 else # 无输入时重绘界面 menu_display tput cup 3 3; echo -n "Type some text here: " tput cup 3 $PROMPT_LEN fi continue fi # 处理普通字符 INPUT_TEXT+="$CHAR" echo -n "$CHAR" done echo "You entered: $INPUT_TEXT" } ask_question exit 0
2. 禁止删除首个输入字符(但允许修改)
逐字符读取输入,仅当输入不为空时响应退格键,确保提示文本不被擦除:
#!/bin/bash function menu_display { clear tput cup 0 0; echo " ┌───────────────────────────────────────────────────────────┐" tput cup 1 0; echo " │ │" tput cup 2 0; echo " │ │" tput cup 3 0; echo " │ │" tput cup 4 0; echo " │ │" tput cup 5 0; echo " │ │" tput cup 6 0; echo " │ │" tput cup 7 0; echo " ╘═══════════════════════════════════════════════════════════╛" } function ask_question { local INPUT_TEXT="" local CHAR menu_display tput cup 3 3; echo -n "Type some text here: " local PROMPT_LEN=$(( ${#"Type some text here: "} + 3 )) while true; do read -n 1 -s -r CHAR if [[ "$CHAR" == $'\x0a' || "$CHAR" == $'\x0d' ]]; then echo break fi if [[ "$CHAR" == $'\x08' || "$CHAR" == $'\x7f' ]]; then # 仅输入不为空时允许退格 if [[ -n "$INPUT_TEXT" ]]; then INPUT_TEXT="${INPUT_TEXT%?}" tput cub 1; tput dch 1 fi continue fi INPUT_TEXT+="$CHAR" echo -n "$CHAR" done echo "You entered: $INPUT_TEXT" } ask_question exit 0
3. 允许删除首个字符但阻止光标跳行首
输入为空时,强制将光标拉回输入区域起始位置,避免擦除提示文本:
#!/bin/bash function menu_display { clear tput cup 0 0; echo " ┌───────────────────────────────────────────────────────────┐" tput cup 1 0; echo " │ │" tput cup 2 0; echo " │ │" tput cup 3 0; echo " │ │" tput cup 4 0; echo " │ │" tput cup 5 0; echo " │ │" tput cup 6 0; echo " │ │" tput cup 7 0; echo " ╘═══════════════════════════════════════════════════════════╛" } function ask_question { local INPUT_TEXT="" local CHAR menu_display tput cup 3 3; echo -n "Type some text here: " local PROMPT_POS=$(( ${#"Type some text here: "} + 3 )) tput cup 3 $PROMPT_POS while true; do read -n 1 -s -r CHAR if [[ "$CHAR" == $'\x0a' || "$CHAR" == $'\x0d' ]]; then echo break fi if [[ "$CHAR" == $'\x08' || "$CHAR" == $'\x7f' ]]; then if [[ -n "$INPUT_TEXT" ]]; then INPUT_TEXT="${INPUT_TEXT%?}" tput cub 1; tput dch 1 else # 强制光标回到输入起始位置 tput cup 3 $PROMPT_POS fi continue fi INPUT_TEXT+="$CHAR" echo -n "$CHAR" done echo "You entered: $INPUT_TEXT" } ask_question exit 0
4. 更优实现方案(支持多行输入)
将提示区域与输入区域分离,输入内容在提示下方的固定区域显示,既避免退格破坏界面,又支持无长度限制的多行输入:
#!/bin/bash function menu_display { clear tput cup 0 0; echo " ┌───────────────────────────────────────────────────────────┐" tput cup 1 0; echo " │ Enter your text below (press Ctrl+D to finish): │" tput cup 2 0; echo " │ │" tput cup 3 0; echo " │ │" tput cup 4 0; echo " │ │" tput cup 5 0; echo " │ │" tput cup 6 0; echo " │ │" tput cup 7 0; echo " ╘═══════════════════════════════════════════════════════════╛" } function ask_question { local INPUT_TEXT="" local LINE local CUR_ROW=3 menu_display tput cup $CUR_ROW 3 # 读取多行输入,直到Ctrl+D结束 while IFS= read -r LINE; do INPUT_TEXT+="$LINE"$'\n' CUR_ROW=$((CUR_ROW + 1)) # 输入超出菜单区域时滚动显示最后4行 if [[ $CUR_ROW -gt 6 ]]; then menu_display CUR_ROW=3 echo "$INPUT_TEXT" | tail -4 | while read -r L; do tput cup $CUR_ROW 3; echo -n "$L" CUR_ROW=$((CUR_ROW + 1)) done fi tput cup $CUR_ROW 3 done INPUT_TEXT="${INPUT_TEXT%$'\n'}" echo -e "\nYou entered:\n$INPUT_TEXT" } ask_question exit 0
内容的提问来源于stack exchange,提问作者Howard Rogers
相关产品推荐
相关产品推荐

