You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 09:50:25