让 ssh 在目标机器上后台执行命令

发布于 2024-07-04 14:12:51 字数 395 浏览 8 评论 0原文

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

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

随风而去 2024-07-11 14:12:52

这应该可以解决您的问题:

nohup myprogram > foo.log 2> foo.err < /dev/null &

< 的语法和异常使用 /dev/null这个答案中解释得特别好,为了方便起见,在这里引用。

<代码>< /dev/null 用于立即向程序发送EOF,以便
不等待输入(/dev/null,空设备,是一个特殊文件
丢弃所有写入其中的数据,但报告写入
操作成功,并且不向任何读取的进程提供任何数据
从中,立即产生 EOF)。

所以命令:

nohup myscript.sh >myscript.log 2>&1 

将移动到后台命令,输出 stdout 和
stderr 到 myscript.log,无需等待任何输入。


另请参阅 nohup 上的维基百科文章,为方便起见,也在此引用。

Nohuping 后台作业适用于
通过 SSH 登录时有用的示例,
因为后台作业可能会导致
shell 由于竞争而挂在注销状态
健康)状况。 这个问题还可以
通过重定向所有三个来克服
I/O 流。

This should solve your problem:

nohup myprogram > foo.log 2> foo.err < /dev/null &

The syntax and unusual use of < /dev/null are explained especially well in this answer, quoted here for your convenience.

< /dev/null is used to instantly send EOF to the program, so that it
doesn't wait for input (/dev/null, the null device, is a special file
that discards all data written to it, but reports that the write
operation succeeded, and provides no data to any process that reads
from it, yielding EOF immediately).

So the command:

nohup myscript.sh >myscript.log 2>&1 </dev/null &
#\__/             \___________/ \__/ \________/ ^
# |                    |          |      |      |
# |                    |          |      |  run in background
# |                    |          |      |
# |                    |          |   don't expect input
# |                    |          |   
# |                    |        redirect stderr to stdout
# |                    |           
# |                    redirect stdout to myscript.log
# |
# keep the command running 
# no matter whether the connection is lost or you logout

will move to background the command, outputting both stdout and
stderr to myscript.log without waiting for any input.


See also the wikipedia artcle on nohup, also quoted 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. This problem can also
be overcome by redirecting all three
I/O streams.

心意如水 2024-07-11 14:12:52

如果您不/无法保持连接打开,您可以使用 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 &

分离 screen 会话: ctrl-a d

列出 screen 会话:

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 配置,您可以尝试。

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.

懒的傷心 2024-07-11 14:12:52

重定向 fd 的

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

括号

最好的方法是使用 sh -c '( ( command ) & )' 其中 command 可以是任何内容。

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

Nohup Shell

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

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

Nice Launch

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

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

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 ) & )"'
蘸点软妹酱 2024-07-11 14:12:52

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

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

在此之后运行的唯一事情是远程计算机上的实际命令

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

稍尽春風 2024-07-11 14:12:52

我认为这就是你所需要的:
首先,您需要在计算机上安装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

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
只涨不跌 2024-07-11 14:12:52

首先按照以下步骤操作:

以用户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 & ;”

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 &"

鹤舞 2024-07-11 14:12:52

@cmcginty 的简洁工作示例的后续示例,该示例还展示了如何将外部命令用双引号括起来。 这是从 PowerShell 脚本中调用时模板的外观(该脚本只能从双引号内插入变量,并在用单引号括起来时忽略任何变量扩展):

ssh user@server "sh -c `"($cmd) &>/dev/null </dev/null &`""

内部双引号被转义用反勾号代替反斜杠。 这允许 $cmdPowerShell 脚本组成,例如用于部署脚本和自动化等。 如果与 unix LF 组合,$cmd 甚至可以包含多行 heredoc

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.

春风十里 2024-07-11 14:12:52
YOUR-COMMAND &> YOUR-LOG.log &    

这应该运行命令并分配一个进程 ID,您可以简单地 tail -f YOUR-LOG.log 来查看写入的结果。 您可以随时注销,该过程将继续

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

欲拥i 2024-07-11 14:12:52

如果您使用的是 zsh,则使用 program-to-execute &! 是 zsh 特定的快捷方式,可通往后台并拒绝进程,这样退出 shell 就会使其继续运行。

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√萤火虫的光℡ 2024-07-11 14:12:52

实际上,每当我需要在远程计算机上运行一个复杂的命令时,我喜欢将该命令放入目标计算机上的脚本中,然后使用 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"

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"
花期渐远 2024-07-11 14:12:52

我试图做同样的事情,但由于我试图通过 Java 来完成它,所以增加了复杂性。 因此,在一台运行java的机器上,我试图在另一台机器上在后台运行脚本(使用nohup)。

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

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.

它需要一些尝试& 让它工作时出错,但现在似乎工作得很好。

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.

吾性傲以野 2024-07-11 14:12:52
  1. 如果您在未分配 tty 的情况下运行远程命令,则重定向 stdout/stderr 有效,nohup 不是必需的。

    ssh user@host '后台命令 &>/dev/null &'

  2. 如果使用-t分配tty与后台一起运行交互式命令命令,后台命令是最后一个命令,如下所示:

    ssh -t user@host 'bash -c "交互式命令; nohup 后台命令 &>/dev/null &"'

    后台命令可能实际上并未启动。 这里有比赛:

    1. bashnohup 启动后退出。 作为会话领导者,bash 退出会导致 HUP 信号发送至 nohup 进程。
    2. nohup 忽略 HUP 信号。

    如果12之前完成,nohup进程将退出并且不会启动后台命令根本不。 我们需要等待nohup启动后台命令。 一个简单的解决方法是添加一个 sleep

    ssh -t user@host 'bash -c "交互式命令; nohup 后台命令 &>/dev/null & sleep 1"'

这个问题是几年前提出并回答的,我不知道从那时起 openssh 的行为是否发生了变化。 我正在测试:
OpenSSH_8.6p1,OpenSSL 1.1.1g FIPS 2020 年 4 月 21 日

  1. If you run remote command without allocating tty, redirect stdout/stderr works, nohup is not necessary.

    ssh user@host 'background command &>/dev/null &'

  2. 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:

    1. bash exits after nohup starts. As a session leader, bash exit results in HUP signal sent to nohup process.
    2. 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

划一舟意中人 2024-07-11 14:12:52

使用 tmux new -d 语法进行远程 tmux 会话对我来说似乎非常方便,如下所示:

ssh someone@elsewhere 'tmux new -d sleep 600'

这将在 上启动新会话其他地方 本地计算机上的主机和 ssh 命令几乎会立即返回 shell。 然后,您可以 ssh 到远程主机并将 tmux Attach 到该会话。 请注意,本地 tmux 运行没有任何内容,只有远程运行!

另外,如果您希望会话在工作完成后持续存在,只需在命令后添加 shell 启动器,但不要忘记用引号引起来:

ssh someone@elsewhere 'tmux new -d "~/myscript.sh; bash"'

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"'
神回复 2024-07-11 14:12:52

你可以这样做...

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

You can do it like this...

sudo /home/script.sh -opt1 > /tmp/script.out &
魂ガ小子 2024-07-11 14:12:52

这对我来说可能多次有效:

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

This worked for me may times:

ssh -x remoteServer "cd yourRemoteDir; ./yourRemoteScript.sh </dev/null >/dev/null 2>&1 & " 
森罗 2024-07-11 14:12:52

最快、最简单的方法是使用“at”命令:

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

Quickest and easiest way is to use the 'at' command:

ssh user@target "at now -f /home/foo.sh"
蓝戈者 2024-07-11 14:12:52

我认为你必须结合这些答案才能得到你想要的。 如果您将 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。

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.

玩物 2024-07-11 14:12:52

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

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

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 &\""
俏︾媚 2024-07-11 14:12:52

您可以在不使用 nohup 的情况下执行此操作:

ssh user@host 'myprogram >out.log 2>err.log &'

You can do this without nohup:

ssh user@host 'myprogram >out.log 2>err.log &'
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文