在ssh命令中使用shell变量的未定义变量

时间:2015-11-11 22:31:02

标签: bash shell undefined

我对bash脚本相当新,可能不会经常这样做。我有以下脚本来检查某个位置的最新更新,但我得到: location: Undefined variable并且未成功检索时间戳。

#!/bin/bash

servers=('x' 'y')
results=()
location="/path/to/location/"

for server in "${servers[@]}"; do   
    output=$(ssh "$server" 'find "${location}" -exec stat \{} --printf="%y\n" \; | sort -n -r | head -n 1')
    x=(${server}:${output})
    results+=(${x})
done

echo "Printing results..."
for res in "${results[@]}"; do
    echo "$res"
done

我的目标是在最后打印:

x:<timestamp of last update>
y:<timestamp of last update>

我想知道出了什么问题。

1 个答案:

答案 0 :(得分:0)

这里的直接问题是'foo "$bar"'仍然在单引号内扩展bar(这使得双引号成为字面,而不是语法)。这不是一件坏事 - 当传递给ssh时,字面引号可以被ssh连接的远程端的shell所尊重 - 但它具有$location不是'的副作用扩大;因此,对"${location}"的引用使它成为远程shell,它未定义。

eval生成正确转义命令或作为参数传递给ssh的最佳方法是让shell为您执行此操作。在目前的情况下,看起来有点像这样:

printf -v cmd1 '%q ' find "$location" -exec stat --printf='%y\n' '{}' +
ssh "$server" "$cmd1 | sort -n -r | head -n 1"

管道组件未包含在cmd1中,因为 - 在将转义内容视为数据而不是语法时,printf %q会导致文字|字符作为find的参数而不是shell指令。 (这正是你想要它与$location中的任何文字内容有关,否则它们可能会用作命令!)。

这样,shell本身(通过printf %q的魔力)将负责扩展变量并正确地转义find命令,以确保将内容视为数据(即。location '/tmp/$(rm -rf /)' ./Crashlytics.framework/submit $CRASHLYTICS_API_KEY $CRASHLYTICS_BUILD_SECRET -ipaPath $IPA_PATH不会删除文件。)