20

I copied a snippet of Bash to background an ssh command executed remotely:

ssh user@remote <<CMD
some process <&- >log 2>error &
CMD

What does <&- do?
My guess is that it is the same as < /dev/null

My next understanding is that the three main file descriptors (stdin, stdout, stderr) need to be closed to prevent:

  1. The job being backgrounded and the script exiting -- conflicting somehow?
  2. When the terminal closes, all processes that are accepting stdin from terminal are closed?

3 Answers3

30

<&- is not quite the same thing as < /dev/null. <&- closes fd 0, whereas < /dev/null redirects it from the device /dev/null, which never provides any data and always gives EOF on read. The difference is mostly that a read(2) call from a closed FD (the <&- case) will error with EBADF, whereas a call from a null-redirected FD will return no bytes read (end-of-file condition). If your program never reads from stdin, the distinction doesn't matter.

Closing the FDs is good practice if you're backgrounding something, since a backgrounded process will hang if it tries to read anything from TTY. This example doesn't fully handle everything it should, though; ideally there would be a nohup or setsid invocation somewhere, to fully disassociate the background process.

Tom Hunt
  • 10,056
  • So I should use nohup in addition to closing file descriptors? – Eric Francis Sep 17 '15 at 16:31
  • 2
    The most thorough method (which imitates the way programs daemonize themselves) is something like setsid some process <&- >path/to/log 2>path/to/error. The quicker method is something like nohup some process &. – Tom Hunt Sep 17 '15 at 16:34
  • 2
    @EricFrancis: Using nohup make no sense here. nohup prevent process from receiving HUP signal when its control terminal closed. But you didn't have any terminal in this case. – cuonglm Sep 17 '15 at 16:44
  • @TomHunt: The background process didn't hang, the ssh session did. – cuonglm Sep 17 '15 at 16:48
  • 2
    not a good idea to close fds 0, 1 & 2 ... you don't want the next fd created to take up one of those values. better to redirect them to /dev/null – Murray Jensen Sep 19 '15 at 00:01
  • @MurrayJensen True enough. Presumably if you're closing them you're stating intent to stop using functions that default to them, but no point in tempting fate. – Tom Hunt Sep 19 '15 at 05:55
  • @MurrayJensen I don't understand. Can you refer me to a resource that explains "you don't want the next fd created to take up one of those values"? Also, which comment were you responding to? – Eric Francis Sep 24 '15 at 15:25
  • when you open a file the system creates a file descriptor and assigns it the smallest non-negative number that is not already assigned. e.g. if you close file descriptor 0, the next successful open will return 0. the convention is for file descriptors 0, 1 and 2 to have special meaning, being stdin, stdout and stderr resp. so closing fds 0, 1 or 2 instead of redirecting from/to /dev/null could mean you have an ordinary fd being used as a standard input or output channel. I was commenting on the main response "Closing the FDs is good practice ..." - which is true, except for fds 0, 1 & 2. – Murray Jensen Sep 24 '15 at 22:50
7

See man bash:

  [n]<&word

is used to duplicate input file descriptors. If word expands to one or more digits, the file descriptor denoted by n is made to be a copy of that file descriptor. If the digits in word do not specify a file descriptor open for input, a redirection error occurs. If word evaluates to -, file descriptor n is closed. If n is not specified, the standard input (file descriptor 0) is used.

choroba
  • 47,233
7

<&- close standard input.

The general form, defined by POSIX, is:

[n]<&word

Its purpose to made file descriptor n is a copy of file descriptor denoted by word. Standard in is assumed if n is omitted, and if word is -, the file descriptor n will be closed.

It's not the same as </dev/null, since when in case of </dev/null, the standard input still opened, and was redirected to other place.

You need to closed all file descriptors of processes which were attached to ssh socket, otherwise, ssh session can not close.

You can run the command on remote machine without attach it to ssh session, by using screen or tmux:

ssh user@remote 'screen -S test -d -m command'
cuonglm
  • 153,898