Related
I already have an ssh agent set up, and I can run commands on an external server in Bash script doing stuff like:
ssh blah_server "ls; pwd;"
Now, what I'd really like to do is run a lot of long commands on an external server. Enclosing all of these in between quotation marks would be quite ugly, and I'd really rather avoid ssh'ing multiple times just to avoid this.
So, is there a way I can do this in one go enclosed in parentheses or something? I'm looking for something along the lines of:
ssh blah_server (
ls some_folder;
./someaction.sh;
pwd;
)
Basically, I'll be happy with any solution as long as it's clean.
Edit
To clarify, I'm talking about this being part of a larger bash script. Other people might need to deal with the script down the line, so I'd like to keep it clean. I don't want to have a bash script with one line that looks like:
ssh blah_server "ls some_folder; ./someaction.sh 'some params'; pwd; ./some_other_action 'other params';"
because it is extremely ugly and difficult to read.
How about a Bash Here Document:
ssh otherhost << EOF
ls some_folder;
./someaction.sh 'some params'
pwd
./some_other_action 'other params'
EOF
To avoid the problems mentioned by #Globalz in the comments, you may be able to (depending what you're doing on the remote site) get away with replacing the first line with
ssh otherhost /bin/bash << EOF
Note that you can do variable substitution in the Here document, but you may have to deal with quoting issues. For instance, if you quote the "limit string" (ie. EOF in the above), then you can't do variable substitutions. But without quoting the limit string, variables are substituted. For example, if you have defined $NAME above in your shell script, you could do
ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF
and it would create a file on the destination otherhost with the name of whatever you'd assigned to $NAME. Other rules about shell script quoting also apply, but are too complicated to go into here.
Edit your script locally, then pipe it into ssh, e.g.
cat commands-to-execute-remotely.sh | ssh blah_server
where commands-to-execute-remotely.sh looks like your list above:
ls some_folder
./someaction.sh
pwd;
To match your sample code, you can wrap your commands inside single or double qoutes. For example
ssh blah_server "
ls
pwd
"
I see two ways:
First you make a control socket like this:
ssh -oControlMaster=yes -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip>
and run your commands
ssh -oControlMaster=no -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip> -t <yourcommand>
This way you can write an ssh command without actually reconnecting to the server.
The second would be to dynamically generate the script, scping it and running.
This can also be done as follows.
Put your commands in a script, let's name it commands-inc.sh
#!/bin/bash
ls some_folder
./someaction.sh
pwd
Save the file
Now run it on the remote server.
ssh user#remote 'bash -s' < /path/to/commands-inc.sh
Never failed for me.
Put all the commands on to a script and it can be run like
ssh <remote-user>#<remote-host> "bash -s" <./remote-commands.sh
Not sure if the cleanest for long commands but certainly the easiest:
ssh user#host "cmd1; cmd2; cmd3"
This works well for creating scripts, as you do not have to include other files:
#!/bin/bash
ssh <my_user>#<my_host> "bash -s" << EOF
# here you just type all your commmands, as you can see, i.e.
touch /tmp/test1;
touch /tmp/test2;
touch /tmp/test3;
EOF
# you can use '$(which bash) -s' instead of my "bash -s" as well
# but bash is usually being found in a standard location
# so for easier memorizing it i leave that out
# since i dont fat-finger my $PATH that bad so it cant even find /bin/bash ..
SSH and Run Multiple Commands in Bash.
Separate commands with semicolons within a string, passed to echo, all piped into the ssh command. For example:
echo "df -k;uname -a" | ssh 192.168.79.134
Pseudo-terminal will not be allocated because stdin is not a terminal.
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda2 18274628 2546476 14799848 15% /
tmpfs 183620 72 183548 1% /dev/shm
/dev/sda1 297485 39074 243051 14% /boot
Linux newserv 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 2013 x86_64 x86_64 x86_64 GNU/Linux
The posted answers using multiline strings and multiple bash scripts did not work for me.
Long multiline strings are hard to maintain.
Separate bash scripts do not maintain local variables.
Here is a functional way to ssh and run multiple commands while keeping local context.
LOCAL_VARIABLE=test
run_remote() {
echo "$LOCAL_VARIABLE"
ls some_folder;
./someaction.sh 'some params'
./some_other_action 'other params'
}
ssh otherhost "$(set); run_remote"
For anyone stumbling over here like me - I had success with escaping the semicolon and the newline:
First step: the semicolon. This way, we do not break the ssh command:
ssh <host> echo test\;ls
^ backslash!
Listed the remote hosts /home directory (logged in as root), whereas
ssh <host> echo test;ls
^ NO backslash
listed the current working directory.
Next step: breaking up the line:
v another backslash!
ssh <host> echo test\;\
ls
This again listed the remote working directory - improved formatting:
ssh <host>\
echo test\;\
ls
If really nicer than here document or quotes around broken lines - well, not me to decide...
(Using bash, Ubuntu 14.04 LTS.)
The easiest way to configure your system to use single ssh sessions by default with multiplexing.
This can be done by creating a folder for the sockets:
mkdir ~/.ssh/controlmasters
And then adding the following to your .ssh configuration:
Host *
ControlMaster auto
ControlPath ~/.ssh/controlmasters/%r#%h:%p.socket
ControlMaster auto
ControlPersist 10m
Now, you do not need to modify any of your code. This allows multiple calls to ssh and scp without creating multiple sessions, which is useful when there needs to be more interaction between your local and remote machines.
Thanks to #terminus's answer, http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/ and https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing.
What is the cleanest way to ssh and run multiple commands in Bash?
I recommend using this escaping function. The function takes one argument - a function to escape. Then sshqfunc outputs declare -f of the function and then outputs a string that will call the function with "$#" arguments properly quoted. Then the whole is "%q" quoted and bash -c is added. In case the remote does not have bash, you could change bash to sh.
sshqfunc() { echo "bash -c $(printf "%q" "$(declare -f "$#"); $1 \"\$#\"")"; };
Then define a function with the work you want to do on the remote. The function is defined normally, so it will be properly "clean". You can test such function locally. After defining, properly escaped function is passed to the remote.
work() {
ls
pwd
echo "Some other command"
}
ssh host#something "$(sshqfunc work)"
Passing You can also pass arguments, and they will be passed to your function as positional arguments. The right next argument after the function will be assigned to $0 - usually a placeholder like -- or _ is used to separate arguments from call.
work() {
file=$1
num=$2
ls "$file"
echo "num is $num"
}
ssh host#something "$(sshqfunc work)" -- /this/file 5
But note that arguments should also be properly quoted if there are any magic characters:
ssh host#something "$(sshqfunc work)" -- "$(printf "%q" "$var1" "$var2")"
For simple commands you can use:
ssh <ssh_args> command1 '&&' command2
or
ssh <ssh_args> command1 \&\& command2
I already have an ssh agent set up, and I can run commands on an external server in Bash script doing stuff like:
ssh blah_server "ls; pwd;"
Now, what I'd really like to do is run a lot of long commands on an external server. Enclosing all of these in between quotation marks would be quite ugly, and I'd really rather avoid ssh'ing multiple times just to avoid this.
So, is there a way I can do this in one go enclosed in parentheses or something? I'm looking for something along the lines of:
ssh blah_server (
ls some_folder;
./someaction.sh;
pwd;
)
Basically, I'll be happy with any solution as long as it's clean.
Edit
To clarify, I'm talking about this being part of a larger bash script. Other people might need to deal with the script down the line, so I'd like to keep it clean. I don't want to have a bash script with one line that looks like:
ssh blah_server "ls some_folder; ./someaction.sh 'some params'; pwd; ./some_other_action 'other params';"
because it is extremely ugly and difficult to read.
How about a Bash Here Document:
ssh otherhost << EOF
ls some_folder;
./someaction.sh 'some params'
pwd
./some_other_action 'other params'
EOF
To avoid the problems mentioned by #Globalz in the comments, you may be able to (depending what you're doing on the remote site) get away with replacing the first line with
ssh otherhost /bin/bash << EOF
Note that you can do variable substitution in the Here document, but you may have to deal with quoting issues. For instance, if you quote the "limit string" (ie. EOF in the above), then you can't do variable substitutions. But without quoting the limit string, variables are substituted. For example, if you have defined $NAME above in your shell script, you could do
ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF
and it would create a file on the destination otherhost with the name of whatever you'd assigned to $NAME. Other rules about shell script quoting also apply, but are too complicated to go into here.
Edit your script locally, then pipe it into ssh, e.g.
cat commands-to-execute-remotely.sh | ssh blah_server
where commands-to-execute-remotely.sh looks like your list above:
ls some_folder
./someaction.sh
pwd;
To match your sample code, you can wrap your commands inside single or double qoutes. For example
ssh blah_server "
ls
pwd
"
I see two ways:
First you make a control socket like this:
ssh -oControlMaster=yes -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip>
and run your commands
ssh -oControlMaster=no -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip> -t <yourcommand>
This way you can write an ssh command without actually reconnecting to the server.
The second would be to dynamically generate the script, scping it and running.
This can also be done as follows.
Put your commands in a script, let's name it commands-inc.sh
#!/bin/bash
ls some_folder
./someaction.sh
pwd
Save the file
Now run it on the remote server.
ssh user#remote 'bash -s' < /path/to/commands-inc.sh
Never failed for me.
Put all the commands on to a script and it can be run like
ssh <remote-user>#<remote-host> "bash -s" <./remote-commands.sh
Not sure if the cleanest for long commands but certainly the easiest:
ssh user#host "cmd1; cmd2; cmd3"
This works well for creating scripts, as you do not have to include other files:
#!/bin/bash
ssh <my_user>#<my_host> "bash -s" << EOF
# here you just type all your commmands, as you can see, i.e.
touch /tmp/test1;
touch /tmp/test2;
touch /tmp/test3;
EOF
# you can use '$(which bash) -s' instead of my "bash -s" as well
# but bash is usually being found in a standard location
# so for easier memorizing it i leave that out
# since i dont fat-finger my $PATH that bad so it cant even find /bin/bash ..
SSH and Run Multiple Commands in Bash.
Separate commands with semicolons within a string, passed to echo, all piped into the ssh command. For example:
echo "df -k;uname -a" | ssh 192.168.79.134
Pseudo-terminal will not be allocated because stdin is not a terminal.
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda2 18274628 2546476 14799848 15% /
tmpfs 183620 72 183548 1% /dev/shm
/dev/sda1 297485 39074 243051 14% /boot
Linux newserv 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 2013 x86_64 x86_64 x86_64 GNU/Linux
The posted answers using multiline strings and multiple bash scripts did not work for me.
Long multiline strings are hard to maintain.
Separate bash scripts do not maintain local variables.
Here is a functional way to ssh and run multiple commands while keeping local context.
LOCAL_VARIABLE=test
run_remote() {
echo "$LOCAL_VARIABLE"
ls some_folder;
./someaction.sh 'some params'
./some_other_action 'other params'
}
ssh otherhost "$(set); run_remote"
For anyone stumbling over here like me - I had success with escaping the semicolon and the newline:
First step: the semicolon. This way, we do not break the ssh command:
ssh <host> echo test\;ls
^ backslash!
Listed the remote hosts /home directory (logged in as root), whereas
ssh <host> echo test;ls
^ NO backslash
listed the current working directory.
Next step: breaking up the line:
v another backslash!
ssh <host> echo test\;\
ls
This again listed the remote working directory - improved formatting:
ssh <host>\
echo test\;\
ls
If really nicer than here document or quotes around broken lines - well, not me to decide...
(Using bash, Ubuntu 14.04 LTS.)
The easiest way to configure your system to use single ssh sessions by default with multiplexing.
This can be done by creating a folder for the sockets:
mkdir ~/.ssh/controlmasters
And then adding the following to your .ssh configuration:
Host *
ControlMaster auto
ControlPath ~/.ssh/controlmasters/%r#%h:%p.socket
ControlMaster auto
ControlPersist 10m
Now, you do not need to modify any of your code. This allows multiple calls to ssh and scp without creating multiple sessions, which is useful when there needs to be more interaction between your local and remote machines.
Thanks to #terminus's answer, http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/ and https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing.
What is the cleanest way to ssh and run multiple commands in Bash?
I recommend using this escaping function. The function takes one argument - a function to escape. Then sshqfunc outputs declare -f of the function and then outputs a string that will call the function with "$#" arguments properly quoted. Then the whole is "%q" quoted and bash -c is added. In case the remote does not have bash, you could change bash to sh.
sshqfunc() { echo "bash -c $(printf "%q" "$(declare -f "$#"); $1 \"\$#\"")"; };
Then define a function with the work you want to do on the remote. The function is defined normally, so it will be properly "clean". You can test such function locally. After defining, properly escaped function is passed to the remote.
work() {
ls
pwd
echo "Some other command"
}
ssh host#something "$(sshqfunc work)"
Passing You can also pass arguments, and they will be passed to your function as positional arguments. The right next argument after the function will be assigned to $0 - usually a placeholder like -- or _ is used to separate arguments from call.
work() {
file=$1
num=$2
ls "$file"
echo "num is $num"
}
ssh host#something "$(sshqfunc work)" -- /this/file 5
But note that arguments should also be properly quoted if there are any magic characters:
ssh host#something "$(sshqfunc work)" -- "$(printf "%q" "$var1" "$var2")"
For simple commands you can use:
ssh <ssh_args> command1 '&&' command2
or
ssh <ssh_args> command1 \&\& command2
Can I run a here document script over ssh on remote machine with interactive mode?
Code example is:
ssh -t xijing#ggzshop.com 'bash -s' <<EOF
sudo ls
......Other big scripts......
EOF
double -t won't work properly as well.
-----------------------------One possible solution:-------------------
After a lot of tries, I come up with following answers:
Script=`cat <<'EOF'
sudo ls
.....Big scripts.....
EOF`
ssh -t user#host ${Script}
which will allow user to type password in.
Solution of Xijing appears to work ok for me. However, I did a couple of cosmetic changes. First, for readability I used "dollar-parentheses" instead of backticks. For another thing I don't offer any explanation: Semicolons were needed to separate multiple commands in Script snippet even though commands are written on separate lines. My test:
Script=$( cat <<'HERE'
hostname;
cat /etc/issue;
sudo id
HERE
)
ssh -t user#host ${Script}
Sudo password will be asked in a normal manner, no need to omit that.
No, I don't think you can run interactive scripts like that.
To achieve what you want, you could create dedicated users for your common admin tasks that can run admin commands with sudo without password. Next, setup ssh key authentication to login as the dedicated users and perform the necessary tasks.
It is not necessary to use semicolons to separate multiple commands in Script if there are quotes around it.
Script="$( cat <<'HERE'
hostname;
cat /etc/issue;
sudo id
HERE
)"
- ssh -t user#host ${Script}
+ ssh -t user#host "${Script}"
# alternative (not recommended)
# set IFS variable to null string to avoid deletion of newlines \n in unquoted variable expansion
export IFS=''
ssh -t user#host ${Script}
I already have an ssh agent set up, and I can run commands on an external server in Bash script doing stuff like:
ssh blah_server "ls; pwd;"
Now, what I'd really like to do is run a lot of long commands on an external server. Enclosing all of these in between quotation marks would be quite ugly, and I'd really rather avoid ssh'ing multiple times just to avoid this.
So, is there a way I can do this in one go enclosed in parentheses or something? I'm looking for something along the lines of:
ssh blah_server (
ls some_folder;
./someaction.sh;
pwd;
)
Basically, I'll be happy with any solution as long as it's clean.
Edit
To clarify, I'm talking about this being part of a larger bash script. Other people might need to deal with the script down the line, so I'd like to keep it clean. I don't want to have a bash script with one line that looks like:
ssh blah_server "ls some_folder; ./someaction.sh 'some params'; pwd; ./some_other_action 'other params';"
because it is extremely ugly and difficult to read.
How about a Bash Here Document:
ssh otherhost << EOF
ls some_folder;
./someaction.sh 'some params'
pwd
./some_other_action 'other params'
EOF
To avoid the problems mentioned by #Globalz in the comments, you may be able to (depending what you're doing on the remote site) get away with replacing the first line with
ssh otherhost /bin/bash << EOF
Note that you can do variable substitution in the Here document, but you may have to deal with quoting issues. For instance, if you quote the "limit string" (ie. EOF in the above), then you can't do variable substitutions. But without quoting the limit string, variables are substituted. For example, if you have defined $NAME above in your shell script, you could do
ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF
and it would create a file on the destination otherhost with the name of whatever you'd assigned to $NAME. Other rules about shell script quoting also apply, but are too complicated to go into here.
Edit your script locally, then pipe it into ssh, e.g.
cat commands-to-execute-remotely.sh | ssh blah_server
where commands-to-execute-remotely.sh looks like your list above:
ls some_folder
./someaction.sh
pwd;
To match your sample code, you can wrap your commands inside single or double qoutes. For example
ssh blah_server "
ls
pwd
"
I see two ways:
First you make a control socket like this:
ssh -oControlMaster=yes -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip>
and run your commands
ssh -oControlMaster=no -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip> -t <yourcommand>
This way you can write an ssh command without actually reconnecting to the server.
The second would be to dynamically generate the script, scping it and running.
This can also be done as follows.
Put your commands in a script, let's name it commands-inc.sh
#!/bin/bash
ls some_folder
./someaction.sh
pwd
Save the file
Now run it on the remote server.
ssh user#remote 'bash -s' < /path/to/commands-inc.sh
Never failed for me.
Put all the commands on to a script and it can be run like
ssh <remote-user>#<remote-host> "bash -s" <./remote-commands.sh
Not sure if the cleanest for long commands but certainly the easiest:
ssh user#host "cmd1; cmd2; cmd3"
This works well for creating scripts, as you do not have to include other files:
#!/bin/bash
ssh <my_user>#<my_host> "bash -s" << EOF
# here you just type all your commmands, as you can see, i.e.
touch /tmp/test1;
touch /tmp/test2;
touch /tmp/test3;
EOF
# you can use '$(which bash) -s' instead of my "bash -s" as well
# but bash is usually being found in a standard location
# so for easier memorizing it i leave that out
# since i dont fat-finger my $PATH that bad so it cant even find /bin/bash ..
SSH and Run Multiple Commands in Bash.
Separate commands with semicolons within a string, passed to echo, all piped into the ssh command. For example:
echo "df -k;uname -a" | ssh 192.168.79.134
Pseudo-terminal will not be allocated because stdin is not a terminal.
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda2 18274628 2546476 14799848 15% /
tmpfs 183620 72 183548 1% /dev/shm
/dev/sda1 297485 39074 243051 14% /boot
Linux newserv 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 2013 x86_64 x86_64 x86_64 GNU/Linux
The posted answers using multiline strings and multiple bash scripts did not work for me.
Long multiline strings are hard to maintain.
Separate bash scripts do not maintain local variables.
Here is a functional way to ssh and run multiple commands while keeping local context.
LOCAL_VARIABLE=test
run_remote() {
echo "$LOCAL_VARIABLE"
ls some_folder;
./someaction.sh 'some params'
./some_other_action 'other params'
}
ssh otherhost "$(set); run_remote"
For anyone stumbling over here like me - I had success with escaping the semicolon and the newline:
First step: the semicolon. This way, we do not break the ssh command:
ssh <host> echo test\;ls
^ backslash!
Listed the remote hosts /home directory (logged in as root), whereas
ssh <host> echo test;ls
^ NO backslash
listed the current working directory.
Next step: breaking up the line:
v another backslash!
ssh <host> echo test\;\
ls
This again listed the remote working directory - improved formatting:
ssh <host>\
echo test\;\
ls
If really nicer than here document or quotes around broken lines - well, not me to decide...
(Using bash, Ubuntu 14.04 LTS.)
The easiest way to configure your system to use single ssh sessions by default with multiplexing.
This can be done by creating a folder for the sockets:
mkdir ~/.ssh/controlmasters
And then adding the following to your .ssh configuration:
Host *
ControlMaster auto
ControlPath ~/.ssh/controlmasters/%r#%h:%p.socket
ControlMaster auto
ControlPersist 10m
Now, you do not need to modify any of your code. This allows multiple calls to ssh and scp without creating multiple sessions, which is useful when there needs to be more interaction between your local and remote machines.
Thanks to #terminus's answer, http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/ and https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing.
What is the cleanest way to ssh and run multiple commands in Bash?
I recommend using this escaping function. The function takes one argument - a function to escape. Then sshqfunc outputs declare -f of the function and then outputs a string that will call the function with "$#" arguments properly quoted. Then the whole is "%q" quoted and bash -c is added. In case the remote does not have bash, you could change bash to sh.
sshqfunc() { echo "bash -c $(printf "%q" "$(declare -f "$#"); $1 \"\$#\"")"; };
Then define a function with the work you want to do on the remote. The function is defined normally, so it will be properly "clean". You can test such function locally. After defining, properly escaped function is passed to the remote.
work() {
ls
pwd
echo "Some other command"
}
ssh host#something "$(sshqfunc work)"
Passing You can also pass arguments, and they will be passed to your function as positional arguments. The right next argument after the function will be assigned to $0 - usually a placeholder like -- or _ is used to separate arguments from call.
work() {
file=$1
num=$2
ls "$file"
echo "num is $num"
}
ssh host#something "$(sshqfunc work)" -- /this/file 5
But note that arguments should also be properly quoted if there are any magic characters:
ssh host#something "$(sshqfunc work)" -- "$(printf "%q" "$var1" "$var2")"
For simple commands you can use:
ssh <ssh_args> command1 '&&' command2
or
ssh <ssh_args> command1 \&\& command2
This is a follow-on question to the How do you use ssh in a shell script? question. If I want to execute a command on the remote machine that runs in the background on that machine, how do I get the ssh command to return? When I try to just include the ampersand (&) at the end of the command it just hangs. The exact form of the command looks like this:
ssh user#target "cd /some/directory; program-to-execute &"
Any ideas? One thing to note is that logins to the target machine always produce a text banner and I have SSH keys set up so no password is required.
I had this problem in a program I wrote a year ago -- turns out the answer is rather complicated. You'll need to use nohup as well as output redirection, as explained in the wikipedia artcle on nohup, copied here for your convenience.
Nohuping backgrounded jobs is for
example useful when logged in via SSH,
since backgrounded jobs can cause the
shell to hang on logout due to a race
condition [2]. This problem can also
be overcome by redirecting all three
I/O streams:
nohup myprogram > foo.out 2> foo.err < /dev/null &
This has been the cleanest way to do it for me:-
ssh -n -f user#host "sh -c 'cd /whereever; nohup ./whatever > /dev/null 2>&1 &'"
The only thing running after this is the actual command on the remote machine
Redirect fd's
Output needs to be redirected with &>/dev/null which redirects both stderr and stdout to /dev/null and is a synonym of >/dev/null 2>/dev/null or >/dev/null 2>&1.
Parantheses
The best way is to use sh -c '( ( command ) & )' where command is anything.
ssh askapache 'sh -c "( ( nohup chown -R ask:ask /www/askapache.com &>/dev/null ) & )"'
Nohup Shell
You can also use nohup directly to launch the shell:
ssh askapache 'nohup sh -c "( ( chown -R ask:ask /www/askapache.com &>/dev/null ) & )"'
Nice Launch
Another trick is to use nice to launch the command/shell:
ssh askapache 'nice -n 19 sh -c "( ( nohup chown -R ask:ask /www/askapache.com &>/dev/null ) & )"'
If you don't/can't keep the connection open you could use screen, if you have the rights to install it.
user#localhost $ screen -t remote-command
user#localhost $ ssh user#target # now inside of a screen session
user#remotehost $ cd /some/directory; program-to-execute &
To detach the screen session: ctrl-a d
To list screen sessions:
screen -ls
To reattach a session:
screen -d -r remote-command
Note that screen can also create multiple shells within each session. A similar effect can be achieved with tmux.
user#localhost $ tmux
user#localhost $ ssh user#target # now inside of a tmux session
user#remotehost $ cd /some/directory; program-to-execute &
To detach the tmux session: ctrl-b d
To list screen sessions:
tmux list-sessions
To reattach a session:
tmux attach <session number>
The default tmux control key, 'ctrl-b', is somewhat difficult to use but there are several example tmux configs that ship with tmux that you can try.
I just wanted to show a working example that you can cut and paste:
ssh REMOTE "sh -c \"(nohup sleep 30; touch nohup-exit) > /dev/null &\""
You can do this without nohup:
ssh user#host 'myprogram >out.log 2>err.log &'
Quickest and easiest way is to use the 'at' command:
ssh user#target "at now -f /home/foo.sh"
I think you'll have to combine a couple of these answers to get what you want. If you use nohup in conjunction with the semicolon, and wrap the whole thing in quotes, then you get:
ssh user#target "cd /some/directory; nohup myprogram > foo.out 2> foo.err < /dev/null"
which seems to work for me. With nohup, you don't need to append the & to the command to be run. Also, if you don't need to read any of the output of the command, you can use
ssh user#target "cd /some/directory; nohup myprogram > /dev/null 2>&1"
to redirect all output to /dev/null.
This worked for me may times:
ssh -x remoteServer "cd yourRemoteDir; ./yourRemoteScript.sh </dev/null >/dev/null 2>&1 & "
You can do it like this...
sudo /home/script.sh -opt1 > /tmp/script.out &
It appeared quite convenient for me to have a remote tmux session using the tmux new -d <shell cmd> syntax like this:
ssh someone#elsewhere 'tmux new -d sleep 600'
This will launch new session on elsewhere host and ssh command on local machine will return to shell almost instantly. You can then ssh to the remote host and tmux attach to that session. Note that there's nothing about local tmux running, only remote!
Also, if you want your session to persist after the job is done, simply add a shell launcher after your command, but don't forget to enclose in quotes:
ssh someone#elsewhere 'tmux new -d "~/myscript.sh; bash"'
Actually, whenever I need to run a command on a remote machine that's complicated, I like to put the command in a script on the destination machine, and just run that script using ssh.
For example:
# simple_script.sh (located on remote server)
#!/bin/bash
cat /var/log/messages | grep <some value> | awk -F " " '{print $8}'
And then I just run this command on the source machine:
ssh user#ip "/path/to/simple_script.sh"
If you run remote command without allocating tty, redirect stdout/stderr works, nohup is not necessary.
ssh user#host 'background command &>/dev/null &'
If you use -t to allocate tty to run interactive command along with background command, and background command is the last command, like this:
ssh -t user#host 'bash -c "interactive command; nohup backgroud command &>/dev/null &"'
It's possible that background command doesn't actually start. There's race here:
bash exits after nohup starts. As a session leader, bash exit results in HUP signal sent to nohup process.
nohup ignores HUP signal.
If 1 completes before 2, the nohup process will exit and won't start the background command at all. We need to wait nohup start the background command. A simple workaroung is to just add a sleep:
ssh -t user#host 'bash -c "interactive command; nohup backgroud command &>/dev/null & sleep 1"'
The question was asked and answered years ago, I don't know if openssh behavior changed since then. I was testing on:
OpenSSH_8.6p1, OpenSSL 1.1.1g FIPS 21 Apr 2020
I was trying to do the same thing, but with the added complexity that I was trying to do it from Java. So on one machine running java, I was trying to run a script on another machine, in the background (with nohup).
From the command line, here is what worked: (you may not need the "-i keyFile" if you don't need it to ssh to the host)
ssh -i keyFile user#host bash -c "\"nohup ./script arg1 arg2 > output.txt 2>&1 &\""
Note that to my command line, there is one argument after the "-c", which is all in quotes. But for it to work on the other end, it still needs the quotes, so I had to put escaped quotes within it.
From java, here is what worked:
ProcessBuilder b = new ProcessBuilder("ssh", "-i", "keyFile", "bash", "-c",
"\"nohup ./script arg1 arg2 > output.txt 2>&1 &\"");
Process process = b.start();
// then read from process.getInputStream() and close it.
It took a bit of trial & error to get this working, but it seems to work well now.
YOUR-COMMAND &> YOUR-LOG.log &
This should run the command and assign a process id you can simply tail -f YOUR-LOG.log to see results written to it as they happen. you can log out anytime and the process will carry on
If you are using zsh then use program-to-execute &! is a zsh-specific shortcut to both background and disown the process, such that exiting the shell will leave it running.
A follow-on to #cmcginty's concise working example which also shows how to alternatively wrap the outer command in double quotes. This is how the template would look if invoked from within a PowerShell script (which can only interpolate variables from within double-quotes and ignores any variable expansion when wrapped in single quotes):
ssh user#server "sh -c `"($cmd) &>/dev/null </dev/null &`""
Inner double-quotes are escaped with back-tick instead of backslash. This allows $cmd to be composed by the PowerShell script, e.g. for deployment scripts and automation and the like. $cmd can even contain a multi-line heredoc if composed with unix LF.
First follow this procedure:
Log in on A as user a and generate a pair of authentication keys. Do not enter a passphrase:
a#A:~> ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/a/.ssh/id_rsa):
Created directory '/home/a/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/a/.ssh/id_rsa.
Your public key has been saved in /home/a/.ssh/id_rsa.pub.
The key fingerprint is:
3e:4f:05:79:3a:9f:96:7c:3b:ad:e9:58:37:bc:37:e4 a#A
Now use ssh to create a directory ~/.ssh as user b on B. (The directory may already exist, which is fine):
a#A:~> ssh b#B mkdir -p .ssh
b#B's password:
Finally append a's new public key to b#B:.ssh/authorized_keys and enter b's password one last time:
a#A:~> cat .ssh/id_rsa.pub | ssh b#B 'cat >> .ssh/authorized_keys'
b#B's password:
From now on you can log into B as b from A as a without password:
a#A:~> ssh b#B
then this will work without entering a password
ssh b#B "cd /some/directory; program-to-execute &"
I think this is what you need:
At first you need to install sshpass on your machine.
then you can write your own script:
while read pass port user ip; do
sshpass -p$pass ssh -p $port $user#$ip <<ENDSSH1
COMMAND 1
.
.
.
COMMAND n
ENDSSH1
done <<____HERE
PASS PORT USER IP
. . . .
. . . .
. . . .
PASS PORT USER IP
____HERE