Làm thế nào để không làm gì mãi mãi một cách thanh lịch?


82

Tôi có một chương trình tạo ra thông tin hữu ích stdoutnhưng cũng đọc từ đó stdin. Tôi muốn chuyển hướng đầu ra tiêu chuẩn của nó sang một tệp mà không cung cấp bất cứ điều gì về đầu vào tiêu chuẩn. Cho đến nay, rất tốt: tôi có thể làm:

program > output

và đừng làm bất cứ điều gì trong tty.

Tuy nhiên, vấn đề là tôi muốn làm điều này trong nền. Nếu tôi làm:

program > output &

chương trình sẽ bị treo ("bị treo (đầu vào tty)").

Nếu tôi làm:

program < /dev/null > output &

chương trình chấm dứt ngay lập tức vì nó đạt EOF.

Dường như những gì tôi cần là đưa vào programthứ gì đó không làm gì trong một khoảng thời gian không xác định và không đọc stdin. Các phương pháp sau đây hoạt động:

while true; do sleep 100; done | program > output &
mkfifo fifo && cat fifo | program > output &
tail -f /dev/null | program > output &

Tuy nhiên, đây là tất cả rất xấu xí. Phải một cách thanh lịch, sử dụng các tiện ích Unix tiêu chuẩn, để "không làm gì, vô thời hạn" (để diễn giải man true). Làm thế nào tôi có thể đạt được điều này? (Tiêu chí chính của tôi về sự thanh lịch ở đây: không có tệp tạm thời; không có sự thức dậy bận rộn hoặc chờ đợi định kỳ; không có tiện ích kỳ lạ; càng ngắn càng tốt.)


Hãy thử su -c 'program | output &' user. Tôi sắp hỏi một câu hỏi tương tự với việc tạo các công việc nền như một phương pháp có thể chấp nhận để xử lý "dịch vụ / trình nền". Tôi cũng nhận thấy rằng tôi không thể chuyển hướng STDERRmà không chuyển hướng STDOUT. Giải pháp mà chương trìnhA gửi STDOUTđến STDINchương trìnhB, sau đó chuyển hướng STDERRđến tệp nhật ký:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc

có lẽ ... su -c 'while true; do true; done | cat > ~/output &' user?
mbrownnyc

Đó là loại chương trình gì?
João Portela

João Portela: Đây là chương trình tôi đã viết, gitorious.org/irctk
a3nm

Tại sao không chỉ đơn giản là thêm một chuyển đổi cho chương trình mà bạn đã viết? Ngoài ra, tôi giả sử rằng nếu bạn đóng stdin với 1<&-nó sẽ thoát khỏi chương trình của bạn?
w00t

Câu trả lời:


16

Trong các shell hỗ trợ chúng (ksh, zsh, bash4), bạn có thể bắt đầu programnhư một quá trình hợp tác .

  • ksh: program > output |&
  • zsh, bash:coproc program > output

Điều đó bắt đầu programtrong nền với đầu vào được chuyển hướng từ a pipe. Đầu kia của ống được mở vào vỏ.

Ba lợi ích của phương pháp đó

  • không có quá trình bổ sung
  • bạn có thể thoát khỏi tập lệnh khi programchết (sử dụng waitđể chờ nó)
  • programsẽ chấm dứt (nhận được eofstdin của nó nếu vỏ thoát).

Điều đó dường như làm việc và trông giống như một ý tưởng tuyệt vời! .
a3nm

1
@ a3nm, tail -f /dev/nullkhông lý tưởng vì nó đọc mỗi giây một lần /dev/null(các phiên bản hiện tại của đuôi GNU trên Linux sử dụng inotify thực sự có lỗi ). sleep infhoặc tương đương di động hơn của nó sleep 2147483647là các cách tiếp cận tốt hơn cho một lệnh nằm ở đó không làm gì IMO (lưu ý rằng sleepđược xây dựng trong một vài shell như ksh93hoặc mksh).
Stéphane Chazelas

78

Tôi không nghĩ rằng bạn sẽ trở nên thanh lịch hơn

tail -f /dev/null

mà bạn đã đề xuất (giả sử điều này sử dụng inotify trong nội bộ, không nên bỏ phiếu hoặc đánh thức, vì vậy ngoài việc tìm kiếm kỳ quặc, nó là đủ).

Bạn cần một tiện ích sẽ chạy vô thời hạn, sẽ giữ cho thiết bị xuất chuẩn của nó mở, nhưng thực tế sẽ không viết bất cứ điều gì cho thiết bị xuất chuẩn và sẽ không thoát khi đóng stdin của nó. Một cái gì đó giống như yesthực sự viết vào thiết bị xuất chuẩn. catsẽ thoát khi đóng stdin của nó (hoặc bất cứ điều gì bạn chuyển hướng vào nó được thực hiện). Tôi nghĩ rằng sleep 1000000000dcó thể làm việc, nhưng tailrõ ràng là tốt hơn. Hộp Debian của tôi có một tailflệnh rút ngắn một chút.

Lấy một chiến thuật khác, làm thế nào về việc chạy chương trình theo screen?


Tôi thích tail -f /dev/nullcách tiếp cận tốt nhất và cũng thấy nó đủ thanh lịch, vì việc sử dụng lệnh phù hợp với mục đích dự định khá chặt chẽ.
jw013

2
Từ strace tail -f /dev/nullđó dường như việc tailsử dụng inotifyvà đánh thức xảy ra trong những trường hợp ngớ ngẩn như thế sudo touch /dev/null. Thật đáng buồn khi dường như không có giải pháp nào tốt hơn ... Tôi tự hỏi đó sẽ là tòa nhà phù hợp để sử dụng để thực hiện một giải pháp tốt hơn.
a3nm

5
@ a3nm Tòa nhà chọc trời sẽ có pause, nhưng nó không được tiếp xúc trực tiếp với giao diện shell.
Gilles

PT: Tôi biết screen, nhưng điều này là để chạy nhiều lần xuất hiện của chương trình từ tập lệnh shell cho mục đích thử nghiệm, vì vậy việc sử dụng screenlà hơi quá mức.
a3nm

3
@sillyMunky Khỉ ngớ ngẩn, câu trả lời của WaelJ là sai (gửi số không vô hạn đến stdin).
PT

48

sleep infinity là giải pháp rõ ràng nhất mà tôi biết.

Bạn có thể sử dụng infinitysleepchấp nhận số dấu phẩy động * , có thể là số thập phân , thập lục phân , vô cực hoặc NaN , theo man strtod.

* Đây không phải là một phần của tiêu chuẩn POSIX, vì vậy không thể mang theo như tail -f /dev/null. Tuy nhiên, nó được hỗ trợ trong GNU coreutils (Linux) và BSD (được sử dụng trên Mac) (dường như không được hỗ trợ trên các phiên bản mới hơn của Mac - xem bình luận).


Haha, đó thực sự là một cách tiếp cận tốt :)
a3nm

@ a3nm: Cảm ơn :) Có vẻ như sleep infinitycũng hoạt động trên BSD và Mac .
Zaz

Một loại tài nguyên mà một quá trình ngủ vô hạn mất? Chỉ cần RAM?
CMCDragonkai

1
Câu trả lời này tuyên bố rằng sleep infinitychờ tối đa 24 ngày; ai đúng không
nh2

1
@Zaz Tôi đã điều tra vấn đề một cách chi tiết. Hóa ra ban đầu bạn đã đúng! Các sleeptiện ích được không giới hạn đến 24 ngày ; nó chỉ là tòa nhà cao tầng đầu tiên ngủ trong 24 ngày và sau đó nó sẽ làm nhiều tòa nhà như vậy. Xem bình luận của tôi ở đây: stackoverflow.com/questions/2935183/ trên
nh2

19
sleep 2147483647 | program > output &

Vâng, 2^31-1là một con số hữu hạn và nó sẽ không chạy mãi mãi , nhưng tôi sẽ cho bạn 1000 đô la khi giấc ngủ cuối cùng cũng kết thúc. (Gợi ý: một trong số chúng ta sẽ chết vào lúc đó.)

  • không có hồ sơ tạm thời; kiểm tra.
  • không phải chờ đợi bận rộn hoặc thức dậy định kỳ; kiểm tra
  • không có tiện ích kỳ lạ; kiểm tra.
  • Càng ngắn càng tốt. Được rồi, nó có thể ngắn hơn.

5
bash: ngủ $ ((64 # 1 _____)) | chương trình> đầu ra &
Diego Torres Milano

Ngủ trong 68 năm , giấc ngủ này trong 98 thế kỷ : sleep 2147483647d...
agc

9

Bạn có thể tạo một nhị phân thực hiện điều đó với:

$ echo 'int main(){ pause(); }' > pause.c; make pause

5

Đây là một đề xuất khác sử dụng các tiện ích Unix tiêu chuẩn, để "không làm gì, vô thời hạn" .

sh -c 'kill -STOP $$' | program > output

Điều này kích hoạt một lớp vỏ được gửi ngay lập tức SIGSTOP, làm đình chỉ quá trình. Điều này được sử dụng như "đầu vào" cho chương trình của bạn. Phần bù của SIGSTOPSIGCONT, tức là nếu bạn biết shell có PID 12345, bạn có thể kill -CONT 12345làm cho nó tiếp tục.


2

Trên Linux, bạn có thể làm:

read x < /dev/fd/1 | program > output

Trên Linux, mở / dev / fd / x trong đó x là một mô tả tệp cho đầu viết của một ống, giúp bạn đọc kết thúc đọc của ống, vì vậy ở đây giống như trên stdin của chương trình. Về cơ bản, readsẽ không bao giờ quay trở lại, bởi vì điều duy nhất có thể ghi vào đường ống đó là chính nó, và readkhông tạo ra bất cứ điều gì.

Nó cũng sẽ hoạt động trên FreeBSD hoặc Solaris, nhưng vì một lý do khác. Ở đó, việc mở / dev / fd / 1 giúp bạn có được tài nguyên giống như mở trên fd 1 như bạn mong đợi và như hầu hết các hệ thống ngoại trừ Linux, do đó, kết thúc bằng văn bản. Tuy nhiên, trên FreeBSD và Solaris, các đường ống là hai chiều. Vì vậy, miễn là programkhông ghi vào stdin của nó (không có ứng dụng nào), readsẽ không có gì để đọc từ hướng đó của đường ống.

Trên các hệ thống mà các đường ống không hai chiều, readcó thể sẽ không thành công khi có lỗi khi cố đọc từ một bộ mô tả tệp chỉ ghi. Cũng lưu ý rằng không phải tất cả các hệ thống đều có /dev/fd/x.


Rất đẹp! Trong thực tế các bài kiểm tra của tôi, bạn không cần xvới bash; hơn nữa với zsh bạn chỉ có thể làm readvà nó hoạt động (mặc dù tôi không hiểu tại sao!). Đây có phải là thủ thuật dành riêng cho Linux hay nó hoạt động trên tất cả các hệ thống * nix?
a3nm

@ a3nm, nếu bạn làm readmột mình, nó sẽ đọc từ stdin. Vì vậy, nếu đó là thiết bị đầu cuối, nó sẽ đọc những gì bạn gõ cho đến khi bạn nhấn enter.
Stéphane Chazelas

chắc chắn, tôi hiểu những gì đọc làm. Điều tôi không hiểu là tại sao việc đọc từ thiết bị đầu cuối với việc đọc trong một quá trình nền được chặn bằng bash nhưng không phải với zsh.
a3nm

@ a3nm, tôi không chắc ý của bạn là gì. Bạn có ý nghĩa gì bởi bạn chỉ có thể làm readvà nó hoạt động ?
Stéphane Chazelas

Tôi đang nói rằng với zsh bạn chỉ có thể làm read | program > outputvà nó hoạt động theo cách tương tự như những gì bạn đề xuất. (Và tôi không hiểu tại sao.)
a3nm

0

read Giải pháp của Stéphane Chazelas cũng hoạt động trên Mac OS X nếu mở fd đọc /dev/fd/1.

# using bash on Mac OS X
# -bash: /dev/fd/1: Permission denied
read x </dev/fd/1 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  1 0

exec 3<&- 3</dev/fd/1
read x 0<&3 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  0 0

Để có thể giết tail -f /dev/nulltrong một tập lệnh (ví dụ với SIGINT), cần phải làm nền cho taillệnh và wait.

#!/bin/bash
# ctrl-c will kill tail and exit script
trap 'trap - INT; kill "$!"; exit' INT
exec tail -f /dev/null & wait $!

-2

Chuyển hướng /dev/zerolàm đầu vào tiêu chuẩn!

program < /dev/zero > output &

9
Điều này sẽ cung cấp cho chương trình của anh ta một số lượng byte không giới hạn ... điều đáng buồn là sẽ làm cho nó trở nên bận rộn.
Jander

1
Đây không phải là trò đùa thực sự, / dev / zero sẽ không bao giờ đóng, giữ cho chuỗi ống mở. Tuy nhiên, poster nói rằng anh ta không tham gia stdin, vì vậy sẽ không có số không nào được chuyển sang chương trình. Đây không phải là một vòng lặp bận rộn, nó là một sự chờ đợi thuần túy.
sillyMunky

2
xin lỗi, OP không sử dụng stdin, vì vậy điều này sẽ xóa sạch đầu vào của anh ấy và sẽ được vẽ từ / dev / zero. Tôi nên đọc hai lần vào lần tới! Nếu OP không sử dụng stdin, đây sẽ là giải pháp thanh lịch nhất tôi từng thấy và sẽ không phải là một sự chờ đợi bận rộn.
sillyMunky
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.