我从Solaris文档获得了这个技巧,用于将ssh
公钥复制到远程主机(在Solaris上不提供ssh-copy-id
)。
$ cat some_data_file | ssh user@host "cat >/tmp/some_data_file; some_shell_cmd"
似乎它可以适应更多涉及的事情。具体来说,我希望some_shell_command
是从本地发送到远程端执行的脚本,它将与本地键盘交互。
我尝试了从多个来源通过stdin
发送多个东西的方法。但是在本地shell中工作的一些东西不适用于ssh,而下面的一些东西并没有完全符合我的要求:
$ echo "abc" | cat <(echo "def") # echoes: def (I wanted abc\ndef)
$ echo "abc" | cat < <(echo "def") # echoes: def (I wanted abc\ndef)
$ echo "abc" | cat <<-EOF
> echo $(</dev/stdin) #echoes: echo abc (I wanted: abc)
> EOF
# messed with eval for the above but that was a problem too.
@chepner总结说,在一个ssh
命令中完成所有这一切是不可行的,并解释了一个替代方案,对我来说无法研究和调整他的答案。 (我记录了这个帖子中的结果)。没有它,必须运行多个ssh
,并且默认情况下scp
命令需要多次提示输入密码,这是一个主要阻力,因为我不能指望我在多用户环境中编写的脚本的所有用户都要配置公钥授权,他们将不得不一遍又一遍地输入密码。
即使在使用早期版本的OpenSSH时,此解决方案仍然有效
ControlPersist
option不可用。 (本回答结束时的工作bash示例)
注意:OpenSSH 3.9在“控制主连接”(2005年)上引入了会话多路复用,但是,ControlPersist
选项直到OpenSSH 5.6(2010年发布)才被引入。
ssh会话多路复用允许脚本进行一次身份验证,然后通过经过身份验证的连接执行多个ssh事务。例如,如果您有一个使用ssh
,scp
或sftp
运行多个不同任务的脚本,则可以通过引用文件系统中其命名套接字的位置,在OpenSSH
'控制主会话'上执行每个事务。在运行必须执行多个ssh操作的脚本时,此密码身份验证方案非常有用,如果公钥验证不可用或无法依赖用户配置它。
我见过的大多数当前解决方案都需要使用ControlPersist
告诉ssh
保持控制主连接打开,无论是无限期还是特定的秒数。但是具有OpenSSH之前5.6的系统没有该选项(并且升级它们可能不可行)。不幸的是,似乎没有太多关于在线问题的文档或讨论。
ControlPersist
在游戏中迟到了ssh会话多路复用场景。这意味着必须有一种替代方法来配置会话多路复用,而不依赖于ControlPersist
选项。最初我遇到了ssh会话过早终止并关闭控制连接客户端会话的问题,或者,如果我保持连接打开(保持ssh控件主机处于活动状态),终端I / O被阻止,我的脚本将挂起!但最终我发现了完成它的旗帜。见下文。
OpenSSH option ssh flag Purpose ------------------- --------- ----------------------------- -o ControlMaster=yes -M Establishes sharable connection -o ControlPath=path -S path Specifies path of connection's named socket -o ControlPersist=600 Keep shareable connection open 10 min. -o ControlPersist=yes Keep shareable connection open indefinitely -N Don't create shell or run a command -f Go into background after authenticating -O exit Closes persistent connection
ControlPersist form Equivalent Purpose ------------------- ---------------- ------------------------- -o ControlPersist=yes ssh -Nf Keep control connection open indefinitely -o ControlPersist=300 ssh -f sleep 300 Keep control connection open 5 min.
注意:scp
和sftp
以不同方式实现-S
标志,并且-M
标志完全没有,因此,对于这些命令,始终需要-o option
形式。
概略的操作概述: 注意:此不完整的示例未执行,如图所示。
ctl=<path to dir to store named socket>
ssh -fNMS $ctl user@host # open control master connection
ssh -S $ctl … # example of ssh over connection
scp -o ControlPath=$ctl … # example of scp over connection
sftp -o ControlPath=$ctl … # example of sftp over connection
ssh -S $ctl -O exit # close control master connection
会话多路复用演示(工作示例 - 仅验证一次): 如果您无法访问远程主机,只需在“主机......?”中输入localhost即可。提示您是否要尝试此演示脚本
#!/bin/bash # This script demonstrates ssh session multiplexing
trap "[ -z "$ctl" ] || ssh -S $ctl -O exit $user@$host" EXIT # closes conn, deletes fifo
read -p "Host to connect to? " host
read -p "User to login with? " user
BOLD="\n$(tput bold)"; NORMAL="$(tput sgr0)"
echo -e "${BOLD}Create authenticated persistent control master connection:${NORMAL}"
sshfifos=~/.ssh/controlmasters
[ -d $sshfifos ] || mkdir -p $sshfifos; chmod 755 $sshfifos
ctl=$sshfifos/$user@$host:22 # ssh stores named socket for open ctrl conn here
ssh -fNMS $ctl $user@$host # Control Master: Prompts passwd then persists in background
lcldir=$(mktemp -d /tmp/XXXX); echo -e "\nLocal dir: $lcldir"
rmtdir=$(ssh -S $ctl $user@$host "mktemp -d /tmp/XXXX"); echo "Remote dir: $rmtdir"
echo -e "${BOLD}Copy self to remote with scp:${NORMAL}"
scp -o ControlPath=$ctl ${BASH_SOURCE[0]} $user@$host:$rmtdir
echo -e "${BOLD}Display 4 lines of remote script, with ssh:${NORMAL}"
echo "====================================================================="
echo $rmtdir | ssh -S $ctl $user@$host "dir=$(</dev/stdin); head -4 \$dir/*"
echo "====================================================================="
echo -e "${BOLD}Do some pointless things with sftp:${NORMAL}"
sftp -o ControlPath=$ctl $user@$host:$rmtdir <<EOF
pwd
ls
lcd $lcldir
get *
quit
EOF
$ echo "abc" | cat <(echo "def")
表达式<(echo "def")
扩展为文件名,通常类似于/dev/fd/63
,它命名包含文本"def"
的(虚拟)文件。所以让我们简化一下:
$ echo "def" > def.txt
$ echo "abc" | cat def.txt
这也将打印def
。
管道确实将行abc
提供给cat
命令的标准输入。但是因为cat
在其命令行上被赋予文件名,所以它不会从其标准输入中读取。 abc
被悄悄忽略,cat
命令打印指定文件的内容 - 这正是你告诉它要做的。
使用主控制套接字,您可以使用多个进程,而无需多次进行身份验证。这只是一个简单的例子;请参阅man ssh_config
下的ControlPath
,获取有关使用更安全套接字的建议。
在当地采购somecommand
你的意思并不十分清楚;我将假设它是一个本地脚本,您希望将其复制到远程主机。最简单的方法就是将其复制以运行它。
# Copy the first file, and tell ssh to keep the connection open
# in the background after scp completes
$ scp -o ControlMaster=yes -o ControlPersist=yes -o ControlPath=%C somefile user@host:/tmp/somefile
# Copy the script on the same connection
$ scp -o ControlPath=%C somecommand user@host:
# Run the script on the same connection
$ ssh -o ControlPath=%C user@host somecommand
# Close the connection
$ ssh -o ControlPath=%C -O exit user@host
当然,用户可以使用公钥认证来避免输入他们的凭证,但ssh
每次仍然会经历认证过程。这里,验证过程只使用ControlMaster=yes
命令完成一次。其他两个进程重用该连接。使用-O exit
的最后一个命令实际上没有连接;它只是告诉本地连接关闭自己。
echo abc | cat <(echo def)
的问题在于<()
赢得了“提供投入”的比赛。幸运的是,bash将允许您使用多个<()
构造提供许多输入。所以诀窍是,你如何将你的echo abc
输出到<()
?
怎么样:
$ echo abc | cat <(echo def) <(cat)
def
abc
如果您需要首先处理来自管道的输入,只需切换顺序:
$ echo abc | cat <(cat) <(echo def)
abc
def