Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nohup doesn't work when used with double-ampersand (&&) instead of semicolon (;)

Tags:

bash

nohup

I have a script that uses ssh to login to a remote machine, cd to a particular directory, and then start a daemon. The original script looks like this:

ssh server "cd /tmp/path ; nohup java server 0</dev/null 1>server_stdout 2>server_stderr &"

This script appears to work fine. However, it is not robust to the case when the user enters the wrong path so the cd fails. Because of the ;, this command will try to run the nohup command even if the cd fails.

The obvious fix doesn't work:

ssh server "cd /tmp/path && nohup java server 0</dev/null 1>server_stdout 2>server_stderr &"

that is, the SSH command does not return until the server is stopped. Putting nohup in front of the cd instead of in front of the java didn't work.

Can anyone help me fix this? Can you explain why this solution doesn't work? Thanks!

Edit: cbuckley suggests using sh -c, from which I derived:

ssh server "nohup sh -c 'cd /tmp/path && java server 0</dev/null 1>master_stdout 2>master_stderr' 2>/dev/null 1>/dev/null &"

However, now the exit code is always 0 when the cd fails; whereas if I do ssh server cd /failed/path then I get a real exit code. Suggestions?

like image 697
Dan Halperin Avatar asked Apr 10 '13 19:04

Dan Halperin


People also ask

Do you need ampersand with nohup?

To run a nohup command in the background, add an & (ampersand) to the end of the command. If the standard error is displayed on the terminal and if the standard output is neither displayed on the terminal, nor sent to the output file specified by the user (the default output file is nohup. out), both the ./nohup.

What is the difference between nohup and &?

nohup catches the hangup signal while the ampersand does not.

What is & After nohup?

It essentially returns control to you immediately and allows the command to complete in the background. This is almost always used with nohup because you typically want to exit the shell after starting the command.

Can we use nohup in the shell script?

Nohup, short for no hang up is a command in Linux systems that keep processes running even after exiting the shell or terminal. Nohup prevents the processes or jobs from receiving the SIGHUP (Signal Hang UP) signal. This is a signal that is sent to a process upon closing or exiting the terminal.


1 Answers

See Bash's Operator Precedence.

The & is being attached to the whole statement because it has a higher precedence than &&. You don't need ssh to verify this. Just run this in your shell:

$ sleep 100 && echo yay &
[1] 19934

If the & were only attached to the echo yay, then your shell would sleep for 100 seconds and then report the background job. However, the entire sleep 100 && echo yay is backgrounded and you're given the job notification immediately. Running jobs will show it hanging out:

$ sleep 100 && echo yay &
[1] 20124
$ jobs
[1]+  Running                 sleep 100 && echo yay &

You can use parenthesis to create a subshell around echo yay &, giving you what you'd expect:

sleep 100 && ( echo yay & )

This would be similar to using bash -c to run echo yay &:

sleep 100 && bash -c "echo yay &"

Tossing these into an ssh, and we get:

# using parenthesis...
$ ssh localhost "cd / && (nohup sleep 100 >/dev/null </dev/null &)"
$ ps -ef | grep sleep
me 20136     1  0 16:48 ?        00:00:00 sleep 100

# and using `bash -c`
$ ssh localhost "cd / && bash -c 'nohup sleep 100 >/dev/null </dev/null &'"
$ ps -ef | grep sleep
me 20145     1  0 16:48 ?        00:00:00 sleep 100

Applying this to your command, and we get

ssh server "cd /tmp/path && (nohup java server 0</dev/null 1>server_stdout 2>server_stderr &)"

or:

ssh server "cd /tmp/path && bash -c 'nohup java server 0</dev/null 1>server_stdout 2>server_stderr &'"

Also, with regard to your comment on the post,

Right, sh -c always returns 0. E.g., sh -c exit 1 has error code 0"

this is incorrect. Directly from the manpage:

Bash's exit status is the exit status of the last command executed in the script. If no commands are executed, the exit status is 0.

Indeed:

$ bash -c "true ; exit 1"
$ echo $?
1
$ bash -c "false ; exit 22"
$ echo $?
22
like image 169
Christopher Neylan Avatar answered Sep 19 '22 07:09

Christopher Neylan