Выполнение цикла 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 все еще работает в фоновом режиме.

надеюсь, что это вам поможет:)