Я пытаюсь написать простой скрипт, который считывает данные со стандартного ввода, используя ;
символ в качестве разделителя для завершения строки ввода и позволяющий пользователю редактировать строку.
Вот мой тестовый сценарий:
#!/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
.