逐个字符地读取和处理字符串,但允许用户对输入进行简单的行编辑

逐个字符地读取和处理字符串,但允许用户对输入进行简单的行编辑

我想从终端增量读取一行输入,并允许用户一些基本的行编辑功能; INS、DEL、右、左、结束、退格

每次修改字符串时,我都想对其进行处理,以对文本文件进行增量正则表达式搜索。

这些编辑键和其他键生成多个输入字符,这
使得解释输入变得相当困难,例如 C-Left 生成 6 个字符。

有没有一种简单的方法来实现这种逐字符可编辑输入?
我特别有兴趣知道如何在 bash 中执行此操作,因为其余的处理将是 bash.. 也欢迎其他建议..

这就是我开始的方式,但是对于如此多种潜在的控制代码,它有点失控。

#!/bin/bash
IFS=$'\n' 
while true ;do
  read -n 1 c
  ((${#c}==0)) && break # Exit the loop. Input length is 0  
                        # ie. The user has pressed Enter
  echo "xx=$(echo -n "$c"|xxd -p)="
  # 1b 5b 32 7e  "INS"
  # 1b 5b 33 7e  "DEL"
  # 1b 5b 43     "RIGHT"
  # 1b 5b 44     "LEFT"
  # 1b 5b 46     "END"
  # 1b 5b 48     "HOME"
  # 7f           "BACKSPACE"
done

答案1

如果您使用 一次读取一个字符read -n,则必须实现一个键序列解析器。您可以使用以下方法构建一个适用于大多数终端的缓慢而肮脏的解决方案:考虑功能键转义序列以转义字符开头,并以任意数量的字符继续,后面0-9;[]O跟着一个不在此集合中的最后一个字符。

读取输入的更好方法是使用适当的输入库。 Bash 将其用于其自身目的(阅读线)。通过使用bind内置声明您自己的键绑定,您可以获得有限的接口;专门bind -x用于在按键时运行 shell 命令。由于接口有限,实现您想要的内容可能是可能的,但很困难。

Zsh有自己的输入库,兹勒。它的界面比 bash 丰富得多。使用 zle,您可以定义任意键盘映射,并且可以通过 shell 代码更好地访问 zle 的内部结构。用于zle将 shell 函数分配给 zle 用户定义的命令(称为小部件),bindkey创建并填充您自己的键盘映射,最后vared使用您选择的键盘映射读取一行输入。

答案2

read如果有字符,您可以在第一个终端之后将终端设置为原始模式esc,然后使用第二个终端read来读取和解析剩余的字节(如果有)(参见BASH逃避性格创伤另请参阅箭头.txt)。

#!/bin/bash

# tested on Mac OS X 10.6.8

IFS=$'\n'
old_tty_settings="$(stty -g)"
exec 0</dev/tty

tput smir  # enable insert mode

while IFS="" read -r -s -n1 key; do    # first read (reads only a single byte)

#od -c <<<"$key"
#continue

((${#key}==0)) && break

case "$key" in
  $'\001')  printf '\r'                          #  ctrl-a
            continue;;   
  $'\177')  tput rmir 
            printf "\010\040\010\033[P"          # backspace
            tput smir
            continue;;                          
  $'\025')  printf "\033[1K"                     # tput el1 does not work on Mac OS X 10.6.8 
            continue;;                           # ctrl-u  (clear to start of line)
  $'\v')    tput el
            continue;;                           # ctrl-k  (clear to end of line)
esac

# if the first char is esc (i.e. \e or \033 respectively)
if [[ "$key" == $'\033' ]]; then    

  stty cbreak -echo min 0 time 0   # set raw terminal 

  IFS="" read -r bytes     # second read (reads remaining bytes)

  if [[ ${#bytes} -gt 0 ]]; then

     stty "$old_tty_settings"

     case "${key}${bytes}" in 
       $'\033[3~')   tput dch1         # delete one char
                     continue;; 
     esac

     printf "${key}${bytes}"

  else
     stty "$old_tty_settings"
  fi

 else

  #stty "$old_tty_settings"
  printf '%s' "$key"

fi

done

echo

exit 0

答案3

看一下命令行工具选择器

# usage examples
selector -v -x @ <(find . -maxdepth 2 -type d | awk '{print $0"@cd "$0}')
selector -v -x @ <(grep -E -o 'http[^ ]+' fileWithURLS)

答案4

仅使用 bash 内置函数来不使用任何脚本:

read -e INPUT  # single line

INPUT=$(</dev/stdin)  # multiple lines

来源

或者,安装该ledit软件包,然后您可以使用:

INPUT=$(ledit)

相关内容