如何使用SSH在远程计算机上运行Shell脚本?


1220

我必须在远程计算机上运行Shell脚本(Windows / Linux)。

我在机器A和B上都配置了SSH。我的脚本在机器A上,它将在远程机器B上运行我的一些代码。

本地和远程计算机可以是Windows或Unix系统。

有没有办法使用plink / ssh来执行此操作?


6
相同的问题已经在serverfault上:serverfault.com/questions/215756/…因此,迁移这个问题可能没有意义。
sleske 2012年

9
关于服务器故障的问题虽然没有那么多答案。也许这个问题应该代替那个问题。
Big McLargeHuge 2013年

5
我很喜欢这个答案个人:unix.stackexchange.com/questions/87405/...
mikevoermans

27
此外,它显然应该成为主题,因为ssh是软件开发的主要工具。
static_rtti 2015年

4
Coffee和ssh问题对SO的异同程度不同。投票要求重新开放。
文森特·坎廷

Answers:


1191

如果计算机A是Windows机器,则可以将Plink(PuTTY的一部分)与-m参数一起使用,它将在远程服务器上执行本地脚本。

plink root@MachineB -m local_script.sh

如果计算机A是基于Unix的系统,则可以使用:

ssh root@MachineB 'bash -s' < local_script.sh

您无需将脚本复制到远程服务器即可运行它。


11
使用该-s选件有优势吗? 该手册页使我相信,完成处理选项(无论是否-s使用)后,它将处理标准输入。
aeroNotAuto11年

78
对于需要的脚本sudo,请运行ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh
bradley.ayers 2012年

6
@ bradley.ayers记得以一个“空格”开始命令以跳过历史记录(PS必须HISTCONTROL=ignoreboth or ignorespace使之
生效

8
@ bradley.ayers在什么情况下,如果您已经以root用户身份登录,则需要sudo?
Brian Schlenker 2014年

21
@Agostino,您可以添加这样的参数: ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh 积分将完全转到@chubbsondubs的以下答案。
Yves Van Broekhoven 2014年

632

这是一个古老的问题,Jason的答案很好,但是我想补充一下:

ssh user@host <<'ENDSSH'
#commands to run on remote host
ENDSSH

这也可以与需要用户输入的su和命令一起使用。(请注意'转义的heredoc)

编辑:由于此答案不断获得一些流量,我将添加更多信息到heredoc的这种出色用法:

您可以使用此语法嵌套命令,这就是嵌套似乎起作用的唯一方式(以合理的方式)

ssh user@host <<'ENDSSH'
#commands to run on remote host
ssh user@host2 <<'END2'
# Another bunch of commands on another host
wall <<'ENDWALL'
Error: Out of cheese
ENDWALL
ftp ftp.secureftp-test.com <<'ENDFTP'
test
test
ls
ENDFTP
END2
ENDSSH

您实际上可以与某些服务(例如telnet,f​​tp等)进行对话。但是请记住,heredoc只是将stdin作为文本发送,它不等待行之间的响应

编辑:我刚刚发现,如果使用,您可以缩进制表符的内部<<-END

ssh user@host <<-'ENDSSH'
    #commands to run on remote host
    ssh user@host2 <<-'END2'
        # Another bunch of commands on another host
        wall <<-'ENDWALL'
            Error: Out of cheese
        ENDWALL
        ftp ftp.secureftp-test.com <<-'ENDFTP'
            test
            test
            ls
        ENDFTP
    END2
ENDSSH

(我认为这应该有效)

另请参阅 http://tldp.org/LDP/abs/html/here-docs.html


4
您可以通过添加以下行来进行临时调整:#$(sleep 5)
Olivier Dulac

50
请注意,如果在终止符(<<'ENDSSH')周围加上单引号,则不会扩展字符串,也不会评估变量。您也可以使用<<ENDSSH<<"ENDSSH"如果要扩展。
maackle

3
Expect当您需要自动执行交互式命令(例如FTP)时,可以使用。
programaths

5
请注意,我有一条Pseudo-terminal will not be allocated because stdin is not a terminal.消息。必须使用带-t -t参数的ssh 来避免这种情况。在SO
Buzut '16

8
如果您尝试使用<<-'END'语法,请确保使用TAB(而不是空格)缩进您的heredoc结束定界符。请注意,从stackexchange复制/粘贴将为您提供空间。将其更改为选项卡,缩进功能应起作用。
fbicknel

249

另外,如果要从目标主机中提取变量,请不要忘记对变量进行转义。

过去,这让我感到困惑。

例如:

user@host> ssh user2@host2 "echo \$HOME"

打印出/ home / user2

user@host> ssh user2@host2 "echo $HOME"

打印出/ home / user

另一个例子:

user@host> ssh user2@host2 "echo hello world | awk '{print \$1}'"

正确打印出“ hello”。


2
但是请注意以下几点: ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info 2013年

1
仅添加forssh会话中运行的in 循环中,不得对loop变量进行转义。
2014年

1
在许多情况下,修复上一个示例的明智方法是ssh user2@host2 'echo hello world' | awk '{ print $1 }'在本地运行Awk脚本。如果远程命令产生了大量的输出,那么您当然要避免将其全部复制回本地服务器。顺便说一句,远程命令周围的单引号避免了任何转义。
2015年

151

这是对YarekT答案的扩展,它结合了嵌入式远程命令和将ENV变量从本地计算机传递到远程主机的功能,因此您可以在远程端参数化脚本:

ssh user@host ARG1=$ARG1 ARG2=$ARG2 'bash -s' <<'ENDSSH'
  # commands to run on remote host
  echo $ARG1 $ARG2
ENDSSH

我发现将它们全部保存在一个脚本中非常有帮助,因此它非常易读和可维护。

为什么这样做。ssh支持以下语法:

ssh user @ host remote_command

在bash中,我们可以指定环境变量以在一行上运行命令之前定义,如下所示:

ENV_VAR_1 ='value1'ENV_VAR_2 ='value2'bash -c'echo $ ENV_VAR_1 $ ENV_VAR_2'

这使得在运行命令之前定义变量变得容易。在这种情况下,echo是我们正在运行的命令。echo之前的所有内容都定义了环境变量。

因此,我们将这两个功能与YarekT的答案结合起来,得到:

ssh user @ host ARG1 = $ ARG1 ARG2 = $ ARG2'bash -s'<<'ENDSSH'...

在这种情况下,我们将ARG1和ARG2设置为本地值。在user @ host之后将所有内容作为remote_command发送。当远程计算机执行命令ARG1和ARG2时,设置了本地值,这要归功于本地命令行评估,该评估在远程服务器上定义了环境变量,然后使用这些变量执行bash -s命令。瞧


1
请注意,如果要传递-a这样的args,则可以使用-。例如'ssh user @ host--a foo bar'bash -s'<script.sh'。args也可以执行重定向,例如'ssh user @ host'bash -s'<script.sh--a foo bar'。
gaoithe

8
如果任何env var值包含空格,请使用:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle

就像我在另一条评论中所写的那样,使用的重点-s是能够将参数应用于通过stdin生成的脚本。我的意思是,如果您不打算使用它,也可以忽略它。如果您确实使用它,则没有理由使用环境变量:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JoL

104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

这将提示您输入密码,除非您已将hostA用户的公共密钥复制到用户.ssh目录的目录中的authorized_keys文件中。这将允许无密码身份验证(如果在ssh服务器的配置中被接受为auth方法)


3
投票给你。这是一个有效的解决方案。显然,密钥必须受到保护,但也可以像通过服务器端的密码一样使密钥失效。
willasaywhat

8
我认为这不能回答问题。该示例显示了如何运行远程命令,而不显示如何在远程计算机上执行本地脚本。
杰森·库姆斯

不确定,但是您不能使用这种方法通过管道将hostA上的脚本运行在hostB上吗?
nevets1219 2010年

27

我已经开始使用Fabric进行更复杂的操作。Fabric需要Python和其他几个依赖项,但仅在客户端计算机上。该服务器仅需要是ssh服务器。我发现此工具比传递给SSH的shell脚本更强大,并且值得进行设置(特别是如果您喜欢使用Python编程)。Fabric处理多个主机(或具有某些角色的主机)上正在运行的脚本,帮助进行幂等操作(例如,将行添加到配置脚本中,但如果已经存在则不行),并允许构造更复杂的逻辑(例如Python)语言可以提供)。


11

尝试跑步ssh user@remote sh ./script.unx


8
仅当脚本位于远程服务器的默认(主)目录中时,此方法才有效。我认为问题是如何运行远程存储在本地的脚本。
metasim

1
ssh username @ ip“ chmod + x script.sh” <br/> ssh username @ ip“远程主机中sh文件的路径”
mani deepak 2014年



5

我使用此脚本在远程计算机上运行shell脚本(在/ bin / bash上测试):

ssh deploy@host . /home/deploy/path/to/script.sh

3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

强烈建议您获取环境文件(.bashrc / .bashprofile / .profile)。在远程主机中运行某些命令之前,因为目标主机和源主机的环境变量可能会有所不同。


这没有说明如何将本地脚本移动到远程主机。
kirelagin

2

如果您想执行 temp=`ls -a` echo $temp ``中的此 命令,将导致错误。

下面的命令将解决这个问题 ssh user@host ''' temp=`ls -a` echo $temp '''


1

如果您尝试使用或在远程Linux计算机上运行脚本,则此处的答案(https://stackoverflow.com/a/2732991/4752883)非常有用。如果脚本上有多行,它将起作用。plinksshlinux

**但是,如果您尝试运行位于本地linux/windows计算机上的批处理脚本,而您的远程计算机是Windows,并且该脚本 由多行组成,请使用**

plink root@MachineB -m local_script.bat

不会工作。

仅脚本的第一行将被执行。这可能是的限制plink

解决方案1:

要运行多行批处理脚本(尤其是如果它相对简单一些,则由几行组成):

如果您的原始批处理脚本如下

cd C:\Users\ipython_user\Desktop 
python filename.py

您可以使用文件中的“ &&”分隔符将这些行组合在一起,如下所示 local_script.bathttps : //stackoverflow.com/a/8055390/4752883

cd C:\Users\ipython_user\Desktop && python filename.py

进行此更改后,您可以按照@ JasonR.Coombs此处指出的那样运行脚本:https : //stackoverflow.com/a/2732991/4752883,其中:

`plink root@MachineB -m local_script.bat`

解决方案2:

如果您的批处理脚本相对复杂,则最好使用封装plink命令的批处理脚本以及@Martin https://stackoverflow.com/a/32196999/4752883在此处指出的以下命令:

rem Open tunnel in the background
start plink.exe -ssh [username]@[hostname] -L 3307:127.0.0.1:3306 -i "[SSH
key]" -N

rem Wait a second to let Plink establish the tunnel 
timeout /t 1

rem Run the task using the tunnel
"C:\Program Files\R\R-3.2.1\bin\x64\R.exe" CMD BATCH qidash.R

rem Kill the tunnel
taskkill /im plink.exe

1

此bash脚本确实将ssh放入目标远程计算机,并在远程计算机上运行一些命令,在运行它之前不要忘记安装Expect(在Mac上brew install expect

#!/usr/bin/expect
set username "enterusenamehere"
set password "enterpasswordhere"
set hosts "enteripaddressofhosthere"
spawn ssh  $username@$hosts
expect "$username@$hosts's password:"
send -- "$password\n"
expect "$"
send -- "somecommand on target remote machine here\n"
sleep 5
expect "$"
send -- "exit\n"

1
如果您必须使用密码,这非常好...但是,为了让任何人在家看电视,任何ssh命令都应使用一对公钥+私钥,而不要使用密码...一旦就位,请更新ssh服务器以完全关闭密码
Scott Stensland

-1

您可以使用runoverssh

sudo apt install runoverssh
runoverssh -s localscript.sh user host1 host2 host3...

-s 远程运行本地脚本


有用的标志:
-g对所有主机使用全局密码(单个密码提示),
-n使用SSH代替sshpass,对公共密钥身份验证很有用


-24

首先,使用scp将脚本复制到计算机B

[user @ machineA] $ scp / path / to / script user @ machineB:/ home / user / path

然后,只需运行脚本

[user @ machineA] $ ssh user @ machineB“ / home / user / path / script”

如果您已授予脚本可执行权限,则此方法有效。


嗨,我应用了建议的建议,但它给了我以下错误[oracle @ node1〜] $ ssh oracle @ node2:./ home / oracle / au / fs / conn.sh ssh:node2:./ home / oracle / au / fs / conn.sh:名称或服务未知[oracle @ node1〜] $

'ssh oracle @ node2:./ home / oracle / au / fs / conn.sh'?错误的命令行,命令名称应与用户名@主机部分隔开,而不是冒号。
bortzmeyer

5
我对此表示反对,因为它的主要主张是如果不复制就无法运行。
杰森·库姆斯

Jason补充说,为什么这是不正确的,而不是简单地陈述事实,这将是有帮助的。没用
克里斯(Kris)2013年

[user @ machineA] $ ssh root @ MachineB'bash -s'</ machinea / path / to / script
Oleksii Kyslytsyn
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.