如何永远以优雅的方式无所事事?


82

我有一个程序,可以在上产生有用的信息,stdout但也可以读取stdin。我想将其标准输出重定向到文件,而不在标准输入上提供任何内容。到目前为止,一切都很好:我可以做到:

program > output

而且不要在tty中做任何事情。

但是,问题是我想在后台执行此操作。如果我做:

program > output &

程序将被挂起(“暂停(tty输入)”)。

如果我做:

program < /dev/null > output &

该程序立即终止,因为它达到了EOF。

似乎我需要的是插入program不确定的时间内不做任何事情并且不读取的内容stdin。以下方法起作用:

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

但是,这一切都非常丑陋。有是一种优雅的方式,使用标准的Unix工具,到“什么都不做,无限期”(意译man true)。我怎样才能做到这一点?(我在这里达到优雅的主要标准:没有临时文件;没有繁忙的等待或定期唤醒;没有奇特的实用程序;尽可能短。)


尝试su -c 'program | output &' user。我将要问一个类似的问题,即创建后台作业作为处理“服务/守护程序”的可接受方法。我还注意到,如果不进行重定向,就无法STDERR进行重定向STDOUT。programA 发送STDOUTSTDINprogramB 的解决方案,然后重定向STDERR到日志文件:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc 2012年

也许... su -c 'while true; do true; done | cat > ~/output &' user
mbrownnyc

那是什么样的程序?
若昂里斯本


为什么不简单地向您编写的程序添加一个开关呢?另外,我假设如果用stdin关闭1<&-它会退出程序?
w00t 2012年

Answers:


16

在支持它们的shell(ksh,zsh,bash4)中,您可以program作为协同进程开始

  • kshprogram > output |&
  • zshbashcoproc program > output

program后台开始,其输入从重定向pipe。管道的另一端通向壳体。

该方法的三个好处

  • 没有额外的过程
  • 您可以在program死亡时退出脚本(用于wait等待脚本)
  • program将终止(eof如果外壳退出,则进入其标准输入)。

这似乎可行,并且看起来是个好主意!(为公平起见,我要求提供一些内容传递给我的命令,而不是提供shell功能,但这只是运行中的XY问题。)我正在考虑接受此答案,而不是@PT的答案。
a3nm

1
@ a3nm tail -f /dev/null并不理想,因为它每秒读取一次/dev/null(Linux上使用inotify的当前版本的GNU tail实际上存在一个bug)。sleep inf或它的可移植性sleep 2147483647更高的方法是一种更好的方法,用于坐在那儿不执行IMO的命令(请注意,该命令sleep内置在一些外壳中,例如ksh93or mksh)。
斯特凡Chazelas

78

我认为您不会比这更优雅

tail -f /dev/null

您已经提出的建议(假设此方法在内部使用inotify,则不应进行轮询或唤醒,因此,除了外观怪异之外,应该已经足够了)。

您需要一个可以无限期运行,将其stdout保持打开状态但实际上不会向stdout写入任何内容并且在关闭其stdin时不会退出的实用程序。yes实际写到stdout的东西。 cat当标准输入关闭时(或您重定向到它的所有输入完成),它将退出。我认为sleep 1000000000d可能有效,但是tail显然更好。我的Debian盒子有一个tailf可以稍微缩短命令的地方。

换个角度看,在下面运行程序怎么样screen


我最喜欢这种tail -f /dev/null方法,并且发现它也足够优雅,因为命令用法与预期目的非常接近。
2012年

2
strace tail -f /dev/null看来,tail使用inotify和唤醒发生在像sudo touch /dev/null。令人遗憾的是,似乎没有更好的解决方案……我想知道哪种方法才是实现更好解决方案的正确系统调用。
a3nm 2012年

5
@ a3nm syscall应该是pause,但它不会直接暴露给Shell接口。
吉尔斯

PT:我知道screen,但这是为了测试目的而从shell脚本运行该程序的多个实例,因此使用screen有点过头。
a3nm 2012年

3
@sillyMunky傻猴子,WaelJ的回答是错误的(将无限零发送到stdin)。
PT

48

sleep infinity 是我所知道的最清晰的解决方案。

您可以使用infinity,因为sleep接受一个浮点数*,这可能是十进制十六进制无穷大NaN的,根据man strtod

*这不是POSIX标准的一部分,因此不如便携式tail -f /dev/null。但是,它在GNU coreutils(Linux)和BSD(在Mac上使用)中受支持(显然在较新的Mac版本中不支持-请参见注释)。


哈哈,这确实是一个不错的方法。:)
a3nm 2014年

@ a3nm:谢谢:)似乎sleep infinity可以在BSD和Mac上使用
Zaz

无限睡眠过程需要什么样的资源?只是RAM?
CMCDragonkai

1
该答案要求sleep infinity最多等待24天;谁是对的?
nh2

1
@Zaz我已经详细研究了这个问题。原来您是正确的!该sleep实用程序不局限于24天 ; 它只是第一个睡眠24天的系统调用,之后它将执行更多此类系统调用。在这里查看我的评论:stackoverflow.com/questions/2935183/…–
nh2

19
sleep 2147483647 | program > output &

是的,它2^31-1是一个有限的数字,它不会永远运行,但是当睡眠最终超时时,我会给您$ 1000。(提示:到那时我们中的一个将会死。)

  • 没有临时文件;校验。
  • 没有繁忙的等待或定期唤醒;校验
  • 没有奇特的工具;校验。
  • 越短越好。好吧,它可能会更短。

5
bash:睡眠$(((64#1 _____)))| 程序>输出&
Diego Torres Milano 2012年

睡了68 ,睡了98个世纪sleep 2147483647d...
AGC 2015年

9

您可以创建一个二进制文件来执行以下操作:

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

5

这是使用标准Unix实用程序的另一项建议,“无限期地不执行任何操作”

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

这将触发立即发送的shell SIGSTOP,这将暂停进程。这用作程序的“输入”。的补码SIGSTOPSIGCONT,即,如果您知道外壳程序具有PID 12345,则可以kill -CONT 12345使其继续。


2

在Linux上,您可以执行以下操作:

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

在Linux上,打开/ dev / fd / x,其中x是管道写入端的文件描述符,可以获取管道的读取端,因此此处与程序的stdin相同。因此,基本上,它read永远不会返回,因为可能写入该管道的唯一内容是自身,并且read不会输出任何内容。

它也可以在FreeBSD或Solaris上运行,但这是另一个原因。在那里,打开/ dev / fd / 1可以像您期望的那样获得与fd 1上打开相同的资源,并且除Linux以外的大多数系统都可以使用,因此管道的编写结束。但是,在FreeBSD和Solaris上,管道是双向的。因此,只要program不写入其stdin(没有应用程序执行),read就不会从管道的该方向读取任何内容。

在管道不是双向的系统上,read尝试从只写文件描述符读取时可能会因错误而失败。另请注意,并非所有系统都有/dev/fd/x


非常好!实际上,我的测试不需要xwith bash;进一步使用zsh,您可以做得到read并且可以工作(尽管我不明白为什么!)。这个技巧是特定于Linux的,还是可以在所有* nix系统上使用?
2015年

@ a3nm,如果您一个人做read,它将从stdin中读取。因此,如果是终端机,它将显示您键入的内容,直到您按Enter键为止。
斯特凡Chazelas

当然,我了解阅读的内容。我不明白的是为什么使用bash而不是zsh阻止从终端读取并在后台进程中进行读取。
a3nm

@ a3nm,我不确定您的意思。您可以read做到的意思是什么,并且它有效吗?
斯特凡Chazelas

我说的是,使用zsh可以做到,read | program > output并且它的工作方式与您建议的相同。(我不明白为什么。)
a3nm

0

如果在上打开read fd,则StéphaneChazelas的read 解决方案也可以在Mac OS X上运行/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

为了能够杀死tail -f /dev/null脚本(例如,使用SIGINT),必须使tail命令和成为后台wait

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

-2

重定向/dev/zero为标准输入!

program < /dev/zero > output &

9
这将使他的程序无穷多个零字节……可悲的是,它将使其忙循环。
詹德(Jander)2012年

1
这不是真正的jander,/ dev / zero永远不会关闭,保持管道链处于打开状态。但是,发帖人说他不接受标准输入,因此不会将零转移到程序中。这根本不是一个繁忙的循环,而是纯粹的等待。
sillyMunky 2012年

2
抱歉,OP确实使用了stdin,因此这将清除他的输入,并且将从/ dev / zero中进行绘制。下次我应该读两次!如果OP没有使用stdin,这将是我见过的最优雅的解决方案,并且不会是一个繁忙的等待。
sillyMunky 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.