我正在尝试将变量从本地服务器(位置 1)传递到远程服务器(位置 2)。该代码的目的是从远程服务器的预定义位置复制文件。简单来说,我想使用预定义路径将文件从 location2 复制到 location1,其中 location1 位于本地服务器上,location2 是远程服务器。看代码段:
$location1=somewhere/on/local_server
$location2=somewhere/on/remote_server
sshpass -p "password" \
ssh [email protected] 'su -lc "cp -r $location2 $location1";'
我得到的错误是 $location1 和 $location2 都未定义。另外,我不想手动输入位置路径,因为它们可以随时更改,并且如果手动完成,在代码中更改它们将很痛苦。
答案1
shell 变量就是:shell 的变量。该ssh
命令或远程主机上启动的 shellsshd
无法访问客户端 shell 的该变量。
出口shell 变量作为环境变量传递给执行的命令。因此,如果您导出它,location1
它将作为环境变量传递到ssh
.如果您使用SendEnv
ssh
配置指令(通过-o
或~/.ssh/config
或/etc/ssh/ssh_config
...),ssh
将尝试将其发送到sshd
远程主机。
但是,要使其工作,sshd
需要有一个AcceptEnv
允许接收该变量的配置指令。但是,出于安全原因,通常情况并非如此。
然而,许多默认的 ssh/sshd 部署允许传递名称以LC_
(用于本地化)开头的变量。所以你可以利用它:
LC_location1=$location1 LC_location2=$location2 ssh host
'su -lc '\''cp -r -- "$LC_location2" "$LC_location1"'\''
或者,您可以让本地 shell 扩展传递到远程 shell 的命令行中的变量:
ssh host "su -lc 'cp -r -- $location2 $location1'"
但这相当于运行:
eval eval "cp -r -- $location2 $location1"
也就是说,这些变量不会作为参数传递给cp
,它们会被解释(两次)为 shell(远程用户的登录 shell,并由 启动sh
)su
代码,因此如果$location1
是/somewhere;rm -rf /
,则会产生戏剧性的后果。
正确的方法是正确转义远程 shell。因为有两级shell,所以需要对单引号进行两次转义(这里使用//ksh93
语法):bash
zsh
escaped_location1=\'${location1//\'/\'\\\'\'}\'
escaped_location1=${escaped_location1//\'/\'\\\'\'}
escaped_location2=\'${location2//\'/\'\\\'\'}\'
escaped_location2=${escaped_location2//\'/\'\\\'\'}
ssh host "su -lc 'cp -r -- $escaped_location2 $escaped_location1'"
假设远程用户的登录 shell 与 Bourne 类似。
答案2
变量不在单引号之间扩展。
使用双引号并转义内部双引号:
sshpass -p "password" \
ssh [email protected] "su -lc \"cp -r $location2 $location1\";"
或关闭单引号并再次打开它们:
sshpass -p "password" \
ssh [email protected] 'su -lc "cp -r '$location2' '$location1'";'
bash 自动执行字符串连接。
注意:未测试。如果$locationX
包含空格或其他奇怪的字符,可能会搞砸。
答案3
除了莱斯马纳的回答之外:您应该引用他的第二个示例中的变量。
sshpass -p "password" \
ssh [email protected] 'su -lc "cp -r '\\\"$location2\\\"' '\\\"$location1\\\"'";'
答案4
看看我为更新我们的服务而编写的这个脚本。
PROJ=$2
将被传递到远程运行环境。
function update-env () {
env=$1
proj=$2
# script for update test env
ssh -A root@$2.$1.test.com PROJ=$2 'bash -s' <<'ENDSSH'
# commands to run on remote host
su gmuser
cd /srv/apps/$PROJ
/usr/bin/git pull
exit
supervisorctl restart $PROJ
ENDSSH
}