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

我已经有一个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';"

因为它极其丑陋,难以阅读。

484583 次浏览

我认为有两种方法:

首先,像这样创建一个控制套接字:

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

运行你的命令

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

通过这种方式,您可以编写ssh命令,而不必实际重新连接到服务器。

第二种方法是动态生成脚本,scping并运行。

在本地编辑脚本,然后将其导入ssh。

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

其中commands-to-execute-remotely.sh看起来像上面的列表:

ls some_folder
./someaction.sh
pwd;

Bash 这里的文档怎么样:

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在上面),那么你不能做变量替换。但是不引用极限字符串,变量被替换了。例如,如果你在shell脚本中定义了上面的$NAME,你可以这样做

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

并且它会在目标otherhost上创建一个文件,该文件的名称是你分配给$NAME的任何东西。关于shell脚本引用的其他规则也适用,但太复杂了,这里不详细介绍。

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

ssh blah_server "
ls
pwd
"

把所有的命令放在一个脚本上,它可以像这样运行

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

也可以这样做。 把你的命令放在一个脚本中,让我们命名为commands-inc.sh

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

保存文件

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

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

我从来没有失败过。

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

这对于创建脚本很有效,因为你不需要包含其他文件:

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


# you can use '$(which bash) -s' instead of my "bash -s" as well
# but bash is usually being found in a standard location
# so for easier memorizing it i leave that out
# since i dont fat-finger my $PATH that bad so it cant even find /bin/bash ..

配置系统使用单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

对于任何像我一样在这里出错的人,我成功地逃脱了分号和换行符:

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

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

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

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)

使用多行字符串和多个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"

不确定是否是最干净的长命令,但肯定是最简单的:

ssh user@host "cmd1; cmd2; cmd3"

对于简单的命令,您可以使用:

ssh <ssh_args> command1 '&&' command2

ssh <ssh_args> command1 \&\& command2

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

我推荐使用这个转义函数。该函数接受一个参数——转义函数。然后sshqfunc输出函数的declare -f,然后输出一个字符串,该字符串将调用该函数,"$@"参数被正确引用。然后将整个"%q"加引号,并添加bash -c。如果远程没有bash,您可以将bash更改为sh。

sshqfunc() { echo "bash -c $(printf "%q" "$(declare -f "$@"); $1 \"\$@\"")"; };

然后定义一个函数,其中包含您想在远程上执行的工作。函数的定义是正常的,因此它将是适当的“干净”。您可以在本地测试这样的函数。定义之后,将正确转义的函数传递给远程。

work() {
ls
pwd
echo "Some other command"
}


ssh host@something "$(sshqfunc work)"

你也可以传递参数,它们将作为位置参数传递给你的函数。函数后的下一个参数将被赋值给$0——通常是像--_这样的占位符,用于将参数与调用分开。

work() {
file=$1
num=$2
ls "$file"
echo "num is $num"
}


ssh host@something "$(sshqfunc work)" -- /this/file 5

但请注意,如果有任何神奇的字符,参数也应该被正确引用:

ssh host@something "$(sshqfunc work)" -- "$(printf "%q" "$var1" "$var2")"