Estou tentando escrever um script simples que leia a entrada padrão, usando ;
caractere como delimitador para encerrar a linha de entrada e que permita ao usuário editar a linha.
Aqui está meu script de teste:
#!/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
Isso funciona, mas não imprime o delimitador ';' Caracteres:
# bash test.sh
><> startApp
startApp command
><> stopApp
stopApp command
><> end
Se eu remover a opção -e, ela será impressa, ;
mas o usuário não poderá corrigir seu erro usando o caractere backspace e as strings ecoadas estarão logo após o delimitador:
# bash test.sh
><> startApp;startApp command
><> stopApp;stopApp command
><> end;
Como posso imprimir o caractere delimitador e permitir que o usuário edite a linha enquanto lê a entrada padrão?
Este é o comportamento esperado:
# bash test.sh
><> startApp;
startApp command
><> stopApp;
stopApp command
><> end;
Obrigado
Responder1
Eu usaria zsh
onde o editor de linha tem muito mais recursos e é muito mais personalizável:
#! /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
Com bash-4.3
ou superior, você pode fazer algo semelhante com um hack como:
# 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
Observe que nessa versão, o ;
é sempre inserido no final do buffer de entrada, e não na posição atual do cursor. Altere add_semicolon
para:
add_semicolon() {
READLINE_LINE="${READLINE_LINE:0:READLINE_POINT++};"
}
Se quiser inseri-lo no cursor e tudo à direita descartado. Ou:
add_semicolon() {
READLINE_LINE="${READLINE_LINE:0:READLINE_POINT};${READLINE_LINE:READLINE_POINT}"
READLINE_POINT=${#READLINE_LINE}
}
se você deseja inseri-lo no cursor, mas deseja preservar o que está à direita, como na zsh
abordagem.
Se você não quiser o ;
in $srcCommand
, você pode retirá-lo depois, srcCommand="${srcComman//;}"
por exemplo, mas precisará inseri-lo no widget para que seja exibido por zle
/ readline
.