Как запустить команду после того, как уже запущенная, существующая заканчивается?

32

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

Так, например, если я бегу:

really_long_script.sh

и нажмите ввод ... как я могу запустить другую команду после ее завершения?

mlissner
источник

Ответы:

45

Вы можете разделить несколько команд ;, чтобы они выполнялись последовательно, например:

really_long_script.sh ; echo Finished

Если вы хотите выполнить следующую программу, только если скрипт завершил работу с кодом возврата 0 (что обычно означает, что он выполнен правильно), то:

really_long_script.sh && echo OK

Если вы хотите обратное (то есть продолжить только в случае сбоя текущей команды), чем:

really_long_script.sh || echo FAILED

Вы можете запустить ваш скрипт в фоновом режиме (но будьте осторожны, вывод скриптов ( stdoutи stderr) будет продолжать идти на ваш терминал, если вы не перенаправите его куда-нибудь), а затем waitдля него:

really_long_script.sh &
dosomethingelse
wait; echo Finished

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

fg ; echo Finished

Где fgвыводит приостановленный процесс на передний план ( bgзаставит его работать в фоновом режиме, во многом как началось с &)

земля
источник
@mlissner Расширил мой ответ, чтобы охватить этот случай
Аланд
8
Так как fgвозвращает значение задания, которое оно возобновляет, вы можете использовать его, fg && echo Finishedесли хотите убедиться, что команда выполнена успешно перед выполнением другой команды.
Palswim
13

Вы также можете использовать контроль работы Bash. Если вы начали

$ really_long_script.sh

затем нажмите Ctrl + Z, чтобы приостановить его:

^Z
[1]+  Stopped                 really_long_script.sh
$ bg

перезапустить задание в фоновом режиме (как если бы оно началось с really_long_script.sh &). Тогда вы можете подождать эту фоновую работу с

$ wait N && echo "Successfully completed"

где N - идентификатор задания (вероятно, 1, если вы не выполняли никаких других фоновых заданий), который также отображается, как [1]указано выше.

Яап Элдерни
источник
11

Если процесс не выполняется на текущем tty, попробуйте это:

watch -g ps -opid -p <pid>; mycommand
Маринос Ан
источник
2
Я люблю творческое использование watch. Супер короткий ответ, и каждый узнает новый полезный способ использования часов
Петр Чапла
2

Оказывается, это не так сложно: вы можете просто набрать следующую команду в окне, пока работает существующая, нажать клавишу ввода, и когда первая из них завершится, вторая команда автоматически запустится.

Я предполагаю, что есть более изящные способы, но это, кажется, работает.

Редактирование, чтобы добавить, что если вы хотите запустить предупреждение после завершения команды, вы можете создать эти псевдонимы в .bashrc, а затем запустить, alertпока она выполняется:

 alias alert_helper='history|tail -n1|sed -e "s/^\s*[0-9]\+\s*//" -e "s/;\s*alert$//"'
 alias alert='notify-send -i gnome-terminal "Finished Terminal Job" "[$?] $(alert_helper)"'
mlissner
источник
7
Если запущенный скрипт не ожидает ввода в одной точке.
Даниэль Бек
@ Даниэль - да ... это проблема. Shoot.
mlissner
1

Некоторое время назад я написал сценарий для ожидания завершения другого процесса. NOISE_CMDможет быть что-то вроде notify-send ..., учитывая, что DISPLAYустановлен правильно.

#!/bin/bash

NOISE_CMD="/usr/bin/mplayer -really-quiet $HOME/sfx/alarm-clock.mp3"

function usage() {
    echo "Usage: $(basename "$0") [ PROCESS_NAME [ PGREP_ARGS... ] ]"
    echo "Helpful arguments to pgrep are: -f -n -o -x"
}

if [ "$#" -gt 0 ] ; then
    PATTERN="$1"
    shift
    PIDS="$(pgrep "$PATTERN" "$@")"

    if [ -z "$PIDS" ] ; then
        echo "No process matching pattern \"$PATTERN\" found."
        exit
    fi

    echo "Waiting for:"
    pgrep -l "$PATTERN" "$@"

    for PID in $PIDS ; do
        while [ -d "/proc/$PID" ] ; do
            sleep 1
        done
    done
fi

exec $NOISE_CMD

Без какого-либо шума, просто немедленно пошуметь. Это поведение позволяет что-то подобное для удобства (скажем, вы вызываете скрипт ниже alarm.sh):

apt-get upgrade ; ~/bin/alarm.sh

Конечно, вы можете сделать много забавных вещей с помощью такого скрипта, например, позволить экземпляру alarm.shждать экземпляра alarm.sh, который ожидает какую-то другую команду. Или выполнение команды сразу после того, как какое-то задание другого вошедшего в систему пользователя завершилось ...>: D

Предыдущая версия приведенного выше сценария может быть интересной, если вы хотите избежать зависимости pgrepи разрешить поиск идентификаторов процесса самостоятельно:

#!/bin/bash

if [ "$#" -lt 2 -o ! -d "/proc/$1" ] ; then
  echo "Usage: $(basename "$0") PID COMMAND [ ARGUMENTS... ]"
  exit
fi

while [ -d "/proc/$1" ] ; do
  sleep 1
done

shift
exec "$@"

Немного не по теме, но полезно в подобных ситуациях: может заинтересовать reptyrинструмент, который «крадет» процесс из некоторой (родительской) оболочки и запускает его из текущей оболочки. Я пробовал аналогичные реализации и reptyrявляется самым красивым и надежным для моих целей.

Piscoris
источник