将变量值传递给复杂命令

问题描述 投票:0回答:3

我试图将值从字符串变量传递给复杂的bash命令,我有问题。当我执行命令时:

files=($(ls abc*))

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

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

bash ssh psql scp
3个回答
3
投票

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

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

这里的引用有点棘手,如果你的文件名可能包含例如字面单引号。双引号导致本地shell扩展$file;那么扩展值是单引号,以防止远程ssh进一步操作它。

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

files=(abc*)

2
投票

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

引用双引号(")和引用单引号(')之间的重要区别之一是后者抑制参数扩展。您需要将$file移动到引号之外,或者更改为双引号样式。此外,你应该恰当地引用$file的扩展,否则你会在不寻常的文件名(例如包含空格的文件名)的情况下让自己受到破坏甚至是令人讨厌的惊喜。

而且,您正在格式化将由不同shell处理的命令字符串这一事实会增加一些复杂性。确保正确引用的最简单方法是使用bash的printf builtin,其%q格式化选项完全用于引用任意字符串,以便可以将结果重新读取为shell输入以重现相同的字符串。总的来说,这是我对循环的建议:

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

0
投票

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

ssh -N -L 12345:domain.com:$DB_PORT [email protected] & ssh_pid=$!

files=(abc*)

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

kill "$ssh_pid"

这具有不暴露数据库密码的额外好处,因为您只需手动键入或使用本地.pgpass文件即可。

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

© www.soinside.com 2019 - 2024. All rights reserved.