将变量的值传递给复杂命令

时间:2019-04-17 14:40:13

标签: bash ssh psql scp

我正在尝试将值从字符串变量传递给复杂的bash命令,但我遇到了问题。当我执行命令时:

files=($(ls abc*))

for file in "${files[@]}"
do
    scp $file user@domain.com:~
    ssh user@domain.com 'PGPASSWORD="abcd" psql -h domain.com -U user -d dbb -f ~/$file'
    ssh user@domain.com 'rm ~/$file'
done

我遇到一些错误。只有'scp'是正确执行的,但是后面的行失败。我看到“ psql -f缺少参数”和“找不到文件/无法删除字典” 此代码有什么问题?如何将字符串值传递给ssh命令?

3 个答案:

答案 0 :(得分:2)

单引号将阻止远程外壳程序接收您的变量。 Don't use ls in scripts. Quote your variables.在要求人工审查之前使用http://shellcheck.net/

for file in abc*
do
    scp "$file" user@domain.com:~
    ssh user@domain.com "PGPASSWORD='abcd' psql -h domain.com -U user -d dbb -f ~/'$file'; 
        rm ~/'$file'"
done

这里的引用有点棘手,如果文件名可能包含例如文字单引号。双引号引起本地外壳扩展$file;那么扩展值用单引号引起来,以防止远程ssh进一步操纵它。

我取出了数组,因为它似乎没有任何用处。如果需要数组,只需使用通配符即可分配它:

files=(abc*)

答案 1 :(得分:1)

  

此代码有什么问题?如何将字符串值传递给ssh命令?

双引号(")和单引号(')之间的重要区别之一是后者抑制了参数扩展。您需要将$file移到引号之外,或更改为双引号样式。但是,此外,您应该正确引用$file的扩展名,否则在出现不寻常的文件名(例如包含空格的文件名)的情况下,很容易遭受破坏甚至令人惊讶。

此外,您正在格式化将由其他Shell处理的命令字符串的事实增加了一些复杂性。确保引用正确的最简单方法是使用bash的printf内置函数,其%q格式设置选项的作用恰恰是引用任意字符串的目的,以便可以将结果重新读取为shell输入以重现相同的字符串。总体而言,这是我对循环的建议:

for file in "${files[@]}"
do
    scp "${file}" user@domain.com:~
    ssh user@domain.com "PGPASSWORD='abcd' psql -h domain.com -U user -d dbb -f $(printf %q "~/${file}")"
    ssh user@domain.com "rm $(printf %q "~/${file}")"
done

答案 2 :(得分:0)

设置SSH隧道并在本地运行psql ,而不是将每个文件复制到远程主机上,通过隧道连接到远程数据库。

ssh -N -L 12345:domain.com:$DB_PORT user@domain.com & ssh_pid=$!

files=(abc*)

for file in "${files[@]}"
do
    psql -p 12345 -U user -d dbb -f "$file"
done

kill "$ssh_pid"

这还具有不公开数据库密码的额外优点,因为您可以简单地手动输入密码或使用本地.pgpass文件。

请注意,仅当未将远程数据库配置为接受网络连接时,才需要隧道。