Làm thế nào để chạy một lệnh sau khi một lệnh đã chạy, hiện có kết thúc?


32

Nếu tôi bắt đầu một kịch bản sẽ mất nhiều thời gian, tôi chắc chắn sẽ nhận ra nó sau khi tôi bắt đầu kịch bản, và ước gì tôi có cách thực hiện một loại cảnh báo nào đó khi nó kết thúc.

Vì vậy, ví dụ, nếu tôi chạy:

really_long_script.sh

và nhấn enter ... làm thế nào tôi có thể chạy một lệnh khác khi nó kết thúc?

Câu trả lời:


45

Bạn có thể phân tách nhiều lệnh bằng cách ;, vì vậy chúng được thực hiện tuần tự, ví dụ:

really_long_script.sh ; echo Finished

Nếu bạn chỉ muốn thực hiện chương trình tiếp theo nếu tập lệnh kết thúc với mã trả về 0 (điều này thường có nghĩa là nó đã được thực thi chính xác), thì:

really_long_script.sh && echo OK

Nếu bạn muốn ngược lại (tức là chỉ tiếp tục nếu lệnh hiện tại không thành công), hơn:

really_long_script.sh || echo FAILED

Bạn có thể chạy tập lệnh của mình ở chế độ nền (nhưng hãy cẩn thận, đầu ra tập lệnh ( stdoutstderr) sẽ tiếp tục đi đến thiết bị đầu cuối của bạn trừ khi bạn chuyển hướng tập lệnh ở đâu đó), và sau đó waitcho nó:

really_long_script.sh &
dosomethingelse
wait; echo Finished

Nếu bạn đã chạy tập lệnh, bạn có thể tạm dừng tập lệnh Ctrl-Zđó và sau đó thực thi một cái gì đó như:

fg ; echo Finished

Trường hợp fgđưa quy trình bị treo lên nền trước ( bgsẽ làm cho nó chạy ở chế độ nền, gần giống như bắt đầu với &)


@mlissner Mở rộng câu trả lời của tôi để bao quát trường hợp này
aland

8
fgtrả về giá trị của công việc mà nó tiếp tục, bạn có thể sử dụng fg && echo Finishednếu bạn muốn đảm bảo lệnh thành công trước khi thực hiện lệnh khác.
palswim

13

Bạn cũng có thể sử dụng kiểm soát công việc của bash. Nếu bạn bắt đầu

$ really_long_script.sh

sau đó nhấn ctrl + z để tạm dừng nó:

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

để khởi động lại công việc trong nền (giống như khi bắt đầu với really_long_script.sh &). Sau đó, bạn có thể chờ cho công việc nền này với

$ wait N && echo "Successfully completed"

Trong đó N là ID công việc (có thể là 1 nếu bạn không chạy bất kỳ công việc nền nào khác) cũng được hiển thị như [1]trên.


11

Nếu quá trình không chạy trên tty hiện tại, thì hãy thử điều này:

watch -g ps -opid -p <pid>; mycommand

2
Tôi yêu việc sử dụng sáng tạo của watch. Câu trả lời siêu ngắn và người ta học được một cách sử dụng đồng hồ mới hữu ích
Piotr Czapla

2

Hóa ra điều này không khó lắm: Bạn chỉ cần gõ lệnh tiếp theo vào cửa sổ trong khi lệnh hiện có chạy, nhấn enter và khi lệnh đầu tiên kết thúc, lệnh thứ hai sẽ tự động chạy.

Tôi đoán có nhiều cách thanh lịch hơn, nhưng điều này dường như có hiệu quả.

Chỉnh sửa để thêm rằng nếu bạn muốn chạy một cảnh báo sau khi lệnh kết thúc, bạn có thể tạo các bí danh này trong .bashrc, sau đó chạy alerttrong khi nó đang chạy:

 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)"'

7
Trừ khi tập lệnh đang chạy mong đợi đầu vào tại một điểm.
Daniel Beck

@Daniel - yeah ... đó là một vấn đề. Bắn.
mlissner

1

Cách đây một thời gian tôi đã viết một kịch bản để chờ kết thúc một quá trình khác. NOISE_CMDcó thể là một cái gì đó như notify-send ..., cho rằng DISPLAYđược đặt chính xác.

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

Không có bất kỳ tranh luận, chỉ cần làm cho một số tiếng ồn ngay lập tức. Hành vi này cho phép một cái gì đó như thế này cho thuận tiện (giả sử bạn gọi tập lệnh bên dưới alarm.sh):

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

Tất nhiên bạn có thể làm nhiều điều thú vị với một kịch bản như vậy, như để một ví dụ alarm.shchờ đợi một ví dụ alarm.sh, đó là chờ một số lệnh khác. Hoặc thực thi lệnh ngay sau khi một số tác vụ của người dùng đã đăng nhập khác kết thúc ...>: D

Một phiên bản cũ của tập lệnh ở trên có thể rất thú vị, nếu bạn muốn tránh sự phụ thuộc pgrepvà chấp nhận tự tìm kiếm ID quá trình:

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

Hơi lạc đề, nhưng hữu ích trong các tình huống tương tự: Người ta có thể quan tâm reptyr, một công cụ "đánh cắp" một quy trình từ một số vỏ (cha mẹ) và chạy nó từ vỏ hiện tại. Tôi đã thử thực hiện tương tự và reptyrlà đẹp nhất và đáng tin cậy nhất cho mục đích của tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.