Como posso imprimir o caractere delimitador e permitir que o usuário edite a linha enquanto lê a entrada padrão?

3

Estou tentando escrever um script simples que lê a entrada padrão, usando o caractere ; como delimitador para finalizar a linha de entrada e permitir que o usuário edite a linha.

Aqui está o 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 ';' char:

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

Se eu remover a opção -e, ele imprime ; , mas o usuário não pode corrigir o erro usando o caractere de retrocesso e as sequências de eco sã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 durante a leitura da entrada padrão?

Esse é o comportamento esperado:

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

Obrigado

    
por Lety 26.10.2016 / 15:38

1 resposta

4

Eu usaria zsh , onde o editor de linhas 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 acima, 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 '";":""' 2> /dev/null

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

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

Observe que, nessa versão, o ; é sempre inserido no final do buffer de entrada, não na posição atual do cursor. Altere o add_semicolon para:

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

Se você quiser inseri-lo no cursor e tudo o que está à direita é descartado. Ou:

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

se você quiser inseri-lo no cursor, mas quiser preservar o que está à direita, como na abordagem zsh .

Se você não quiser o ; em $srcCommand , você pode retirá-lo depois com srcCommand="${srcComman//;}" , por exemplo, mas será necessário inseri-lo no widget para que ele seja exibido por zle / readline .

    
por 26.10.2016 / 17:04