2012-05-17 5 views
3

У меня есть старый сценарий оболочки, который нужно переместить в bash. Этот скрипт печатает ход какой-либо активности и ждет пользовательских команд. Если в течение 15 секунд пользователь не предпринимает никаких действий, он перерисовывается с новым прогрессом, и таймер снова запускается. Вот моя проблема:Linux bash - перепечатка пользовательского ввода

Я пытаюсь использовать read -t 15 myVar - таким образом, через 15 секунд цикл ожидания будет перезапущен. Существует, однако сценарий, который приносит мне проблему:

  • экран перерисовывается и сценарий ожидает ввода (печатает «Enter command:»)
  • пользователь вводит foo, но не давит войти
  • через 15 секунд экран снова перерисованы и сценарий ожидает ввода - отметить, что foo не отображается в любом месте на экране (гравюр «Enter command:»)
  • пользователь вводит bar и прижимает введите

В настоящий момент переменная $myVar содержит «foobar».

Что мне нужно? Я ищу способ найти первую строку, набранную пользователем, поэтому я смог повторно отобразить ее после обновления статуса. Таким образом, пользователь увидит: Enter command: foo

В Solaris я мог бы использовать stty -pendin, чтобы сохранить вход в какой-то буфер, и после обновления запуска stty pendin, чтобы получить этот вход из буфера и распечатать его на экране.

Есть ли эквивалент Linux для stty pendin? Или, может быть, вы знаете какое-то решение bash для моей проблемы?

+0

Чтобы уточнить, вам требуется, чтобы через 15 секунд экран был перерисован? Зачем? – Sorpigal

+0

Да, мне нужно, чтобы экран перерисовывался каждые 15 секунд. Этот скрипт контролирует ход выполнения некоторой процедуры на сервере. Когда каждый пользователь перерисовывает, как он прогрессирует. Без тайм-аута пользователю приходилось время от времени нажимать, чтобы видеть изменения, которые мне не нужны. –

ответ

1

Хорошо, думаю, у меня есть решение. Я взял nhed-й предложение и работал немного на нем :)

Основного код печатает определенный статус и ожидает ввод:

while : 
do 
    # print the progress on the screen 
    echo -n "Enter command: " 
    tput sC# save the cursor position 
    echo -n "$tmpBuffer" # this is buffer which holds typed text (no 'ENTER' key yet) 
    waitForUserInput 
    read arguments <<< $(echo $mainBuffer) # this buffer is set when user presses 'ENTER' 
    mainBuffer="" # don't forget to clear it after reading 
    # now do the action requested in $arguments 
done 

Функции waitForUserInput ждет 10 секунд для нажатия клавиши. Если ничего не набирается - выходы, но уже введенные ключи сохраняются в буфере. Если нажата клавиша, она анализируется (добавляется в буфер или удаляется из буфера в случае обратного пространства). Буфер ввода сохраняется в другом буфере, из которого он считывается для дальнейшей обработки:

function waitForUserInput { 
    saveIFS=$IFS # save current IFS 
    IFS="" # change IFS to empty string, so 'ENTER' key can be read 

    while : 
    do 
     read -t10 -n1 char 
     if (($? == 0)) 
     then 
      # user pressed something, so parse it 
      case $char in 
       $'\b') 
        # remove last char from string with sed or perl 
        # move cursor to saved position with 'tput rc' 
        echo -n "$tmpBuffer" 
        ;; 
       "") 
        # empty string is 'ENTER' 
        # so copy tmpBuffer to mainBuffer 
        # clear tmpBuffer and return to main loop 
        IFS=$saveIFS 
        return 0 
        ;; 
       *) 
        # any other char - add it to buffer 
        tmpBuffer=$tmpBuffer$char 
        ;; 
      esac 
     else 
      # timeout occured, so return to main function 
      IFS=$saveIFS 
      return 1 
     fi 
    done 
} 

Спасибо вам всем за помощь!

+0

FYI, вам не нужно сохранять и восстанавливать IFS. Взгляните на мой ответ, как вызвать 'read' с временным IFS. Кроме того, не забудьте процитировать расширения! – Sorpigal

2

Я предполагаю, что одним способом было бы вручную накапливать пользовательский ввод ... используйте read с -n1, чтобы он возвращался после каждого символа, затем вы можете добавить его в свою строку. Для предотвращения чрезмерного чертежа вы должны подсчитать, сколько оставшиеся секунды на вторые часы 15 ...


Добавление:

Для вашего комментария о пространстве/возврата - вы в принципе хотите использовать IFS без символов, которые вы хотите, например, пространство

пример:

XIFS="${IFS}" # backup IFS 
IFS=$'\r'  # use a character your user is not likely to enter (or just the same but w/o the space) 


# Enter will look like en empty string 
$ read -n1 X; echo --; echo -n "${X}" | od -tx1 

-- 
0000000 


# space will be presented as a character 
$ read -n1 X; echo --; echo -n "${X}" | od -tx1 
-- 
0000000 20 
0000001 


# after you are all done, you probably wantto restore the IFS 
IFS="${XIFS}" 
+0

Да, кажется, что это будет лучший подход. Я работаю над решением, как вы описали, но я ударил другую стену - мне нужно правильно обработать «пробел» и «ввести» (пробелы разрешены в командах). Однако bash утверждает, что «пробел» и «ввод» являются пустой строкой при чтении с помощью «read -n1» - любая идея, почему? –

+0

@PatrykBaranowski Можете ли вы попробовать изменить IFS? (просто догадка) – nhed

+0

@PatrykBaranowski см. расширенный мой ответ на адрес вашего последнего комментария – nhed

1

Расширение на то, что @nhed говорил, возможно, что-то вроде этого:

#!/bin/bash 

limit=5 

draw_screen() { 
    clear; 
    echo "Elapsed time: $SECONDS" # simulate progress indicator 
    printf 'Enter command: %s' "$1" 
} 

increment=$limit 
str= 
end=0 
while ! [ $end -eq 1 ] ; do 
    draw_screen "$str" 
    while [ $SECONDS -lt $limit ] && [ $end -eq 0 ] ; do 
      c= 
      IFS= read -t $limit -r -n 1 -d '' c 
      if [ "$c" = $'\n' ] ; then 
        end=1 
      fi 
      str="${str}${c}" 
    done 
    let limit+=increment 
done 
str="${str%$'\n'}" # strip trailing newline 

echo "input was: '$str'" 

решение не идеально:

  • Вы иногда можно вводить в середине цикла и беспорядок ввода
  • Вы ничего не можете редактировать красиво (но это поправимо с гораздо больше работы)

Но, возможно, этого вам достаточно.

1

Если у вас есть баш 4:

read -p 'Enter something here: ' -r -t 15 -e -i "$myVar" myVar 

-e включается Readline поддержка ввода текста пользователя. -i использует следующий текст в качестве содержимого по умолчанию входного буфера, который он отображает для пользователя. Следующий текст в этом случае - это предыдущее содержимое переменной, которую вы читаете.

Демонстрация:

$ myVar='this is some text' # simulate previous entry 
$ read -p 'Enter something here: ' -r -t 15 -e -i "$myVar" myVar 
Enter something here: this is some text[] 

Где [] представляет курсор. При необходимости пользователь сможет вернуться и исправить предыдущий текст.

+0

Денис, спасибо за предложение. К сожалению, код должен работать как в bash3, так и в bash4. Кроме того, это не касается случая, описанного в начальном вопросе - когда пользователь вводит что-то, но не нажимает клавишу «ENTER», тогда $ myVar пуст. –

Смежные вопросы