Как можно вывести символ-разделитель и разрешить пользователю редактировать строку во время чтения стандартного ввода?

Как можно вывести символ-разделитель и разрешить пользователю редактировать строку во время чтения стандартного ввода?

Я пытаюсь написать простой скрипт, который считывает данные со стандартного ввода, используя ; символ в качестве разделителя для завершения строки ввода и позволяющий пользователю редактировать строку.

Вот мой тестовый сценарий:

#!/bin/bash

while true; do

  read -e -d ";" -t 180 -p "><> " srcCommand

  if [ $? != 0 ]; then
    echo "end;"
    echo ""
    exit 0
  fi
  case "$srcCommand" in
    startApp)
       echo "startApp command";;
    stopApp)
       echo "stopApp command";;
    end)
       echo ""
       exit 0
       ;;
    *)
       echo "unknown command";;
  esac
done

Это работает, но не выводит символ-разделитель ';':

# bash test.sh
><> startApp
startApp command
><> stopApp
stopApp command
><> end

Если я уберу опцию -e, вывод продолжится ; , но пользователь не сможет исправить свою ошибку с помощью символа возврата на одну позицию, а отображаемые строки будут располагаться сразу после разделителя:

# bash test.sh
><> startApp;startApp command
><> stopApp;stopApp command
><> end;

Как можно вывести символ-разделитель и разрешить пользователю редактировать строку во время чтения стандартного ввода?

Вот ожидаемое поведение:

# bash test.sh
><> startApp;
startApp command
><> stopApp;
stopApp command
><> end;

Спасибо

решение1

Я бы использовал zshтот вариант, где редактор строк имеет гораздо больше возможностей и его гораздо легче настраивать:

#! /bin/zsh -
insert-and-accept() {
  zle self-insert
  # RBUFFER= # to discard everything on the right
  zle accept-line
}
zle -N insert-and-accept
bindkey ";" insert-and-accept
bindkey "^M" self-insert
vared -p "><> " -c srcCommand

С bash-4.3или выше вы можете сделать что-то подобное с помощью хака вроде:

# bind ; to ^Z^C (^Z, ^C otherwide bypass the key binding when entered
# on the keyboard). Redirect stderr to /dev/null to discard the
# useless warning
bind '";":"\32\3"' 2> /dev/null

# new widget that inserts ";" at the end of the buffer.
# If we did bind '";":";\3"', readline would loop indefinitely
add_semicolon() {
  READLINE_LINE+=";"
  ((READLINE_POINT++))
}
# which we bind to ^Z
bind -x '"\32":add_semicolon' 2> /dev/null

# read until the ^C
read -e -d $'\3' -t 180 -p '><> ' srcCommand

Обратите внимание, что в этой версии ;всегда вставляется в конец входного буфера, а не в текущую позицию курсора. Измените на add_semicolon:

add_semicolon() {
  READLINE_LINE="${READLINE_LINE:0:READLINE_POINT++};"
}

Если вы хотите, чтобы он был вставлен в курсор, а все, что справа, было удалено. Или:

add_semicolon() {
  READLINE_LINE="${READLINE_LINE:0:READLINE_POINT};${READLINE_LINE:READLINE_POINT}"
  READLINE_POINT=${#READLINE_LINE}
}

если вы хотите вставить его в курсор, но хотите сохранить то, что находится справа, как в zshподходе.

Если вам не нужен ;in $srcCommand, вы всегда можете удалить его позже, srcCommand="${srcComman//;}"например, с помощью , но вам придется вставить его в виджет, чтобы он отображался с помощью zle/ readline.

Связанный контент