Сценарий оболочки: добавьте «выход, набрав q», пока do case уже зависит от if

Если это вас беспокоит, то вы можете легко использовать интерфейс xscreensaver-command -watch для запуска ssh-add -D, когда экран заблокирован. Посмотрите на странице man очень простой пример.

1
24.04.2019, 18:15
2 ответа

Более простым решением было бы проинформировать пользователя о необходимости нажать Ctrl-Cдля выхода из цикла, что выдаст сигнал SIGINTи завершит сценарий.

Если вы хотите сделать что-то еще перед завершением скрипта, вы можете перехватить сигнал:

cleanup() {
  #...
  exit 0
}
trap 'cleanup' INT

Кроме того, вы можете запустить одну из них в подоболочке, убедившись, что при выходе из одной из оболочек другая также уничтожается. Таким образом, вы можете заблокировать один из них для ввода, в то время как другой выполняет другие проверки.

#!/bin/bash

i=1
sp="/-\|"
no_config=true
echo "type \"continue\" to exit this while loop if you feel the conditions for continuing sucessfully have been met... "
(
while $no_config
do 
  printf "\b${sp:i++%${#sp}:1}"
  [[ ! $(pidof SupremeCommande) && -f ~/My\ Documents/newfile ]] && no_config=false
  sleep 1
done
kill $$
) &
child_pid=$!

while $no_config
do
  read -r typed_continue
  [[ "$typed_continue" = "continue" ]] && no_config=false
  sleep 1
done
kill $child_pid
2
27.01.2020, 23:18

К сожалению, Bash плохо подходит для таких элементов управления.

Однако есть несколько способов получить что-то работоспособное.

Вот некоторые из них, от самых простых до самых сложных:

Нажмите Ctrl+C для выхода

Вы можете установить trapдля Ctrl+C (, обрабатываемого путем перехвата сигнала INT ), чтобы он установил значение no_configв false, чтобы ваш цикл whileзавершался при нажатии Ctrl+ С:

i=1
sp='/-\|'
no_config=true
echo "type Ctrl+C to exit this while loop"
echo "if you feel the conditions for continuing successfully have been met... ";
trap 'no_config=false' INT
while $no_config; do
    printf "\b${sp:i++%${#sp}:1}"
    [[ ! $(pidof SupremeCommande) && -f ~/My\ Documents/newfile ]] && no_config=false
    sleep 1
done
trap 'trap - INT; kill -INT $$' INT
echo "do more stuff"

(Если вы запускаете приведенный выше код из интерактивной оболочки , не забудьте окружить весь код начальным (и конечным ), чтобы ваша оболочка не была загрязнена кодом. делает.)

Обратите внимание, что цикл whileбудет фактически прерван только тогда, когда все команды внутри цикла будут завершены, потому что тест на $no_configпроисходит только на while. Вы можете или не хотите этого поведения в зависимости от того, что вы делаете внутри цикла. В вашем примере единственной истинной «медленной» операцией является явное sleep 1, поэтому резкое прерывание не требуется, но если вы хотите прервать цикл, не имеет значения --, что тогда самое простое дополнение заключается в том, что с помощью одного вспомогательного -снаряда можно убить в целом. Вот так:

trap 'kill $!' INT
(
i=1
sp='/-\|'
no_config=true
echo "type Ctrl+C to exit this while loop"
echo "if you feel the conditions for continuing successfully have been met... ";
while $no_config; do
    printf "\b${sp:i++%${#sp}:1}"
    [[ ! $(pidof SupremeCommande) && -f ~/My\ Documents/newfile ]] && no_config=false
    sleep 1
done
) &
wait
trap 'trap - INT; kill -INT $$' INT
echo "do more stuff"

Здесь мы устанавливаем trapдля INT (Ctrl+C ), что убивает вспомогательную оболочку -, так что waitпереходит к части «дополнительные действия».

Однако обратите внимание, что при таком способе переменные, установленные в цикле, не распространяются на часть «дополнительные действия». Чтобы обойти это, вам потребуются некоторые вспомогательные средства связи, такие как временный файл или именованный -FIFO.


Введите 'q' для выхода

Альтернативой вышеизложенному, которая позволит вам использовать любой символ для выхода (, а также оставить Ctrl+C в его обычной работе ), может быть встраивание неблокирующего -read(, доступного в Bash. v4+ )внутри цикла. Это также требует некоторых вспомогательных приготовлений для настройки терминала.Все может быть так:

term_settings="$(stty -g)"
trap 'stty "${term_settings}"' EXIT
i=1
sp='/-\|'
no_config=true
echo "type exactly \"q\" to exit this while loop"
echo "if you feel the conditions for continuing successfully have been met... ";
stty -icanon -echo
while $no_config; do
    while read -t 0 && read -rN 1 ; do [ "${REPLY}" = q ] && break 2 || true; done
    printf "\b${sp:i++%${#sp}:1}"
    [[ ! $(pidof SupremeCommande) && -f ~/My\ Documents/newfile ]] && no_config=false
    sleep 1
done
stty "${term_settings}"
echo "do more stuff"

Здесь мы сначала сохраняем текущие настройки терминала, чтобы восстановить их позже и при ВЫХОДЕ, затем отключаем отображение набираемого символа(-echo)и строки -, ориентированного на ввод с клавиатуры(-icanon). В цикле whileмы используемread -t 0(не-блокирующийread)для проверки наличия типизированных символов, и в этом случае мы читаем их все один за другим (-N 1), чтобы проверить, является ли какой-либо из них q], чтобы выйти из цикла.

Это увеличивает вычислительную нагрузку из-за того, что read -t 0выполняется на каждой итерации цикла while. В вашем примере накладные расходы незначительны.


Если вы хотите внезапно прерываемый цикл с type q to quit, необходим другой подход, и все становится намного сложнее, потому что вам приходится запускать прерываемую часть вашего кода в фоновом режиме, ожидая readв коде переднего плана, и это также требует способа разблокировать read, когда фоновый код завершается регулярно.

Чтобы получить это, требуется сильно расширить способности Баша, например:

  1. помещение прерываемой части вашего кода в фоновую подпрограмму -отдельно, что немного похоже на то, что мы делали ранее, но с дополнительными предосторожностями
  2. имитация Ctrl+C из фоновой подпрограммы -на передний план read.

Это также означает, что будет невозможно передать переменные из цикла whileв часть «дополнительные действия», как мы уже говорили ранее (вспомогательная связь должна быть настроена специально ).

Пример может быть таким:

#!/bin/bash

term_settings="$(stty -g)"
(
trap 'stty "${term_settings}"' EXIT
_keyboard_waiter=$BASHPID
(
i=1
sp='/-\|'
no_config=true
echo "type exactly \"q\" to exit this while loop"
echo "if you feel the conditions for continuing successfully have been met... ";
while $no_config; do
    printf "\b${sp:i++%${#sp}:1}"
    [[ ! $(pidof SupremeCommande) && -f ~/My\ Documents/newfile ]] && no_config=false
    sleep 1
done
kill -INT $_keyboard_waiter
) &
while read -rsN 1 && ! [ "${REPLY}" = q ] ; do :; done
{ kill $! && wait; } 2>/dev/null
)
echo "do more stuff"

Здесь имеем:

  • Лучше использовать все это через файл сценария из-за необходимости обработки сигналов
  • настройки терминала, сохраненные в начале, чтобы восстановить их позже при ВЫХОДЕ,что необходимо, когда readпрерывается
  • одна подпрограмма -оболочка запускается на переднем плане, отвечает за ожидание ввода с клавиатуры
  • одна подпрограмма -подпрограмма -запуск оболочки в фоновом режиме (и )для прерываемой части кода
  • использование kill -INTфоновой подпрограммой -для прерывания подпрограммы read-. Это имитирует Ctrl+C для этой подпрограммы -, которая обрабатывается Bash
  • .
  • параметр -ed read, чтобы не отображать введенные символы (-s), а также ожидать/принимать только один символ независимо от новой строки (-N 1), плюс общий -r, чтобы не интерпретировать обратную косую черту как escape-символ
  • простое «убийство и ожидание» для фоновой подпрограммы -когда действительно набирается «q»
2
27.01.2020, 23:18

Теги

Похожие вопросы