在Bash中ssh和运行多个命令的最干净方法是什么?


349

我已经设置了ssh代理,并且可以使用Bash脚本在外部服务器上运行命令,例如:

ssh blah_server "ls; pwd;"

现在,我真正想做的是在外部服务器上运行许多长命令。将所有这些用引号引起来是很丑陋的,我真的宁愿避免多次使用ssh来避免这种情况。

那么,有什么办法可以让我用括号括起来吗?我正在寻找以下方面的东西:

ssh blah_server (
   ls some_folder;
   ./someaction.sh;
   pwd;
)

基本上,只要它是干净的,我都会对任何解决方案感到满意。

编辑

为了澄清起见,我说的是这是一个更大的bash脚本的一部分。其他人可能需要处理脚本,因此我想保持脚本干净。我不想让bash脚本的一行看起来像这样:

ssh blah_server "ls some_folder; ./someaction.sh 'some params'; pwd; ./some_other_action 'other params';"

因为它非常难看且难以阅读。


2
嗯,将所有这些内容放到服务器上的脚本中,然后只调用一次就可以了ssh吗?
Nikolai Fetissov

@尼古拉如果命令依赖于客户端,可以将它们写入一个shell脚本,然后scpssh和运行。我认为这将是最干净的方法。
khachik 2010年

6
这是一个更大的bash脚本的一部分,因此我不希望将其拆分成一半放在我的个人计算机上,另一半放在服务器上并通过ssh运行。如果可能的话,我真的很想保留它,因为它是从我的个人计算机运行的一个脚本。真的没有干净的方法将一堆命令包含在ssh中吗?
伊莱(Eli)2010年

最好的办法是不使用bash的,但Perl,Python和Ruby的,等等
萨尔瓦

1
为什么要避免将远程命令放在引号中?您可以根据需要在引号内包含换行符;使用字符串代替标准输入意味着可以使用标准输入,例如读取对远程脚本的输入。(尽管在Unix上,单引号通常比双引号更可取,除非您特别需要本地shell来评估字符串的某些部分。)
Tripleee 2015年

Answers:


456

Bash Here文档如何:

ssh otherhost << EOF
  ls some_folder; 
  ./someaction.sh 'some params'
  pwd
  ./some_other_action 'other params'
EOF

为了避免@Globalz在评论中提到的问题,您可以(根据您在远程站点上所做的事情)摆脱掉用第一行代替

ssh otherhost /bin/bash << EOF

请注意,您可以在Here文档中进行变量替换,但是您可能必须处理引用问题。例如,如果您引用“限制字符串”(即EOF上述内容),则无法进行变量替换。但是不引用限制字符串,而是替换变量。例如,如果您已经$NAME在shell脚本中定义了以上内容,则可以

ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF

它将在目标位置创建一个文件otherhost,其名称为您分配的名称$NAME。有关shell脚本引用的其他规则也适用,但是太复杂了,无法在此处介绍。


6
这看起来正是我想要的!如何运作?您是否会找到指向说明页面的链接?
伊莱(Eli)2010年

9
+1只是以为自己是一个人-这是一个可以阅读的地方:tldp.org/LDP/abs/html/here-docs.html
bosmacs

14
我也将此输出发送到本地:不会分配伪终端,因为stdin不是终端。
Globalz 2012年

14
为您引用这可能是重要的,以防止命令行的扩张(即第一个“EOF”)。另请:man bash | 更少+ /'这里文档'
汇编

6
Paul,我只建议这样做,因为在这个问题上有将近200,000的意见,当使用ssh编写脚本时,似乎很多人都来了。编写脚本时通常需要注入值。如果不是因为其他问题和评论中的喧闹声,我只会提出一个问题并继续前进,但在现阶段不太可能看到。单行脚注可能会减轻人们的头疼。
koyae

122

在本地编辑脚本,然后将其通过管道传输到ssh中,例如

cat commands-to-execute-remotely.sh | ssh blah_server

这里commands-to-execute-remotely.sh看起来像你的列表上方:

ls some_folder
./someaction.sh
pwd;

7
这具有很大的优点,即您可以确切地知道远程脚本正在执行什么操作,而引用没有问题。如果您需要动态命令,则可以使用带有子shell的shell脚本,并且仍在ssh中进行管道( echo $mycmd $myvar ; ...) | ssh myhost传输,即-与cat用法一样,您确切知道ssh命令流中的内容。当然,脚本中的subshel​​l可以是多行的,以提高可读性-参见linuxjournal.com/content/bash-sub-shells
RichVel 2013年

6
您可以使用中的参数来执行此操作commands-to-execute-remotely.sh吗?
elaRosca 2014年

1
我认为这比Pombomblin解决方案有用得多。由于脚本可以重定向到任何ssh服务器,而无需打开文件。此外,它的可读性也更高。
Patrick Bassut 2014年

3
是的,但是使用echo或here文档(请参见顶部答案):使用:$localvar解释本地定义的变量,\$remotevar远程解释远程定义的变量,\$(something with optionnal args)以获取在远程服务器上执行的内容的输出。一个示例,您可以ssh到1(或如此处所示,使用多个ssh命令):echo " for remotedir in /*/${localprefix}* ; do cd \"\$remotedir\" && echo \"I am now in \$(pwd) on the remote server \$(hostname) \" ; done " | ssh user1@hop1 ssh user2@hop2 ssh user@finalserver bash
Olivier Dulac

2
嗯...这与使用输入重定向ssh blah_server < commands-to-execute-remotely.sh有何不同?
flow2k

41

为了匹配示例代码,您可以将命令包装在单或双qoutes中。例如

ssh blah_server "
  ls
  pwd
"

我喜欢这种格式,但是遗憾的是,它对于将std数据存储到变量中没有用。
Signus

1
Signus,“将标准数据存储到变量中”是什么意思?
Andrei B

1
@Signus完全可以按照您的描述进行操作,尽管您可能希望在远程命令周围使用单引号而不是双引号(或转义需要在双引号内转义的运算符,以防止您的本地shell拦截和插值)。
2015年

我无法在双引号中放入类似fileToRemove = $(find。-type f -name'xxx.war')的命令。fileToRemove里面应该有一个文件名,但是它有一个空字符串。是否需要逃脱?
jkonst,

37

我看到两种方式:

首先,您要制作一个控制套接字,如下所示:

 ssh -oControlMaster=yes -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip>

并运行您的命令

 ssh -oControlMaster=no -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip> -t <yourcommand>

这样,您可以编写ssh命令,而无需实际重新连接到服务器。

第二个是动态生成脚本,scp并开始运行。


20

这也可以如下进行。将您的命令放在脚本中,我们命名为commands-inc.sh

#!/bin/bash
ls some_folder
./someaction.sh
pwd

保存文件

现在,在远程服务器上运行它。

ssh user@remote 'bash -s' < /path/to/commands-inc.sh

从来没有为我失败。


类似于我最初的想法!但是为什么bash -s需要呢?
flow2k

另外,#!/bin/bash真的用过吗?
flow2k

2
-s是为了兼容性。From man bash -s如果存在-s选项,或者在选项处理后没有剩余参数,则将从标准输入中读取命令。此选项允许在调用交互式外壳程序时设置位置参数。
RJ

1
RJ,谢谢!在我的第一条评论中,看起来我们就这样做了ssh user@remote < /path/to/commands-inc.sh(它似乎对我有用)。好吧,我想您的版本可以确保我们使用bash外壳程序,而不是其他外壳程序,这是目的,不是吗?
flow2k

2
谢谢。是的,我们当中有些人有我们的笔记,还有旧版本的bash和其他shell的习惯。:-)
RJ

10

将所有命令放在脚本上,它可以像

ssh <remote-user>@<remote-host> "bash -s" <./remote-commands.sh

有点奇怪,但效果很好。可以将args传递给脚本(在重定向之前或之后)。例如'ssh <远程用户> @ <远程主机>“ bash -s” arg1 <./ remote-commands.sh arg2'例如'ssh <远程用户> @ <远程主机>“ bash -s”- - -arg1 <./ remote-commands.sh ARG2'
gaoithe

我在上面的另一个答案中也遇到了类似的问题,但是包含的目的是bash -s什么?
flow2k

1
@ flow2k -s指示bash从标准输入读取命令。这是来自man页面上If the -s option is present, or if no arguments remain after option processing, then commands are read from the standard input. This option allows the positional parameters to be set when invoking an interactive shell.
Jai Prakash

@JaiPrakash感谢您,但是我实际上是在思考是否可以完全取消该bash命令,即ssh remote-user@remote-host <./remote-commands.sh。我尝试了一下,它似乎起作用了,尽管我不确定它是否存在某种缺陷。
flow2k

根据我对手册页的了解,@ flow2k没有该选项就不会有任何不足。如果您尝试传递任何参数,则需要它。-谢谢
Jai Prakash

9

SSH并在Bash中运行多个命令。

字符串中用分号分隔的命令传递给echo,所有命令均通过ssh命令传递。例如:

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

arnab,请在将来简要介绍您的代码。然后讨论它是如何工作的,然后放入代码中。然后将代码放在代码块中,以便于阅读。
埃里克·莱斯钦斯基

从问题中,您确切地提供了OP想要避免的事情
jww

6

对于像我一样在这里绊脚的任何人-我成功地转义了分号和换行符:

第一步:分号。这样,我们不会破坏ssh命令:

ssh <host> echo test\;ls
                    ^ backslash!

列出了远程主机/ home目录(以root用户身份登录),而

ssh <host> echo test;ls
                    ^ NO backslash

列出当前工作目录。

下一步:分线:

                      v another backslash!
ssh <host> echo test\;\
ls

这再次列出了远程工作目录-改进了格式:

ssh <host>\
  echo test\;\
  ls

如果真的比这里的文档好,或者用引号引起来的话-那么,不是我决定...

(使用bash,Ubuntu 14.04 LTS。)


1
更好的是您可以使用&&和|| 这样-回声测试\&\&ls
Miro Kropacek '19

@MiroKropacek也很好。更好?取决于你想要什么;有条件地运行第二个命令,然后是,无条件运行(无论第一个命令成功还是失败),然后就不...
阿空加瓜

@MiroKropacek,在命令ssh host "echo test && ls"
两边加上

5

使用多行字符串和多个bash脚本发布的答案对我不起作用。

  • 长的多行字符串很难维护。
  • 单独的bash脚本不维护局部变量。

这是在保持本地上下文的同时ssh和运行多个命令的一种实用方法。

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"

3
您正在考虑这个问题,好像某人想要这样做的唯一原因就是他们坐在命令行中。有此问题的其他常见原因还有,如用工具,如rundeck,詹金斯等在分布式环境中执行命令
查尔斯亚的斯亚贝巴

可行,


4

这对于创建脚本非常有效,因为您不必包括其他文件:

#!/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

“ $(which bash)-s”部分为您提供了bash在本地计算机而不是远程计算机上的位置。我认为您想使用“ $(哪一个bash)-s”(单引号来禁止局部参数替换)。
jsears

1
Paul Tomblin于 6年前提供了Bash Here文档的答案。这个答案增加了什么价值?
jww

-s标志,我引用他,您也许可以用...代替第一行,而我只是隐约记得打电话给bash就无法摆脱。
sjas 16-10-25

4

默认情况下,通过复用将系统配置为使用单个ssh会话的最简单方法。

这可以通过为套接字创建一个文件夹来完成:

mkdir ~/.ssh/controlmasters

然后将以下内容添加到您的.ssh配置中:

Host *
    ControlMaster auto
    ControlPath ~/.ssh/controlmasters/%r@%h:%p.socket
    ControlMaster auto
    ControlPersist 10m

现在,您无需修改​​任何代码。这样可以在不创建多个会话的情况下多次调用ssh和scp,这在本地计算机与远程计算机之间需要更多交互时非常有用。

感谢@terminus的回答,http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/https://en.wikibooks.org / wiki / OpenSSH / Cookbook / Multiplexing

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.