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

What is the cleanest way to ssh and run multiple commands in Bash?

我已经设置了一个ssh代理,我可以在bash脚本的外部服务器上运行命令,执行如下操作:

1
ssh blah_server"ls; pwd;"

现在,我真正想做的是在外部服务器上运行大量的长命令。把所有这些都放在引号之间是很难看的,我真的希望避免多次使用ssh'ing来避免这种情况。

那么,有没有一种方法可以做到这一点,用括号或其他什么东西括起来?我在找一些符合以下条件的东西:

1
2
3
4
5
ssh blah_server (
   ls some_folder;
   ./someaction.sh;
   pwd;
)

基本上,只要解决方案是干净的,我都会满意的。

编辑

为了澄清这一点,我说这是一个更大的bash脚本的一部分。其他人可能需要直接处理这个脚本,所以我想保持它的整洁。我不想让一个bash脚本有一行看起来像:

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

因为它非常难看,很难阅读。


这里的bash文档如何:

1
2
3
4
5
6
ssh otherhost << EOF
  ls some_folder;
  ./someaction.sh 'some params'
  pwd
  ./some_other_action 'other params'
EOF

为了避免@globalz在评论中提到的问题,您可以(取决于您在远程站点上所做的)将第一行替换为

1
ssh otherhost /bin/bash << EOF

注意,您可以在这里的文档中进行变量替换,但是您可能需要处理引用问题。例如,如果您引用"限制字符串"(即上面的EOF),那么就不能进行变量替换。但在不引用极限字符串的情况下,变量被替换。例如,如果您在shell脚本中定义了上面的$NAME,则可以这样做。

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

它将在目的地otherhost上创建一个文件,文件名为您分配给$NAME的任何文件。关于shell脚本引用的其他规则也同样适用,但在这里太复杂了。


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

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

其中EDOCX1[1]与上面的列表类似:

1
2
3
ls some_folder
./someaction.sh
pwd;


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

1
2
3
4
ssh blah_server"
  ls
  pwd
"


我看到两种方式:

首先你要做一个这样的控制插座:

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

运行你的命令

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

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

第二步是动态生成脚本,scp运行它。


这也可以按如下方式进行。把你的命令放在一个脚本里,让我们把它命名为commands-inc.sh。

1
2
3
4
#!/bin/bash
ls some_folder
./someaction.sh
pwd

保存文件

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

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

我从不失败。


ssh并在bash中运行多个命令。

在字符串中用分号分隔命令,传递给echo,所有命令都通过管道传输到ssh命令。例如:

1
2
3
4
5
6
7
8
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


把所有的命令放到一个脚本上,它可以像

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


对于像我这样在这里跌跌撞撞的人,我成功地摆脱了分号和换行符:

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

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

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

1
2
ssh <host> echo test;ls
                    ^ NO backslash

列出了当前工作目录。

下一步:拆线:

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

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

1
2
3
ssh <host>\
  echo test\;\
  ls

如果真的比这里更好的文件或断行引用-好吧,不是我来决定…

(使用bash,Ubuntu 14.04 LTS。)


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

1
2
3
4
5
6
7
#!/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


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

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

这里是一种在保持本地上下文的情况下进行ssh和运行多个命令的功能性方法。

1
2
3
4
5
6
7
8
9
10
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会话的最简单方法。

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

1
mkdir ~/.ssh/controlmasters

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

1
2
3
4
5
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/和http s://en.wikibooks.org/wiki/openssh/cookbook/multiplexing。


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

1
ssh user@host"cmd1; cmd2; cmd;3"