在远程执行时,使排版功能访问本地变量

时间:2018-02-17 22:49:27

标签: bash sh

我想在本地创建一个函数echo_a,并通过ssh将其传递给远程shell,此处为typeset -f。问题是函数无法访问局部变量。

export a=1

echo_a() {
    echo a: $a
}

bash <<EOF

$(typeset -f echo_a)

echo local heredoc:
echo_a
echo

echo local raw heredoc:
echo a: $a
echo

EOF

ssh localhost bash <<EOF

$(typeset -f echo_a)

echo remote heredoc:
echo_a
echo

echo remote raw heredoc:
echo a: $a
echo

EOF

假设ssh连接是自动的,运行上面的脚本会给我输出:

local heredoc:
a: 1

local raw heredoc:
a: 1

remote heredoc:
a:

remote raw heredoc:
a: 1

看看“远程heredoc”a是如何空的?我该怎么办才能获得1

我测试过在任何地方添加引号和反斜杠都没有成功。

我错过了什么?除typeset以外的其他内容会使这项工作吗?

1 个答案:

答案 0 :(得分:0)

感谢@Guy提示,确实是因为默认情况下ssh禁用发送环境变量。就我而言,不需要更改服务器的设置。

希望我们可以使用compgenevaldeclare进行攻击。

首先,我们通常会识别添加的变量。如果在被调用函数内创建变量也可以工作。使用compgen很简洁,因为我们不需要显式地export变量。

数组差异代码来自https://stackoverflow.com/a/2315459/1013628和来自https://stackoverflow.com/a/16337687/1013628compgen技巧。

# Store in env_before all variables created at this point
IFS=$'\n' read -rd '' -a env_before <<<"$(compgen -v)"
a=1
# Store in env_after all variables created at this point
IFS=$'\n' read -rd '' -a env_after <<<"$(compgen -v)"

# Store in env_added the diff betwen env_after and env_before
env_added=()
for i in "${env_after[@]}"; do
    skip=
    for j in "${env_before[@]}"; do
        [[ $i == $j ]] && { skip=1; break; }
    done
    if [[ $i == "env_before" || $i == "PIPESTATUS" ]]; then
        skip=1
    fi
    [[ -n $skip ]] || env_added+=("$i")
done

echo_a() {
    echo a: $a
}

env_added现在拥有两个compgen调用之间添加变量的所有名称数组。

$ echo "${env_added[@]}"
a

我还会过滤出变量env_beforePIPESTATUS,因为它们是由bash自动添加的。

然后,在heredocs中,我们添加eval $(declare -p "${env_added[@]}")

declare -p VAR [VAR ...]为每个VAR打印变量名称,后跟=,后跟其值:

$ a = 1
$ b = 2
$ declare -p a b 
declare -- a=1 
declare -- b=2

eval实际上是评估declare行。其余代码如下:

bash <<EOF

# Eval the variables computed earlier
eval $(declare -p "${env_added[@]}")
$(typeset -f echo_a)

echo local heredoc:
echo_a
echo

echo local raw heredoc:
echo a: $a
echo

EOF

ssh rpi_301 bash <<EOF

# Eval the variables computed earlier
eval $(declare -p "${env_added[@]}")
$(typeset -f echo_a)

echo remote heredoc:
echo_a
echo

echo remote raw heredoc:
echo a: $a
echo

EOF

最后,运行修改过的脚本会给我想要的行为:

local heredoc:
a: 1

local raw heredoc:
a: 1

remote heredoc:
a: 1

remote raw heredoc:
a: 1