Làm cách nào để đặt một quy trình đã chạy trong nohup?


940

Tôi có một quy trình đã chạy trong một thời gian dài và không muốn kết thúc nó.

Làm cách nào để đặt nó dưới nohup (nghĩa là, làm cách nào để khiến nó tiếp tục chạy ngay cả khi tôi đóng thiết bị đầu cuối?)


29
Đối với bất kỳ ai gặp phải cùng một vấn đề: Hãy nhớ rằng, ngay cả khi bạn nhập yourExecutable &và các đầu ra tiếp tục xuất hiện trên màn hình và Ctrl+Cdường như không dừng lại bất cứ điều gì, chỉ cần gõ disown;và nhấn một cách mù quáng Enterngay cả khi màn hình đang cuộn với đầu ra và bạn không thể thấy gì bạn đang gõ Quá trình sẽ bị từ chối và bạn sẽ có thể đóng thiết bị đầu cuối mà không bị quá trình chết.
Nav

Câu trả lời:


1367

Sử dụng Kiểm soát công việc của bash để gửi quy trình vào nền:

  1. Ctrl+ Zđể dừng (tạm dừng) chương trình và quay lại trình bao.
  2. bg để chạy nó trong nền.
  3. disown -h [job-spec]trong đó [job-spec] là số công việc (như %1đối với công việc đang chạy đầu tiên; tìm về số của bạn bằng jobslệnh) để công việc không bị giết khi đóng thiết bị đầu cuối.

38
Vì câu hỏi là làm thế nào để "đặt nó dưới nohup", disown -hcó lẽ đây là câu trả lời chính xác hơn: "làm cho sự thất vọng hành xử giống như nohup (tức là các công việc sẽ ở trong cây quy trình hiện tại của bạn cho đến khi bạn thoát khỏi vỏ của mình) Điều này cho phép bạn nhìn thấy tất cả các công việc mà cái vỏ này bắt đầu. " (từ [ quantprincipl.com/invest/index.php/docs/tipsandtricks/unix/ mẹo )
Tiến sĩ Jan-Philip Gehrcke

8
Làm thế nào để tôi phục hồi công việc sau này? Tôi có thể thấy nó chạy bằng ps -e.
Paulo Casaretto

26
Bạn không thể thấy đầu ra của một công việc sau khi disown, từ chối tạo một quy trình, có nghĩa là đầu vào / đầu ra tiêu chuẩn được chuyển hướng đến / dev / null. Vì vậy, nếu bạn có kế hoạch để không thừa nhận một công việc, nó tốt hơn để bắt đầu nó bằng cách đăng nhập vào một tập tin, ví dụmy_job_command | tee my_job.log
rustyx

8
bằng cách nào đó có thể làm một cái gì đó như 'my_job_command | tee my_job.log ' sau khi lệnh đã chạy?
arod

23
disowntách bất kỳ đường ống từ quá trình. Để gắn lại ống, sử dụng gdbnhư mô tả trong chủ đề này . Cụ thể hơn, bài này .
mbrownnyc

185

Giả sử vì một số lý do Ctrl+ Zcũng không hoạt động, hãy đi đến một thiết bị đầu cuối khác, tìm id quá trình (sử dụng ps) và chạy:

kill -SIGSTOP PID 
kill -SIGCONT PID

SIGSTOPsẽ đình chỉ quá trình và SIGCONTsẽ tiếp tục quá trình, trong nền. Vì vậy, bây giờ, đóng cả hai thiết bị đầu cuối của bạn sẽ không dừng quá trình của bạn.


10
Vâng, đó là một vấn đề hệ điều hành, kill không hoạt động với Cygwin trên Windows.
Pungs

6
Cũng rất hữu ích nếu công việc được bắt đầu từ một phiên ssh khác.
Amir Ali Akbari

5
Chỉ cần nhớ làm disown %1trong thiết bị đầu cuối trước khi đóng nó.
fred

1
Điều này rất hữu ích vì tôi đã bắt đầu giao diện đồ họa với bảng điều khiển (trong trường hợp của tôi, tôi đã bắt đầu từ konsole kwinsau khi gặp sự cố mà không nghĩ đến hậu quả). Vì vậy, nếu tôi dừng kwin, mọi thứ sẽ đóng băng và tôi không có khả năng chạy bg!
Michele

@fred Tôi đã không làm điều này và nó dường như tiếp tục chạy. Có thể hoặc tôi đã nhấn sai PID?
Không ai

91

Lệnh để tách một công việc đang chạy khỏi shell (= làm cho nó không hoạt động) là disown và một lệnh shell cơ bản.

Từ bash-manpage (man bash):

từ chối [-ar] [-h] [jobspec ...]

Không có tùy chọn, mỗi jobspec sẽ bị xóa khỏi bảng công việc đang hoạt động. Nếu tùy chọn -h được đưa ra, mỗi jobspec sẽ không bị xóa khỏi bảng, nhưng được đánh dấu để SIGHUP không được gửi đến công việc nếu shell nhận được SIGHUP. Nếu không có jobspec nào xuất hiện và cả tùy chọn -a hay -r đều được cung cấp, công việc hiện tại sẽ được sử dụng. Nếu không có jobspec nào được cung cấp, tùy chọn -a có nghĩa là loại bỏ hoặc đánh dấu tất cả các công việc; tùy chọn -r không có đối số jobspec sẽ hạn chế hoạt động đối với các công việc đang chạy. Giá trị trả về là 0 trừ khi một jobspec không chỉ định một công việc hợp lệ.

Điều đó có nghĩa là, đơn giản

disown -a

sẽ loại bỏ tất cả các công việc khỏi bảng công việc và làm cho chúng không hoạt động


9
disown -aloại bỏ tất cả các công việc. Một đơn giản disownchỉ loại bỏ công việc hiện tại. Như trang người đàn ông trong câu trả lời nói.
Rune Schjellerup Philosof

73

Đây là những câu trả lời tốt ở trên, tôi chỉ muốn thêm một sự làm rõ:

Bạn không thể disownlà một mánh khóe hay quá trình, disownmột công việc, và đó là một sự khác biệt quan trọng.

Một công việc là một cái gì đó là một khái niệm của một quá trình được gắn vào một vỏ, do đó bạn phải ném công việc vào nền (không đình chỉ nó) và sau đó từ chối nó.

Vấn đề:

%  jobs
[1]  running java 
[2]  suspended vi
%  disown %1

Xem http: //www.quantprincipl.com/invest/index.php/docs/tipsandtricks/unix/jobcontrol/ để thảo luận chi tiết hơn về Unix Job Control.


48

Thật không may disownlà cụ thể cho bash và không có sẵn trong tất cả các shell.

Một số hương vị nhất định của Unix (ví dụ AIX và Solaris) có một tùy chọn trên nohupchính lệnh có thể được áp dụng cho một quy trình đang chạy:

nohup -p pid

Xem http://en.wikipedia.org/wiki/Nohup


Chỉ cho AIXSolaris. "Các phiên bản AIX và Solaris của nohup có tùy chọn -p sửa đổi quy trình đang chạy để bỏ qua các tín hiệu SIGHUP trong tương lai. Không giống như bản dựng sẵn được mô tả ở trên của bash, nohup -p chấp nhận ID tiến trình.". Nguồn
AlikElzin-kilaka

27

Câu trả lời của Node thực sự rất hay, nhưng nó bỏ ngỏ câu hỏi làm thế nào để có thể chuyển hướng xuất chuẩn và thiết bị xuất chuẩn. Tôi đã tìm thấy một giải pháp trên Unix & Linux , nhưng nó cũng chưa hoàn thành. Tôi muốn hợp nhất hai giải pháp này. Đây là:

Đối với thử nghiệm của mình, tôi đã tạo ra một tập lệnh bash nhỏ có tên loop.sh, nó in bản thân của nó với một phút ngủ trong một vòng lặp vô hạn.

$./loop.sh

Bây giờ có được PID của quá trình này bằng cách nào đó. Thường ps -C loop.shlà đủ tốt, nhưng nó được in trong trường hợp của tôi.

Bây giờ chúng ta có thể chuyển sang một thiết bị đầu cuối khác (hoặc nhấn ^ Z và trong cùng một thiết bị đầu cuối). Bây giờ gdbnên được gắn liền với quá trình này.

$ gdb -p <PID>

Điều này dừng tập lệnh (nếu đang chạy). Trạng thái của nó có thể được kiểm tra bởips -f <PID> , trong đó STATtrường là 'T +' (hoặc trong trường hợp ^ Z 'T'), có nghĩa là (man ps (1))

    T Stopped, either by a job control signal or because it is being traced
    + is in the foreground process group

(gdb) call close(1)
$1 = 0

Đóng (1) trả về số 0 khi thành công.

(gdb) call open("loop.out", 01102, 0600)
$6 = 1

Mở (1) trả về bộ mô tả tệp mới nếu thành công.

Điều này mở bằng với open(path, O_TRUNC|O_CREAT|O_RDWR, S_IRUSR|S_IWUSR). Thay vì O_RDWR O_WRONLYcó thể được áp dụng, nhưng/usr/sbin/lsof nói 'u' cho tất cả các trình xử lý tệp std * ( FDcột) O_RDWR.

Tôi đã kiểm tra các giá trị trong tệp tiêu đề /usr/include/bits/fcntl.h.

Các tập tin đầu ra có thể được mở bằng O_APPEND, nhưnohup sẽ làm, nhưng điều này không được đề xuất bởi man open(2), vì các vấn đề NFS có thể xảy ra.

Nếu chúng ta lấy -1 làm giá trị trả về, thì sẽ call perror("")in thông báo lỗi. Nếu chúng ta cần errno, sử dụngp errno gdb comand.

Bây giờ chúng ta có thể kiểm tra tệp mới được chuyển hướng. /usr/sbin/lsof -p <PID>in:

loop.sh <PID> truey    1u   REG   0,26        0 15008411 /home/truey/loop.out

Nếu chúng ta muốn, chúng ta có thể chuyển hướng stderr sang một tệp khác, nếu chúng ta muốn sử dụng call close(2)call open(...)sử dụng lại tên tệp khác.

Bây giờ phần đính kèm bashphải được phát hành và chúng tôi có thể thoát gdb:

(gdb) detach
Detaching from program: /bin/bash, process <PID>
(gdb) q

Nếu đoạn script bị dừng bởi gdbmột thiết bị đầu cuối khác, nó sẽ tiếp tục chạy. Chúng ta có thể chuyển trở lại terminal của loop.sh. Bây giờ nó không ghi bất cứ điều gì lên màn hình, nhưng chạy và ghi vào tệp. Chúng ta phải đặt nó vào nền. Vì vậy, nhấn ^Z.

^Z
[1]+  Stopped                 ./loop.sh

(Bây giờ chúng tôi ở trong trạng thái như thể ^Zđược nhấn vào lúc đầu.)

Bây giờ chúng ta có thể kiểm tra trạng thái của công việc:

$ ps -f 24522
UID        PID  PPID  C STIME TTY      STAT   TIME CMD
<UID>    <PID><PPID>  0 11:16 pts/36   S      0:00 /bin/bash ./loop.sh
$ jobs
[1]+  Stopped                 ./loop.sh

Vì vậy, quá trình nên được chạy trong nền và tách ra khỏi thiết bị đầu cuối. Số trong jobsđầu ra của lệnh trong ngoặc vuông xác định công việc bên trong bash. Chúng ta có thể sử dụng các bashlệnh được tích hợp sẵn sau đây áp dụng dấu '%' trước số công việc:

$ bg %1
[1]+ ./loop.sh &
$ disown -h %1
$ ps -f <PID>
UID        PID  PPID  C STIME TTY      STAT   TIME CMD
<UID>    <PID><PPID>  0 11:16 pts/36   S      0:00 /bin/bash ./loop.sh

Và bây giờ chúng ta có thể thoát khỏi bash gọi. Quá trình tiếp tục chạy trong nền. Nếu chúng ta thoát PPID của nó trở thành quá trình 1 (init (1)) và thiết bị đầu cuối điều khiển trở nên không xác định.

$ ps -f <PID>
UID        PID  PPID  C STIME TTY      STAT   TIME CMD
<UID>    <PID>     1  0 11:16 ?        S      0:00 /bin/bash ./loop.sh
$ /usr/bin/lsof -p <PID>
...
loop.sh <PID> truey    0u   CHR 136,36                38 /dev/pts/36 (deleted)
loop.sh <PID> truey    1u   REG   0,26     1127 15008411 /home/truey/loop.out
loop.sh <PID> truey    2u   CHR 136,36                38 /dev/pts/36 (deleted)

BÌNH LUẬN

Các công cụ gdb có thể được tự động hóa tạo một tệp (ví dụ loop.gdb) có chứa các lệnh và chạy gdb -q -x loop.gdb -p <PID>. Loop.gdb của tôi trông như thế này:

call close(1)
call open("loop.out", 01102, 0600)
# call close(2)
# call open("loop.err", 01102, 0600)
detach
quit

Hoặc người ta có thể sử dụng một lớp lót sau đây để thay thế:

gdb -q -ex 'call close(1)' -ex 'call open("loop.out", 01102, 0600)' -ex detach -ex quit -p <PID>

Tôi hy vọng đây là một mô tả khá đầy đủ về giải pháp.


Thực sự rất nhiều thông tin, và có khả năng làm việc tốt trong các trường hợp đơn giản. Nhưng được cảnh báo, các trường hợp phức tạp hơn có thể thất bại thảm hại. Tôi đã có một trong những điều đó ngày hôm nay: Quá trình của tôi đã tạo ra một quy trình khác thực hiện đầu ra (có lẽ là stderr), nhưng đã xuất hiện kết nối để liên lạc với chủ nhân của nó. Chuyển hướng các FD chủ là vô hiệu vì đứa trẻ được thừa hưởng stderr, và đóng cửa stdout của đứa trẻ đã làm thất bại chủ đang chờ ở đầu kia của ống. X- | Bettern biết rõ quy trình của bạn trước khi bạn thử điều này.
cmaster - phục hồi monica

@cmaster Bạn có thể kiểm tra xem một tay cầm có được chuyển hướng hay không bằng cách sử dụng lsof(tên của tay cầm tệp pipe, không giống như /dev/pts/1) hoặc bởi ls -l /proc/<PID>/fd/<fd>(điều này hiển thị liên kết tượng trưng của tay cầm). Ngoài ra các quy trình con vẫn có thể không được chuyển hướng đầu ra, mà nên được chuyển hướng đến một tệp.
TrueY

7

Để gửi quy trình chạy tới nohup ( http://en.wikipedia.org/wiki/Nohup )

nohup -p pid , nó không làm việc cho tôi

Sau đó, tôi đã thử các lệnh sau và nó hoạt động rất tốt

  1. Chạy một số SOMECOMMAND, nói /usr/bin/python /vol/scripts/python_scripts/retention_all_properties.py 1.

  2. Ctrl+ Zđể dừng (tạm dừng) chương trình và quay lại trình bao.

  3. bg để chạy nó trong nền.

  4. disown -h để quá trình không bị chết khi thiết bị đầu cuối đóng cửa.

  5. exitđể thoát ra khỏi vỏ vì bây giờ bạn rất tốt vì thao tác sẽ chạy trong nền trong quy trình riêng của nó, vì vậy nó không bị ràng buộc với vỏ.

Quá trình này là tương đương với chạy nohup SOMECOMMAND.


3

Trên hệ thống AIX của tôi, tôi đã thử

nohup -p  processid>

Điều này làm việc tốt. Nó tiếp tục chạy quy trình của tôi ngay cả sau khi đóng các cửa sổ đầu cuối. Chúng ta có ksh là shell mặc định nên các lệnh bgdisownlệnh không hoạt động.


2
  1. ctrl+ z - điều này sẽ tạm dừng công việc (sẽ không hủy!)
  2. bg - điều này sẽ đặt công việc trong nền và trở lại trong quá trình chạy
  3. disown -a - điều này sẽ cắt tất cả các tệp đính kèm với công việc (vì vậy bạn có thể đóng thiết bị đầu cuối và nó vẫn sẽ chạy)

Các bước đơn giản này sẽ cho phép bạn đóng thiết bị đầu cuối trong khi vẫn duy trì quá trình chạy.

Nó sẽ không được đưa vào nohup(dựa trên sự hiểu biết của tôi về câu hỏi của bạn, bạn không cần nó ở đây).


Tôi nghĩ rằng hành vi cho sự thất vọng -a là cắt giảm sự gắn bó với tất cả các công việc. Tuy nhiên, nó không tắt các đường ống stdin / stdout, điều đó có nghĩa là quá trình này vẫn sẽ cố gắng viết (/ đọc) từ thiết bị đầu cuối
Kelthar

-2

Điều này làm việc cho tôi trên Ubuntu linux trong khi ở tcshell.

  1. CtrlZ tạm dừng nó

  2. bg chạy trong nền

  3. jobs để có được số công việc của nó

  4. nohup %n Trong đó n là số công việc


2
Không, nó không hoạt động:nohup: failed to run command '%1': No such file or directory
Dunatotatos
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.