正确引用通过另一个命令间接传递的数组

Kon*_*lph 4 bash array quoting arguments

我需要将文件名数组传递给命令,并保留正确的引用。到目前为止,一切都很好。不幸的是,该命令实际上是一个子命令,又被另一个命令调用。具体来说,命令是:

\n\n
git filter-branch --index-filter \\\n    \'git rm -rf --cached \xe2\x80\xb9file1\xe2\x80\xba \xe2\x80\xb9file2\xe2\x80\xba\xe2\x80\xa6\' \\\n    HEAD\n
Run Code Online (Sandbox Code Playgroud)\n\n

为简单起见,我\xe2\x80\x99m 将在下面用一个显示相同问题的更简单的命令替换它:

\n\n
printf \'%s\\n\' \'cmd file1 file2\xe2\x80\xa6\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在我\xe2\x80\x99已经得到了一个数组files=(\'a b\' c)。我想要的结果是上面的命令在一行中打印,并cmd根据需要单独引用后面的每个标记(例如,当存在\xe2\x80\x99s 空格时)。

\n\n

如果我手动扩展并引用文件名,它会起作用:

\n\n
$ printf \'%s\\n\' \'cmd \'\\\'\'a b\'\\\'\' c\'\n\xe2\x86\x92 cmd \'a b\' c\n
Run Code Online (Sandbox Code Playgroud)\n\n

(或者,我可以混合使用单引号和双引号来达到相同的结果。)

\n\n

但如果我尝试传递数组,它就不再起作用:

\n\n
    \n
  1. $ (set -x; printf \'%s\\n\' "cmd \'${files[@]}\'")\n+ printf \'%s\\n\' \'cmd \'\\\'\'a b\' \'c\'\\\'\'\'\n\xe2\x86\x92 cmd \'a b\nc\'\n
    Run Code Online (Sandbox Code Playgroud)
  2. \n
  3. $ (set -x; printf \'%s\\n\' \'cmd \'\\\'"${files[@]}"\\\')\n+ printf \'%s\\n\' \'cmd \'\\\'\'a b\' \'c\'\\\'\'\'\n\xe2\x86\x92 cmd \'a b\nc\'\n
    Run Code Online (Sandbox Code Playgroud)
  4. \n
  5. $ (set -x; printf \'%s\\n\' \'cmd \'"${files[@]}")\n+ printf \'%s\\n\' \'cmd a b\' c\n\xe2\x86\x92 cmd a b\nc\n
    Run Code Online (Sandbox Code Playgroud)
  6. \n
\n\n

我\xe2\x80\x99m并不感到惊讶(3)不\xe2\x80\x99工作(并且它\xe2\x80\x99s只是为了完整性而包含在内)。根据 的输出set -x,shell 正确地引用了 (1) 和 (2) 中的各个数组元素,甚至在整个内容周围添加了转义引号。但随后它会分解单独引用的项目。有办法防止这种情况吗?

\n\n
\n\n

顺便说一下,Shellcheck (SC2145) 建议将上述[@]部分替换为[*]上述部分。这显然会破坏带有空格的文件名。

\n

agc*_*agc 5

  1. 使用数组代替数组set -- file1 file2 ...来填充参数列表,然后使用uote运算符进行bash 参数转换Q

    set -- 'a "b' c "d 'e" "f 'g "'"h' ; (set -x; printf 'cmd %s\n' "${*@Q}")
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    + printf 'cmd %s\n' ''\''a "b'\'' '\''c'\'' '\''d '\''\'\'''\''e'\'' '\''f '\''\'\'''\''g "h'\'''
    cmd 'a "b' 'c' 'd '\''e' 'f '\''g "h'
    
    Run Code Online (Sandbox Code Playgroud)

    或者,如果我们删除该set -x; 部分,输出将变为:

    cmd 'a "b' 'c' 'd '\''e' 'f '\''g "h'
    
    Run Code Online (Sandbox Code Playgroud)
  2. LL3的评论提出了一种不需要的更好方法 set -- ...

    export x; n=(a "b 'c"); x="${n[@]@Q}"
    ( n=($x); printf 'cmd %s\n' "${n[*]}"; )
    
    Run Code Online (Sandbox Code Playgroud)

    简单版本:

    set -- 'a "b' c "d 'e" "f 'g "'"h' ; (set -x; printf 'cmd %s\n' "${*@Q}")
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    cmd 'a' 'b '\''c'
    
    Run Code Online (Sandbox Code Playgroud)
  3. 另一种方法是使用带有赋值运算符的bash 参数转换(也需要):Aeval

    export x;n=(a b 'c d');x="${n[@]@A}"; (eval "$x";printf '%s\n' "${n[@]}")
    
    Run Code Online (Sandbox Code Playgroud)

    输出显示所printf看到的内容:

    a
    b
    c d
    
    Run Code Online (Sandbox Code Playgroud)