如何在 bash 中的源函数中的 scp 命令中正确转义空格形成多个文件
How to properly escape spaces form multiple files in an scp command in a sourced function in bash
我在我的 .bashrc 中构建了一个函数,当它尝试对名称中包含空格的文件进行 scp 时会中断,但是如果我 运行 从该函数生成的命令输出在 shell 直接它似乎工作正常。
我试过转义空格,以及单引号和双引号的几种变体,下面的版本是我最接近工作的版本,但我不明白为什么会失败。
来自 .bashrc
push2() {
# parse args, build file list, get suffix from final arg
files=""
i=1
orig_IFS=$IFS; IFS=":"
for arg in $*; do
if [ "$i" = "$#" ]; then
suffix=$arg
else
files="$files $(echo $arg | sed -r 's/ /\ /')" #escape spaces
fi
i=$(($i+1))
done
IFS=$orig_IFS
# determine prefix and proxy
gen_prefix $suffix
# output generated command for debugging
echo "scp $scp_proxy$files testuser@$prefix$suffix:"
# run command
scp $scp_proxy$files testuser@$prefix$suffix:
}
运行 即使输出命令字符串显示正确转义,函数似乎仍然失败
root@DHCP-137:~$ push2 temp* 42
scp temp temp\ file testuser@10.3.3.42:
temp 100% 6008 1.1MB/s 00:00
temp\: No such file or directory
file: No such file or directory
运行 它生成的命令按预期工作
root@DHCP-137:~$ scp temp temp\ file testuser@10.3.3.42:
temp 100% 6008 896.4KB/s 00:00
temp file 100% 0 0.0KB/s 00:00
root@DHCP-137:~$
附加信息:GNU bash,版本 4.4.12(1)-release (x86_64-pc-linux-gnu) - 运行ning on Debian 9
整个事情通过更改最后一行得到修复
scp $scp_proxy$files testuser@$prefix$suffix:
并将其包装在这样的 eval 中
eval "scp $scp_proxy$files testuser@$prefix$suffix:"
首先,更改您的呼叫签名,使后缀在前:
push2 42 ./temp*
那么函数应该简单定义为
push2 () {
local -a scpProxy
local prefix suffix
suffix=
shift
gen_prefix "$suffix"
scp "${scpProxy[@]}" "$@" "testuser@$prefix.$suffix:"
}
其中 gen_prefix
看起来像
gen_prefix () {
case in
42) scpProxy=()
prefix=10.3.3
;;
89) scpProxy=(-o ProxyJump=user@server)
prefix=123.456.789
;;
esac
}
调用 shift
后,$@
只包含您要传输的文件。 scpProxy
是一个数组,其中包含要传递给 scp
的多个单独参数;如果为空,则 "${scpProxy[@]}"
将扩展为 0 个参数,而不是空字符串。
(使用 ./temp*
而不是 temp*
可以防止包含 :
的匹配项因此可能被误认为是远程文件名。)
尽管 gen_prefix
似乎定义了它的变量 "globally",但它实际上只是在 gen_prefix
被 调用 的任何范围内定义它们(bash
使用动态范围,而不是词法范围,就像大多数其他常见语言一样)。对 local
的两次调用确保 gen_prefix
分配的内容保留在 push2
中,并且在 push2
退出后不可见。
作为附加说明,此功能的大部分可以通过合适的 ssh
配置消失。在您的 .ssh/config
文件中考虑这一点:
Host somehost
User testuser
Hostname 10.3.3.42
Host someotherhost
User testuser
Hostname 123.456.789.89
ProxyJump user@server
现在您根本不需要 push2
;只是 运行
scp temp* somehost:
或
scp temp* someotherhost:
并且将自动使用正确的地址和选项。 ssh
配置取代了 gen_prefix
所做的一切,并且无需调用 gen_prefix
,不再需要包装 scp
.
我在我的 .bashrc 中构建了一个函数,当它尝试对名称中包含空格的文件进行 scp 时会中断,但是如果我 运行 从该函数生成的命令输出在 shell 直接它似乎工作正常。
我试过转义空格,以及单引号和双引号的几种变体,下面的版本是我最接近工作的版本,但我不明白为什么会失败。
来自 .bashrc
push2() {
# parse args, build file list, get suffix from final arg
files=""
i=1
orig_IFS=$IFS; IFS=":"
for arg in $*; do
if [ "$i" = "$#" ]; then
suffix=$arg
else
files="$files $(echo $arg | sed -r 's/ /\ /')" #escape spaces
fi
i=$(($i+1))
done
IFS=$orig_IFS
# determine prefix and proxy
gen_prefix $suffix
# output generated command for debugging
echo "scp $scp_proxy$files testuser@$prefix$suffix:"
# run command
scp $scp_proxy$files testuser@$prefix$suffix:
}
运行 即使输出命令字符串显示正确转义,函数似乎仍然失败
root@DHCP-137:~$ push2 temp* 42
scp temp temp\ file testuser@10.3.3.42:
temp 100% 6008 1.1MB/s 00:00
temp\: No such file or directory
file: No such file or directory
运行 它生成的命令按预期工作
root@DHCP-137:~$ scp temp temp\ file testuser@10.3.3.42:
temp 100% 6008 896.4KB/s 00:00
temp file 100% 0 0.0KB/s 00:00
root@DHCP-137:~$
附加信息:GNU bash,版本 4.4.12(1)-release (x86_64-pc-linux-gnu) - 运行ning on Debian 9
整个事情通过更改最后一行得到修复
scp $scp_proxy$files testuser@$prefix$suffix:
并将其包装在这样的 eval 中
eval "scp $scp_proxy$files testuser@$prefix$suffix:"
首先,更改您的呼叫签名,使后缀在前:
push2 42 ./temp*
那么函数应该简单定义为
push2 () {
local -a scpProxy
local prefix suffix
suffix=
shift
gen_prefix "$suffix"
scp "${scpProxy[@]}" "$@" "testuser@$prefix.$suffix:"
}
其中 gen_prefix
看起来像
gen_prefix () {
case in
42) scpProxy=()
prefix=10.3.3
;;
89) scpProxy=(-o ProxyJump=user@server)
prefix=123.456.789
;;
esac
}
调用 shift
后,$@
只包含您要传输的文件。 scpProxy
是一个数组,其中包含要传递给 scp
的多个单独参数;如果为空,则 "${scpProxy[@]}"
将扩展为 0 个参数,而不是空字符串。
(使用 ./temp*
而不是 temp*
可以防止包含 :
的匹配项因此可能被误认为是远程文件名。)
尽管 gen_prefix
似乎定义了它的变量 "globally",但它实际上只是在 gen_prefix
被 调用 的任何范围内定义它们(bash
使用动态范围,而不是词法范围,就像大多数其他常见语言一样)。对 local
的两次调用确保 gen_prefix
分配的内容保留在 push2
中,并且在 push2
退出后不可见。
作为附加说明,此功能的大部分可以通过合适的 ssh
配置消失。在您的 .ssh/config
文件中考虑这一点:
Host somehost
User testuser
Hostname 10.3.3.42
Host someotherhost
User testuser
Hostname 123.456.789.89
ProxyJump user@server
现在您根本不需要 push2
;只是 运行
scp temp* somehost:
或
scp temp* someotherhost:
并且将自动使用正确的地址和选项。 ssh
配置取代了 gen_prefix
所做的一切,并且无需调用 gen_prefix
,不再需要包装 scp
.