如何将Bash脚本的read-while循环改写成支持单字符无回车输入的select菜单
Bash单选交互实现方案与代码优化
核心需求结论
Bash内置的select命令默认依赖行缓冲输入,必须按回车确认,无法原生支持read -n1的无回车单字符输入效果,强行结合需要修改终端属性,反而会增加不必要的复杂度,因此最优方案是优化现有read逻辑,而非切换到select实现。
原始代码问题梳理
- 外层
while true循环无实际作用:所有case分支都直接break,循环仅执行1次 - 大量重复代码:4个选项中有3个重复定义了
wmstream3 - 存在隐性bug:选项1的
wmpos变量未赋值就直接使用 - 无效输入逻辑不合理:原始代码中输入无效后直接break,不会重新提示用户输入
优化后的实现代码(推荐)
wmstream1="[2:v]lut=a=val*0.7,fade=in:st=5:d=2:alpha=1,fade=out:st=$length1:d=2:alpha=1[v2];" wmstream2="[v2][tmp2]scale2ref=w=oh*mdar:h=ih*0.1[wm_scaled][video];" # 提前定义默认值(对应选项1的右上角位置) wmpos="W-w-50:50" # 循环直到收到有效输入 while true; do read -n1 -p $'Select watermark position:\n1) Top right\n2) Top left\n3) Bottom left\n4) None\n' ans echo # 输入后自动换行优化显示 case $ans in 1) echo "WARNING: defaulting to top-right position." break ;; 2) echo "Positioning watermark at top left." wmpos="50:50" break ;; 3) echo "Positioning watermark at bottom left." wmpos="50:H-h-50" break ;; 4) echo "Disabling watermark." unset wmstream1 wmstream2 wmstream3="[tmp2]setsar=1[outv];" # 直接退出循环,不执行后面的通用wmstream3赋值 break 2 ;; *) echo "Invalid option selected. Select a valid number." # 不break,循环重新提示输入 ;; esac done # 通用wmstream3赋值,仅选项1/2/3会执行到这里 [[ -v wmstream1 ]] && wmstream3="[video][wm_scaled]overlay=$wmpos:format=auto:shortest=1[outv];"
优化点说明
- 把无用的外层循环调整为输入校验循环,无效输入时会重新提示用户选择,符合正常交互逻辑
- 提前定义
wmpos默认值,修复原始代码选项1的变量未定义bug - 把重复的
wmstream3赋值提取到循环外,减少冗余代码 - 简化变量unset写法,支持一次unset多个变量
强制使用select实现单字符输入的方案(不推荐)
如果确实要使用select结构,可以通过修改终端属性实现无回车输入,实现代码如下:
wmstream1="[2:v]lut=a=val*0.7,fade=in:st=5:d=2:alpha=1,fade=out:st=$length1:d=2:alpha=1[v2];" wmstream2="[v2][tmp2]scale2ref=w=oh*mdar:h=ih*0.1[wm_scaled][video];" wmpos="W-w-50:50" # 保存原有终端配置,关闭行缓冲和回显 old_stty=$(stty -g) stty -icanon -echo PS3="Select watermark position: " select opt in "Top right" "Top left" "Bottom left" "None"; do # 直接读取单字符作为选项编号 REPLY=$(dd bs=1 count=1 2>/dev/null) case $REPLY in 1) echo "WARNING: defaulting to top-right position." wmstream3="[video][wm_scaled]overlay=$wmpos:format=auto:shortest=1[outv];" break ;; 2) echo "Positioning watermark at top left." wmpos="50:50" wmstream3="[video][wm_scaled]overlay=$wmpos:format=auto:shortest=1[outv];" break ;; 3) echo "Positioning watermark at bottom left." wmpos="50:H-h-50" wmstream3="[video][wm_scaled]overlay=$wmpos:format=auto:shortest=1[outv];" break ;; 4) echo "Disabling watermark." unset wmstream1 wmstream2 wmstream3="[tmp2]setsar=1[outv];" break ;; *) echo "Invalid option selected. Select a valid number." ;; esac done # 恢复终端默认属性 stty "$old_stty"
该方案需要手动维护终端属性,脚本异常退出时可能导致终端输入异常,因此无特殊需求不建议使用。
内容的提问来源于stack exchange,提问作者Hashim Aziz
相关产品推荐
相关产品推荐

