我想通过 SSH 传输环境变量。
“正确”的方法是使用 SendEnv/ ~/.ssh/environment
,但这需要服务器支持 AcceptEnv 或 PermitUserEnvironment,而我的情况并不支持。
因此,我考虑像这样在远程站点上设置变量:
FOO=val
export FOO
ssh server export FOO=$FOO'; do_stuff_which_uses_FOO'
这部分很简单。我想要一个通用的解决方案,这样无论 $FOO 的内容是什么,它都可以工作。例如
FOO=" '\""
export FOO
QFOO=`quote "$FOO"` # quote will return "\ \ \'\\\""
export QFOO
ssh server export FOO=$QFOO'; do_stuff_which_uses_FOO'
无论发送或接收 shell 是 sh 还是 bash,这都可以起作用。
但是,我还需要它适用于 csh/tcsh。而且我事先不知道接收端正在运行哪个 shell。这意味着我必须编写一些可以在 /bin/sh 和 /bin/csh 中运行的代码。
到目前为止我已经设法让它在 sh/bash 上运行:
ssh server // followed by the below quoted
eval `echo $SHELL | grep -E "/(t)?csh" > /dev/null && echo setenv FOO \\\ \\\ \\\\\'\\\\\" || echo export FOO=\\\ \\\ \\\\\'\\\\\";` ; echo "$FOO"
我还可以让它在 csh/tcsh 上工作(用户csh
使用 csh 作为登录 shell):
ssh csh@server // followed by the below quoted
eval `echo $SHELL | grep -E "/(t)?csh" > /dev/null && echo setenv FOO \\\ \\\ \\\\\'\\\\\" || echo export FOO=\\\ \\\ \\\\\'\\\\\";` ; echo "$FOO"
如果 $FOO 是 * 或 ?,它可以与 BASH 一起正常工作:
ssh server eval\ \`echo\ \$SHELL\ \|\ grep\ -E\ \"/\(t\)\?csh\"\ \>\ /dev/null\ \&\&\ echo\ setenv\ FOO\ \\\\\\\\\\\*\\\;\ \|\|\ echo\ export\ FOO=\\\\\\\\\\\*\\\;\`\;echo\ \"\$FOO\"\ a;
但它使用 csh 失败:
ssh csh@server eval\ \`echo\ \$SHELL\ \|\ grep\ -E\ \"/\(t\)\?csh\"\ \>\ /dev/null\ \&\&\ echo\ setenv\ FOO\ \\\\\\\\\\\*\\\;\ \|\|\ echo\ export\ FOO=\\\\\\\\\\\*\\\;\`\;echo\ \"\$FOO\"\ a;
No match.
FOO: Undefined variable.
似乎 * 和 ? 拒绝被引用。
要回答这个问题,你的解决方案应该:
- 能够将环境变量传输到远程服务器
- 不使用 SendEnv/AcceptEnv/PermitUserEnvironment
- 无论源 shell 是 sh/bash/csh/tcsh 都可以工作
- 无论目标 shell 是 sh/bash/csh/tcsh 都可以工作
- 无论环境变量的内容如何,它都可以工作。具体来说,它至少应该适用于:\n * space ' " ? < > ! $ \ 以及这些的任意组合。
如果您可以找到比引用更好的方法来传输变量,那么这也是可以的。
答案1
我现在有了一个适用于除 \n 之外的所有字符的工作模型:
sub shell_quote_scalar {
# Quote the string so shell will not expand any special chars
# Returns:
# string quoted with \ as needed by the shell
my $a = shift;
$a =~ s/([\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\*\>\<\~\|\; \"\!\$\&\'\202-\377])/\\$1/g;
$a =~ s/[\n]/'\n'/g; # filenames with '\n' is quoted using \'
return $a;
}
sub env_quote {
my $v = shift;
$v =~ s/([ \n\&\<\>\(\)\;\'\{\}\t\"\$\`\*\174\!\?\~])/\\$1/g;
return $v;
}
my @qcsh = map { my $a=$_; "setenv $a " . env_quote($ENV{$a}) } @vars;
my @qbash = map { my $a=$_; "export $a=" . env_quote($ENV{$a}) } @vars;
$Global::envvar =
join"",
(q{echo $SHELL | grep -E "/t?csh" > /dev/null && }
. join(" && ", @qcsh)
. q{ || }
. join(" && ", @qbash)
.q{;});
print shell_quote_scalar($Global::envvar);
答案2
你能直接对变量进行 base64 编码吗?处理这个问题最简单的方法可能就是直接将数据视为二进制。
export QFOO=`echo $FOO | base64 --wrap=0`
ssh server "export FOO=`echo \"$QFOO\" | base64 --decode --wrap=0`; <command>"
您可能需要对行中的引号进行修改ssh
,但这就是要点。这应该会删除与引号有关的 shell 细节,但可能会引入一些子命令(我对除 之外的任何内容都不太熟悉bash
)