108

I have a program which produces useful information on stdout but also reads from stdin. I want to redirect its standard output to a file without providing anything on standard input. So far, so good: I can do:

program > output

and don't do anything in the tty.

However, the problem is I want to do this in the background. If I do:

program > output &

the program will get suspended ("suspended (tty input)").

If I do:

program < /dev/null > output &

the program terminates immediately because it reaches EOF.

It seems that what I need is to pipe into program something which does not do anything for an indefinite amount of time and does not read stdin. The following approaches work:

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

However, this is all very ugly. There has to be an elegant way, using standard Unix utilities, to "do nothing, indefinitely" (to paraphrase man true). How could I achieve this? (My main criteria for elegance here: no temporary files; no busy-waiting or periodic wakeups; no exotic utilities; as short as possible.)

8
  • Try su -c 'program | output &' user. I am about to ask a similar question with creating background jobs as an acceptable method for handling a "service/daemon." I also noticed that I could not redirect STDERR without also redirecting STDOUT. The solution where programA sends STDOUT to STDIN of programB, then redirects STDERR to a log file: programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
    – brandeded
    Commented Jul 12, 2012 at 19:12
  • maybe... su -c 'while true; do true; done | cat > ~/output &' user?
    – brandeded
    Commented Jul 12, 2012 at 19:39
  • what kind of program is that? Commented Jul 13, 2012 at 9:38
  • João Portela: This is a program I wrote, gitorious.org/irctk
    – a3nm
    Commented Jul 13, 2012 at 12:37
  • Why not simply add a switch to that program you wrote? Also, I assume that if you close stdin with 1<&- it will exit your program?
    – w00t
    Commented Jul 19, 2012 at 19:10

11 Answers 11

105

I don't think you're going to get any more elegant than the

tail -f /dev/null

that you already suggested (assuming this uses inotify internally, there should be no polling or wakeups, so other than being odd looking, it should be sufficient).

You need a utility that will run indefinitely, will keep its stdout open, but won't actually write anything to stdout, and won't exit when its stdin is closed. Something like yes actually writes to stdout. cat will exit when its stdin is closed (or whatever you re-direct into it is done). I think sleep 1000000000d might work, but the tail is clearly better. My Debian box has a tailf that shortens command slightly.

Taking a different tack, how about running the program under screen?

8
  • 1
    I like the tail -f /dev/null approach the best and find it elegant enough as well, since the command usage matches the intended purpose quite closely.
    – jw013
    Commented Jul 12, 2012 at 19:40
  • 2
    From strace tail -f /dev/null it seems that tail uses inotify and that wakeups occur in silly cases like sudo touch /dev/null. It's sad that there seems to be no better solution... I wonder which would be the right syscall to use to implement a better solution.
    – a3nm
    Commented Jul 12, 2012 at 19:59
  • 7
    @a3nm The syscall would be pause, but it isn't exposed directly to a shell interface. Commented Jul 12, 2012 at 22:25
  • 3
    @sillyMunky Silly Monkey, WaelJ's answer is wrong (sends infinite zeros to stdin).
    – P.T.
    Commented Jul 22, 2012 at 18:07
  • 1
    stackoverflow.com/questions/2935183/… is worth a read for why to prefer sleep loop over tailing /dev/null
    – jdf
    Commented Sep 6, 2018 at 19:43
66

sleep infinity is the clearest solution I know of.

You can use infinity because sleep accepts a floating point number*, which may be decimal, hexadecimal, infinity, or NaN, according to man strtod.

* This isn't part of the POSIX standard, so isn't as portable as tail -f /dev/null. However, it is supported in GNU coreutils (Linux) and BSD (used on Mac) (apparently not supported on newer versions of Mac — see comments).

15
  • 2
    Haha, that's really a nice approach. :)
    – a3nm
    Commented May 7, 2014 at 12:16
  • 2
    @a3nm: Thanks : ) Seems sleep infinity also works on BSD and Mac.
    – Zaz
    Commented Jul 8, 2014 at 20:50
  • 2
    sleep infinity doesn't work for me on Mac OS X 10.9. It just returns after a few microseconds. Commented Aug 9, 2015 at 0:05
  • 1
    This answer claims that sleep infinity waits for 24 days at max; who's right?
    – nh2
    Commented Dec 11, 2017 at 0:32
  • 5
    @Zaz I have investigated the issue in detail now. It turns out that you were initially correct! The sleep utility is not limited to 24 days; it is just the first syscall that sleeps for 24 days, and afterwards it will do more such syscalls. See my comment here: stackoverflow.com/questions/2935183/…
    – nh2
    Commented Dec 15, 2017 at 20:54
23
sleep 2147483647 | program > output &

Yes, 2^31-1 is a finite number, and it won't run forever, but I'll give you $1000 when the sleep finally times out. (Hint: one of us will be dead by then.)

  • no temporary files; check.
  • no busy-waiting or periodic wakeups; check
  • no exotic utilities; check.
  • as short as possible. Okay, it could be shorter.
2
  • 6
    bash: sleep $((64#1_____)) | program > output & Commented Jul 13, 2012 at 5:24
  • 2
    That sleeps for 68 years, this sleeps for 98 centuries: sleep 2147483647d...
    – agc
    Commented Jun 4, 2017 at 16:14
17

In shells that support them (ksh, zsh, bash4), you can start program as a co-process.

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

That starts program in background with its input redirected from a pipe. The other end of the pipe is open to the shell.

Three benefits of that approach

  • no extra process
  • you can exit the script when program dies (use wait to wait for it)
  • program will terminate (get eof on its stdin if the shell exits).
4
  • That seems to work and looks like a great idea! (To be fair, I had asked for something to pipe to my command, not for a shell feature, but this was just the XY problem at play.) I'm considering accepting this answer instead of @P.T.'s one.
    – a3nm
    Commented Aug 14, 2015 at 21:26
  • 2
    @a3nm, tail -f /dev/null is not ideal as it does a read every second on /dev/null (current versions of GNU tail on Linux using inotify there is actually a bug). sleep inf or its more portable equivalent sleep 2147483647 are better approaches for a command that sits there doing nothing IMO (note that sleep is built in a few shells like ksh93 or mksh). Commented Aug 15, 2015 at 21:03
  • coproc doesn't seem to be part of Mac OS X, while tail -f /dev/null works on Linux and Mac OS X. My version is GNU bash, version 3.2.57(1)-release (arm64-apple-darwin20). Obviously S.O. has an answer for this too: stackoverflow.com/questions/40181521/… Commented Feb 28, 2021 at 22:27
  • Is an "elegant approach" that isn't available to most user shells, by installed base, actually elegant? Scenarios that require blocking execution, like synchronizing between concurrent/parallel threads of execution, should prioritize portability over anything else - this answer is going to fubar junior/inexperienced operators Commented Mar 8, 2022 at 21:44
12

You can create a binary that does just that with:

$ echo 'main(){pause();}'| gcc -Wno-all -xc - -o pause

I find this elegant because the pause system call is literally the one system call whose sole purpose is to do nothing forever (or until a signal kills the process) without busy-waiting, doing it as a side-effect of something else or without telling the parent process (parents can elegantly nonblockingly waitpid on their children to be signal-stopped, and many (most notably shells) do, while it is quite a bit harder to wait on a child to get blocked in a non-signal-raising system call).

2
  • @roaima Yup, or echo 'main(){pause();}'| gcc -Wno-all -xc - -o pause on any POSIX shell. Commented Sep 14, 2023 at 11:44
  • Oops, yes of course :-) That was me overthinking it Commented Sep 14, 2023 at 12:00
8

Here's another suggestion using standard Unix utilities, to "do nothing, indefinitely".

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

This fires up a shell that is immediately sent SIGSTOP, which suspends the process. This is used as "input" to your program. The complement of SIGSTOP is SIGCONT, i.e. if you know the shell has PID 12345 you can kill -CONT 12345 to make it continue.

3

On Linux, you can do:

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

On Linux, opening /dev/fd/x where x is a file descriptor to the writing end of a pipe, gets you the reading end of the pipe, so here the same as on the stdin of program. So basically, read will never return, because the only thing that may write to that pipe is itself, and read doesn't output anything.

It will also work on FreeBSD or Solaris, but for another reason. There, opening /dev/fd/1 gets you the same resource as open on fd 1 as you'd expect and as most systems except Linux do, so the writing end of the pipe. However, on FreeBSD and Solaris, pipes are bidirectional. So as long as program doesn't write to its stdin (no application does), read will get nothing to read from that direction of the pipe.

On systems where pipes are not bidirectional, read will probably fail with an error when attempting to read from a write-only file descriptor. Also note that not all systems have /dev/fd/x.

5
  • Very nice! In fact my tests you don't need the x with bash; further with zsh you can just do read and it works (though I don't understand why!). Is this trick Linux-specific, or does it work on all *nix systems?
    – a3nm
    Commented Aug 11, 2015 at 22:45
  • @a3nm, if you do read alone, it will read from stdin. So if it's the terminal, it will read what you type until you press enter. Commented Aug 12, 2015 at 7:10
  • sure, I understand what read does. What I don't understand is why reading from the terminal with read in a backgrounded process is blocking with bash but not with zsh.
    – a3nm
    Commented Aug 14, 2015 at 21:19
  • @a3nm, I'm not sure what you mean. What do you mean by you can just do read and it works? Commented Aug 15, 2015 at 20:51
  • I'm saying that with zsh you can just do read | program > output and it works in the same way as what you suggested. (And I don't get why.)
    – a3nm
    Commented Aug 23, 2015 at 17:56
1

Stéphane Chazelas' read solution works on Mac OS X as well if a reading fd gets opened on /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

To be able to kill tail -f /dev/null in a script (with SIGINT, for example) it is necessary to background the tail command and wait.

#!/bin/bash
# ctrl-c will kill tail and exit script
trap 'trap - INT; kill "$!"; exit' INT
exec tail -f /dev/null & wait $!
1
  • This workaround for macOS (using fd 3) doesn't actually work -- it reads from the shell's original stdin, so it will exit if you type something (which is what this question is trying to avoid).
    – tom
    Commented May 22, 2023 at 16:10
1

There are problems with the solutions already mentioned:

  • sleep infinity is not supported by some libc/musl versions.
  • tail -f /dev/null wakes up each time some process drops something to /dev/null.
  • pause() requires gcc to be installed
  • piping from a halted shell will keep going even after the program is terminated

A good alternative that avoids these limitations is waiting on a halted process:

mkfifo pipe; (while :; do :; done & kill -STOP $!) > pipe; command < pipe > output & wait $!

0
-1

When reading these answers you have to ask yourself:

  • Is the CPU idling while your no-op command executes? You don't want a solution which burns CPU cycles while doing your no-op command. Most answers on this page does not answer this question.

  • Is the no-op command immediately interruptible? This is quite often a requirement although you may not think of it up front. (the word immediately is important here: you don't want to wait for some sleep interval to expire before the process realizes it has been interrupted). Most answers on this page does not answer this question.

DO NOT IMPLEMENT ANY ANSWERS FROM THIS PAGE UNLESS YOU KNOW (AND CAN ACCEPT) HOW THE PROPOSED SOLUTION COMPLIES WITH ABOVE.

In modern Bash you can do something like trap : TERM INT; sleep infinity & wait which complies with both of the above requirements. However, it seems convoluted and brittle to me. It won't work on Linux/FreeBSD/MacOS implementations which do not support sleep infinity. This being said, it is probably the best userland solution at the moment.

The requirement is dead simple to implement in C. The Kubernetes project has done it in their famous pause docker image which is one of the smallest docker images known to man. You can find the C source code HERE (it can be shortened even further). Unfortunately, I do not know of a userland "pause" command in Linux, at least not one which has been accepted into the major distros.

1
  • Most if not all of your concerns are already addressed in other answers here. None of the current answers suggest busy loops. All are interruptible in some way or another. At least one of the existing answers already provides an implementation of pause. Even your suggested implementation of sleep infinity is over-complex as sleep itself is already interruptible Commented Sep 14, 2023 at 10:48
-4

Redirect /dev/zero as standard input!

program < /dev/zero > output &
3
  • 10
    This would give his program an infinite number of zero-bytes... which, sadly, would make it busy-loop.
    – Jander
    Commented Jul 12, 2012 at 23:51
  • 1
    This is not true jander, /dev/zero will never close, holding the pipe chain open. However, poster says he doesn't take in stdin, so no zeros will ever be transferred to program. This is not a busy loop at all, it is a pure wait.
    – sillyMunky
    Commented Jul 22, 2012 at 17:38
  • 3
    sorry, OP does use stdin, so this will wipe out his input and will be drawing from /dev/zero. I should read twice next time! If OP wasn't using stdin, this would be the most elegant solution I've seen, and would not be a busy wait.
    – sillyMunky
    Commented Jul 22, 2012 at 17:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .