首页 > 解决方案 > 如何在 bash 的源函数中的 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) - 在 Debian 9 上运行

标签: bashescapingscp

解决方案


首先,更改您的调用签名,以便后缀在前

push2 42 ./temp*

那么函数应该简单地定义为

push2 () {
  local -a scpProxy
  local prefix suffix
  suffix=$1
  shift

  gen_prefix "$suffix"

  scp "${scpProxy[@]}" "$@" "testuser@$prefix.$suffix:"
}

看起来gen_prefix

gen_prefix () {
  case $1 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似乎“全局”定义了它的变量,但实际上只是在调用gen_prefix的任何范围内定义它们(使用动态范围,而不是词法范围,就像大多数其他常见语言一样)。这两个调用以确保任何分配都留在内部,并且在退出后不可见。bashlocalgen_prefixpush2push2


作为附加说明,此功能的大部分可以通过合适的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,不再需要 wrap scp


推荐阅读