将变量值传递给复杂命令
Pass value of variable to complex command
我正在尝试将值从字符串变量传递到复杂的 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' 被正确执行,但它们之后的行失败了。我看到 "missing parameter for psql -f" 和 "file not found / cannot remove dictionary"
这段代码有什么问题?如何将字符串值传递给 ssh 命令?
单引号会阻止远程 shell 接收您的变量。 Don't use ls
in scripts. Quote your variables. Use 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
此处的引用有点棘手,如果您的文件名可能包含例如文字单引号。双引号导致局部 shell 展开 $file
;然后扩展值用单引号引起来以防止远程 ssh 进一步操纵它。
我把数组拿出来了,因为它似乎没有任何用处。如果你想要一个数组,只需用通配符分配它:
files=(abc*)
What is problem with this code? How to pass string value to ssh commands?
双引号 ("
) 和单引号 ('
) 之间的重要区别之一是后者抑制参数扩展。您需要将 $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
不是将每个文件复制到远程主机执行,而是设置 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
文件即可。
请注意,仅当远程数据库未配置为接受网络连接时才需要隧道。
我正在尝试将值从字符串变量传递到复杂的 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' 被正确执行,但它们之后的行失败了。我看到 "missing parameter for psql -f" 和 "file not found / cannot remove dictionary" 这段代码有什么问题?如何将字符串值传递给 ssh 命令?
单引号会阻止远程 shell 接收您的变量。 Don't use ls
in scripts. Quote your variables. Use 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
此处的引用有点棘手,如果您的文件名可能包含例如文字单引号。双引号导致局部 shell 展开 $file
;然后扩展值用单引号引起来以防止远程 ssh 进一步操纵它。
我把数组拿出来了,因为它似乎没有任何用处。如果你想要一个数组,只需用通配符分配它:
files=(abc*)
What is problem with this code? How to pass string value to ssh commands?
双引号 ("
) 和单引号 ('
) 之间的重要区别之一是后者抑制参数扩展。您需要将 $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
不是将每个文件复制到远程主机执行,而是设置 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
文件即可。
请注意,仅当远程数据库未配置为接受网络连接时才需要隧道。