Выполнение цикла bash во время выполнения команды
Я хочу, чтобы построить bash script, который выполняет команду и тем временем выполняет другие вещи, с возможностью убить команду, если script убит. Скажем, выполняет ф большого файла и в то же время выводит время, прошедшее с момента начала копирования, но если script убит он убивает также копию.
Я не хочу использовать rsync по двум причинам: 1) медленный и 2) я хочу научиться, как это сделать, это может быть полезно.
Я пробовал это:
until cp SOURCE DEST
do
#evaluates time, stuff, commands, file dimensions, not important now
#and echoes something
done
но он не выполняет блок do - done
, так как он ожидает завершения копирования. Не могли бы вы предложить что-нибудь?
Ответы
Ответ 1
until
противоположно while
. Это не имеет никакого отношения к тому, чтобы делать вещи, пока выполняется другая команда. Для этого вам нужно выполнить свою задачу в фоновом режиме с помощью &
.
cp SOURCE DEST &
pid=$!
# If this script is killed, kill the `cp'.
trap "kill $pid 2> /dev/null" EXIT
# While copy is running...
while kill -0 $pid 2> /dev/null; do
# Do stuff
...
sleep 1
done
# Disable the trap on a normal exit.
trap - EXIT
kill -0
проверяет, запущен ли процесс. Обратите внимание, что это фактически не сигнализирует процесс и не убивает его, как может показаться имя. Не с сигналом 0, по крайней мере.
Ответ 2
В решении вашей проблемы предпринимаются три шага:
-
Выполните команду в фоновом режиме, чтобы она продолжала работать, пока ваш script выполняет что-то еще. Вы можете сделать это, выполнив команду &
. Подробнее см. В разделе в разделе "Управление заданиями" в справочном руководстве Bash.
-
Отслеживать этот статус команды, чтобы вы знали, что он все еще работает. Вы можете сделать это со специальной переменной $!
, которая установлена на идентификатор PID (идентификатор процесса) последней команды, которую вы выполняли в фоновом режиме, или пустая, если не была запущена фоновая команда. Linux создает каталог /proc/$PID
для каждого запущенного процесса и удаляет его, когда процесс завершается, поэтому вы можете проверить наличие этого каталога, чтобы узнать, все ли работает фоновая команда. Вы можете узнать больше, чем когда-либо хотели узнать о /proc
из проекта документации Linux Страница файловой системы или Дополнительно Bash -Scripting Guide.
-
Убейте команду фона, если ваш script будет убит. Вы можете сделать это с помощью команды trap
, которая является bash встроенной командой.
Сложение частей:
# Look for the 4 common signals that indicate this script was killed.
# If the background command was started, kill it, too.
trap '[ -z $! ] || kill $!' SIGHUP SIGINT SIGQUIT SIGTERM
cp $SOURCE $DEST & # Copy the file in the background.
# The /proc directory exists while the command runs.
while [ -e /proc/$! ]; do
echo -n "." # Do something while the background command runs.
sleep 1 # Optional: slow the loop so we don't use up all the dots.
done
Обратите внимание, что мы проверяем каталог /proc
, чтобы узнать, работает ли фоновая команда, потому что kill -0
генерирует ошибку, если она вызывается, когда процесс больше не существует.
Обновить, чтобы объяснить использование trap
:
Синтаксис trap [arg] [sigspec …]
, где sigspec …
- это список сигналов для catch, а arg
- это команда для выполнения, когда какой-либо из этих сигналов поднимается. В этом случае команда представляет собой список:
'[ -z $! ] || kill $!'
Это обычная идиома Bash, которая использует способ обработки ||
. Выражение формы cmd1 || cmd2
будет оцениваться как успешное, если выполняется либо cmd1
OR cmd2
. Но Bash умный: если cmd1
преуспевает, Bash знает, что полное выражение также должно быть успешным, поэтому не стоит оценивать cmd2
. С другой стороны, если cmd1
терпит неудачу, результат cmd2
определяет общий результат выражения. Поэтому важной особенностью ||
является то, что она выполнит cmd2
только в случае неудачи cmd1
. Это означает, что это ярлык для (недопустимой) последовательности:
if cmd1; then
# do nothing
else
cmd2
fi
Имея это в виду, мы видим, что
trap '[ -z $! ] || kill $!' SIGHUP SIGINT SIGQUIT SIGTERM
будет проверять, является ли $!
пустым (что означает, что фоновая задача никогда не выполнялась). Если это не удается, это означает, что задача выполнена, она убивает задачу.
Ответ 3
То, что вы хотите, - это сделать сразу две вещи в оболочке. Обычный способ сделать это с заданием. Вы можете запустить фоновое задание, закончив команду амперсандом.
copy $SOURCE $DEST &
Затем вы можете использовать команду jobs
, чтобы проверить ее статус.
Подробнее:
Ответ 4
вот самый простой способ сделать это с помощью ps -p
:
[command_1_to_execute] &
pid=$!
while ps -p $pid &>/dev/null; do
[command_2_to_be_executed meanwhile command_1 is running]
sleep 10
done
Это будет работать каждый 10 seconds
command_2
, если command_1
все еще работает в фоновом режиме.
надеюсь, что это вам поможет:)