Jak uruchomić polecenie po zakończeniu już działającego, istniejącego?

32

Jeśli uruchomię skrypt, który zajmie dużo czasu, nieuchronnie zdaję sobie z tego sprawę po uruchomieniu skryptu i żałuję, że nie mam możliwości zrobienia jakiegoś ostrzeżenia po jego zakończeniu.

Na przykład, jeśli uruchomię:

really_long_script.sh

i naciśnij enter ... jak mogę uruchomić inne polecenie, gdy zakończy się?

mlissner
źródło

Odpowiedzi:

45

Możesz rozdzielić wiele poleceń ;, dzięki czemu są one wykonywane sekwencyjnie, na przykład:

really_long_script.sh ; echo Finished

Jeśli chcesz uruchomić następny program tylko wtedy, gdy skrypt zakończy się kodem powrotu 0 (co zwykle oznacza, że ​​wykonał się poprawnie), to:

really_long_script.sh && echo OK

Jeśli chcesz mieć coś przeciwnego (tzn. Kontynuuj tylko wtedy, gdy bieżące polecenie nie powiodło się), niż:

really_long_script.sh || echo FAILED

Możesz uruchomić skrypt w tle (ale uważaj, dane wyjściowe skryptów ( stdouti stderr) będą nadal przechodzić do terminala, chyba że gdzieś go przekierujesz), a następnie wait:

really_long_script.sh &
dosomethingelse
wait; echo Finished

Jeśli skrypt został już uruchomiony, możesz go zawiesić Ctrl-Z, a następnie wykonać coś takiego:

fg ; echo Finished

Gdzie fgprzenosi zawieszony proces na pierwszy plan ( bgsprawiłby, że działałby w tle, prawie jak na początku &)

Ziemia
źródło
@mlissner Rozszerzyłem moją odpowiedź na tę sprawę
i
8
Ponieważ fgzwraca wartość wznowionego zadania, możesz użyć tej opcji fg && echo Finished, aby upewnić się, że wykonanie polecenia zakończy się powodzeniem przed wykonaniem drugiego polecenia.
palswim
13

Możesz także użyć kontroli zadań bash. Jeśli zacząłeś

$ really_long_script.sh

następnie naciśnij ctrl + z, aby go zawiesić:

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

aby zrestartować zadanie w tle (tak jakby je rozpoczęło really_long_script.sh &). Następnie możesz poczekać na tę pracę w tle za pomocą

$ wait N && echo "Successfully completed"

gdzie N jest identyfikatorem zadania (prawdopodobnie 1, jeśli nie uruchomiłeś żadnych innych zadań w tle), które jest również wyświetlane jak [1]powyżej.

Jaap Eldering
źródło
11

Jeśli proces nie działa na bieżącym tty, spróbuj tego:

watch -g ps -opid -p <pid>; mycommand
Marinos An
źródło
2
Uwielbiam kreatywne wykorzystanie watch. Super krótka odpowiedź i jedna z nowych przydatnych metod korzystania z zegarka
Piotr Czapla,
2

Okazuje się, że nie jest to takie trudne: możesz po prostu wpisać kolejne polecenie w oknie podczas działania istniejącego, nacisnąć klawisz Enter, a gdy pierwsze zakończy działanie, drugie polecenie uruchomi się automatycznie.

Sądzę, że są bardziej eleganckie sposoby, ale wydaje się, że to działa.

Edytowanie w celu dodania, że ​​jeśli chcesz uruchomić alert po zakończeniu polecenia, możesz utworzyć te aliasy w .bashrc, a następnie uruchomić alertpodczas działania:

 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
źródło
7
O ile uruchomiony skrypt nie oczekuje danych wejściowych w jednym punkcie.
Daniel Beck
@Daniel - tak ... to problem. Strzelać.
mlissner,
1

Jakiś czas temu napisałem skrypt czekający na koniec innego procesu. NOISE_CMDmoże być coś takiego notify-send ..., biorąc pod uwagę, że DISPLAYjest ustawiony poprawnie.

#!/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

Bez żadnych kłótni natychmiast zrób hałas. To zachowanie pozwala na coś takiego dla wygody (powiedz, że wywołujesz skrypt poniżej alarm.sh):

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

Oczywiście przy pomocy takiego skryptu możesz robić wiele zabawnych rzeczy, np. Pozwalać instancji alarm.shczekać na instancję alarm.sh, która czeka na jakieś inne polecenie. Lub wykonanie polecenia tuż po zakończeniu zadania innego zalogowanego użytkownika ...>: D

Poprzednia wersja powyższego skryptu może być interesująca, jeśli chcesz uniknąć zależności pgrepi zaakceptować samodzielne wyszukiwanie identyfikatorów procesów:

#!/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 "$@"

Nie na temat, ale przydatne w podobnych sytuacjach: Ktoś może zainteresować reptyrsię narzędziem, które „kradnie” proces z jakiejś (macierzystej) powłoki i uruchamia ją z bieżącej powłoki. Wypróbowałem podobne implementacje i reptyrjest najładniejszy i najbardziej niezawodny dla moich celów.

Piscoris
źródło