让 ssh 在目标机器的后台执行命令
Posted
技术标签:
【中文标题】让 ssh 在目标机器的后台执行命令【英文标题】:Getting ssh to execute a command in the background on target machine 【发布时间】:2010-09-06 22:43:52 【问题描述】:这是How do you use ssh in a shell script? 问题的后续问题。如果我想在该机器上在后台运行的远程机器上执行命令,我如何让 ssh 命令返回?当我尝试在命令末尾包含与号 (&) 时,它只是挂起。该命令的确切形式如下所示:
ssh user@target "cd /some/directory; program-to-execute &"
有什么想法吗?需要注意的一点是,登录到目标机器总是会产生一个文本横幅,并且我设置了 SSH 密钥,因此不需要密码。
【问题讨论】:
【参考方案1】:我在一年前编写的一个程序中遇到了这个问题——结果答案相当复杂。您需要使用 nohup 以及输出重定向,如nohup 上的***文章中所述,为方便起见,复制到此处。
Nohuping 后台作业适用于 通过 SSH 登录时有用的示例, 因为后台作业会导致 由于比赛,shell 挂起注销 条件[2]。这个问题还可以 通过重定向所有三个来克服 I/O 流:
nohup myprogram > foo.out 2> foo.err < /dev/null &
【讨论】:
这些文件是在当前目录中创建的。所以限制是分区上的可用空间量。当然你也可以重定向到/dev/null
。
在完成请求提示后,对进程进行后台处理有什么想法吗? (就像一个 gpg --decrypt 完成了询问密码)
尝试使用 nohup 在后台启动一个 resque worker,但它不起作用.. :(
你能解释一下< /dev/null
是什么意思吗?谢谢。
同样来自关于 nohup 的***文章:“还请注意,关闭 SSH 会话并不总是向依赖的进程发送 HUP 信号。除其他外,这取决于是否分配了伪终端。”因此,虽然严格来说可能并不总是需要 nohup,但长期使用它比不使用它更好。【参考方案2】:
这对我来说是最干净的方式:-
ssh -n -f user@host "sh -c 'cd /whereever; nohup ./whatever > /dev/null 2>&1 &'"
在这之后唯一运行的是远程机器上的实际命令
【讨论】:
-n
不需要,因为-f
暗示-n
ssh -f
使 ssh 进程保持连接,只是在后台运行。 /dev/null 解决方案允许 ssh 快速断开连接,这可能更可取。
此解决方案也适用于通过 ssh 向 Syonology NAS 发送命令
我需要 "ls -l" 的结果显示在我的遥控器上,这个命令不这样做。
@Siddharth 然后重定向到某个命名文件而不是 /dev/null。【参考方案3】:
重定向 fd 的
需要使用&>/dev/null
重定向输出,这会将stderr 和stdout 都重定向到/dev/null,并且是>/dev/null 2>/dev/null
或>/dev/null 2>&1
的同义词。
括号
最好的方法是使用sh -c '( ( command ) & )'
,其中命令是任何东西。
ssh askapache 'sh -c "( ( nohup chown -R ask:ask /www/askapache.com &>/dev/null ) & )"'
Nohup 外壳
你也可以直接使用 nohup 来启动 shell:
ssh askapache 'nohup sh -c "( ( chown -R ask:ask /www/askapache.com &>/dev/null ) & )"'
不错的发布
另一个技巧是使用 nice 启动命令/shell:
ssh askapache 'nice -n 19 sh -c "( ( nohup chown -R ask:ask /www/askapache.com &>/dev/null ) & )"'
【讨论】:
我知道这是您的一个非常古老的答案,但是您能否添加一些 cmets 说明为什么括号方式是最好的方式,添加nohup
有什么(如果有)差异,以及为什么和你什么时候会使用nice
?我认为这会给这个答案增加很多。
也许可以部分回答这个问题:使用 nohup,您不需要将 & 附加到要运行的命令中。【参考方案4】:
如果您不/不能保持连接打开,您可以使用screen,如果您有权安装它。
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
列出屏幕会话:
screen -ls
重新附加会话:
screen -d -r remote-command
请注意,screen 还可以在每个会话中创建多个 shell。使用tmux也可以达到类似的效果。
user@localhost $ tmux
user@localhost $ ssh user@target # now inside of a tmux session
user@remotehost $ cd /some/directory; program-to-execute &
要分离 tmux 会话:ctrl-b d
列出屏幕会话:
tmux list-sessions
重新附加会话:
tmux attach <session number>
默认的 tmux 控制键“ctrl-b”有点难以使用,但是您可以尝试使用 tmux 附带的几个示例 tmux 配置。
【讨论】:
如何使用screen
?【参考方案5】:
我只是想展示一个可以剪切和粘贴的工作示例:
ssh REMOTE "sh -c \"(nohup sleep 30; touch nohup-exit) > /dev/null &\""
【讨论】:
非常有帮助。谢谢。【参考方案6】:我认为您必须将其中的几个答案结合起来才能得到您想要的。如果您将 nohup 与分号一起使用,并将整个内容用引号括起来,那么您会得到:
ssh user@target "cd /some/directory; nohup myprogram > foo.out 2> foo.err < /dev/null"
这似乎对我有用。使用 nohup,您无需将 & 附加到要运行的命令中。另外,如果您不需要读取任何命令的输出,您可以使用
ssh user@target "cd /some/directory; nohup myprogram > /dev/null 2>&1"
将所有输出重定向到 /dev/null。
【讨论】:
【参考方案7】:最快最简单的方法是使用'at'命令:
ssh user@target "at now -f /home/foo.sh"
【讨论】:
如果at
在时间之后接受命令行参数,而不仅仅是来自文件,这将是一个很好的通用解决方案。
你可以用
涉及一个单独的守护进程(并使用除非该守护进程正在运行,否则将无法工作的工具)对于doesn't require an extra daemon at all 而言似乎是很多不必要的复杂性。【参考方案8】:
这可能对我有用:
ssh -x remoteServer "cd yourRemoteDir; ./yourRemoteScript.sh </dev/null >/dev/null 2>&1 & "
【讨论】:
【参考方案9】:你可以在没有 nohup 的情况下做到这一点:
ssh user@host 'myprogram >out.log 2>err.log &'
【讨论】:
这是一个被低估的答案,谢谢! 我建议对这个答案做出的唯一改变是</dev/null
,所以我们也不能阻止标准输入。【参考方案10】:
你可以这样做......
sudo /home/script.sh -opt1 > /tmp/script.out &
【讨论】:
完美,适用于 PuTTY SSH 会话。我可以退出,脚本在机器上继续。谢谢。【参考方案11】:实际上,每当我需要在远程机器上运行复杂的命令时,我喜欢将命令放在目标机器上的脚本中,然后使用 ssh 运行该脚本。
例如:
# simple_script.sh (located on remote server)
#!/bin/bash
cat /var/log/messages | grep <some value> | awk -F " " 'print $8'
然后我就在源机器上运行这个命令:
ssh user@ip "/path/to/simple_script.sh"
【讨论】:
【参考方案12】:使用tmux new -d <shell cmd>
语法进行远程 tmux 会话对我来说似乎很方便,如下所示:
ssh someone@elsewhere 'tmux new -d sleep 600'
这将在elsewhere
主机上启动新会话,并且本地机器上的 ssh 命令将几乎立即返回到 shell。然后,您可以通过 ssh 连接到远程主机并将 tmux attach
连接到该会话。请注意,没有关于本地 tmux 运行,只有远程!
此外,如果您希望会话在作业完成后继续存在,只需在命令后添加一个 shell 启动器,但不要忘记用引号引起来:
ssh someone@elsewhere 'tmux new -d "~/myscript.sh; bash"'
【讨论】:
【参考方案13】:我试图做同样的事情,但是我试图从 Java 中做这件事增加了复杂性。因此,在一台运行 java 的机器上,我试图在另一台机器上运行脚本,在后台(使用 nohup)。
从命令行,这里是有效的:(如果你不需要它来 ssh 到主机,你可能不需要“-i keyFile”)
ssh -i keyFile user@host bash -c "\"nohup ./script arg1 arg2 > output.txt 2>&1 &\""
请注意,在我的命令行中,“-c”后面有一个参数,全部用引号引起来。但要让它在另一端工作,它仍然需要引号,所以我必须在其中添加转义引号。
从 java 中,这是有效的:
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.
它需要一些试验和错误才能使其正常工作,但现在似乎运行良好。
【讨论】:
为什么-c
后面的参数中有文字引号?我不明白这怎么可能奏效。就像 bash 中的 ssh -i keyFile bash -c '"nohup ./script arg1 arg2 > output.txt 2>&1 &"'
一样,它也不起作用并且出于同样的原因(不正确的文字引号嵌套在必要和正确的语法引号中)。【参考方案14】:
YOUR-COMMAND &> YOUR-LOG.log &
这应该运行命令并分配一个进程 ID,您可以简单地使用 tail -f YOUR-LOG.log 来查看写入它的结果。您可以随时注销,该过程将继续进行
【讨论】:
&>your-log.log
仅在远程系统的 shell 是 bash (或者手头有扩展名)时才有效。如果可能是 sh,>your-log.log 2>&1
更好。【参考方案15】:
如果您使用的是 zsh,那么使用 program-to-execute &!
是一个 zsh 特定的快捷方式,既可以访问后台也可以取消进程,这样退出 shell 就会使其继续运行。
【讨论】:
【参考方案16】:首先遵循以下程序:
以用户 a 的身份登录 A 并生成一对身份验证密钥。不要输入密码:
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 上以用户 b 的身份创建一个目录 ~/.ssh。(该目录可能已经存在,这很好):
a@A:~> ssh b@B mkdir -p .ssh
b@B's password:
最后将 a 的新公钥附加到 b@B:.ssh/authorized_keys 并最后一次输入 b 的密码:
a@A:~> cat .ssh/id_rsa.pub | ssh b@B 'cat >> .ssh/authorized_keys'
b@B's password:
从现在开始,您可以从 A 以 b 身份登录 B,而无需密码:
a@A:~> ssh b@B
那么这将在不输入密码的情况下工作
ssh b@B "cd /some/directory; program-to-execute &"
【讨论】:
问题说 dagorym 已经将密钥设置为不需要密码,但它仍然挂起【参考方案17】:我认为这是您需要的:
首先你需要在你的机器上安装sshpass
。
那么你就可以编写自己的脚本了:
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
【讨论】:
以上是关于让 ssh 在目标机器的后台执行命令的主要内容,如果未能解决你的问题,请参考以下文章