通过 SSH 传输环境变量/在 bash/sh/csh/tcsh 中引用

通过 SSH 传输环境变量/在 bash/sh/csh/tcsh 中引用

我想通过 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

相关内容