关于bash:让ssh在目标计算机的后台执行命令

Getting ssh to execute a command in the background on target machine

这是shell脚本中如何使用ssh的后续问题?问题。如果我想在远程机器上执行一个在该机器后台运行的命令,如何让ssh命令返回?当我尝试只在命令末尾包含和号(&;)时,它将挂起。命令的确切形式如下:

1
ssh user@target"cd /some/directory; program-to-execute &"

有什么想法吗?需要注意的一点是,登录到目标计算机总是会生成一个文本横幅,并且我设置了ssh密钥,因此不需要密码。


我在一年前写的一个程序中遇到了这个问题——结果发现答案相当复杂。您需要使用nohup以及输出重定向,正如在nohup上的wikipedia artcle中所解释的,为了您的方便复制到这里。

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:

1
nohup myprogram > foo.out 2> foo.err < /dev/null &amp;


这是对我来说最干净的方法:

1
ssh -n -f user@host"sh -c 'cd /whereever; nohup ./whatever > /dev/null 2>&1 &'"

在这之后唯一运行的是远程机器上的实际命令


重定向FD

输出需要使用&>/dev/null进行重定向,它将stderr和stdout都重定向到/dev/null,是>/dev/null 2>/dev/null>/dev/null 2>&1的同义词。

偏执

最好的方法是使用sh -c '( ( command ) & )',其中命令是任何东西。

1
ssh askapache 'sh -c"( ( nohup chown -R ask:ask /www/askapache.com &>/dev/null ) & )"'

诺霍普壳牌

您也可以直接使用nohup来启动shell:

1
ssh askapache 'nohup sh -c"( ( chown -R ask:ask /www/askapache.com &>/dev/null ) & )"'

尼斯发射

另一个技巧是使用nice启动命令/shell:

1
ssh askapache 'nice -n 19 sh -c"( ( nohup chown -R ask:ask /www/askapache.com &>/dev/null ) & )"'


我只想展示一个可以剪切和粘贴的工作示例:

1
ssh REMOTE"sh -c "(nohup sleep 30; touch nohup-exit) > /dev/null &""


如果您不能/不能保持连接打开,您可以使用屏幕,如果您有权安装它。

1
2
3
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 &

分离屏幕会话:ctrl a d

要列出屏幕会话:

1
screen -ls

要重新附加会话:

1
screen -d -r remote-command

请注意,屏幕还可以在每个会话中创建多个shell。使用TMUX也可以达到类似的效果。

1
2
3
user@localhost $ tmux
user@localhost $ ssh user@target # now inside of a tmux session
user@remotehost $ cd /some/directory; program-to-execute &

分离TMUX会话:ctrl bakbd d

要列出屏幕会话:

1
tmux list-sessions

要重新附加会话:

1
tmux attach <session number>

默认的tmux控制键"ctrl bakbd"有点难以使用,但有几个示例tmux配置随tmux一起提供,您可以尝试使用。


最快和最简单的方法是使用"at"命令:

ssh user@target"at now -f /home/foo.sh"


我想你得把这些答案结合起来才能得到你想要的。如果将nohup与分号结合使用,并用引号将整个内容括起来,则可以得到:

1
ssh user@target"cd /some/directory; nohup myprogram > foo.out 2> foo.err < /dev/null"

这似乎对我有用。使用nohup,不需要将&;附加到要运行的命令。此外,如果不需要读取命令的任何输出,则可以使用

1
ssh user@target"cd /some/directory; nohup myprogram > /dev/null 2>&1"

将所有输出重定向到/dev/null。


这对我很有用,五月的时候:

1
ssh -x remoteServer"cd yourRemoteDir; ./yourRemoteScript.sh </dev/null >/dev/null 2>&1 &"

实际上,每当我需要在一台复杂的远程机器上运行一个命令时,我喜欢将该命令放入目标机器上的一个脚本中,然后使用ssh运行该脚本。

例如:

1
2
3
4
5
# simple_script.sh (located on remote server)

#!/bin/bash

cat /var/log/messages | grep <some value> | awk -F"" '{print $8}'

然后我在源计算机上运行这个命令:

1
ssh user@ip"/path/to/simple_script.sh"

你可以这样做…

1
sudo /home/script.sh -opt1 > /tmp/script.out &

我试着做同样的事情,但是增加了复杂性,我试着用Java来做。因此,在运行Java的一台机器上,我试图在另一台机器上运行脚本(在后台)(NoHUP)。

在命令行中,这是有效的:(如果不需要-i keyfile来ssh到主机,则可能不需要它)

1
ssh -i keyFile user@host bash -c""nohup ./script arg1 arg2 > output.txt 2>&1 &""

注意,对于我的命令行,在"-c"后面有一个参数,所有参数都用引号括起来。但要在另一端工作,它仍然需要引号,所以我必须在其中放入转义引号。

从Java,这里是什么工作:

1
2
3
4
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.

这项工作花了一点时间,但现在看来工作得很好。


我想这就是你需要的:首先,您需要在您的机器上安装sshpass。然后您可以编写自己的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

首先遵循以下步骤:

以用户A身份登录并生成一对身份验证密钥。不要输入密码:

1
2
3
4
5
6
7
8
9
10
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

现在使用ssh在b上创建一个目录~/.ssh作为用户b(该目录可能已经存在,这很好):

1
2
a@A:~> ssh b@B mkdir -p .ssh
b@B's password:

最后将a的新公钥附加到b@b:.ssh/授权的密钥,最后输入b的密码:

1
2
a@A:~> cat .ssh/id_rsa.pub | ssh b@B 'cat >> .ssh/authorized_keys'
b@B's password:

从现在起,您可以从A以B身份登录到B,而不需要密码:

1
a@A:~> ssh b@B

这样就不用输入密码了

ssh b@b"cd/some/directory;要执行的程序&;