1

我有这个(测试)脚本:

#!/bin/bash

my_cmd_bad_ ( ) {
    cmd="$@"
    $cmd
}

my_cmd_good_ ( ) {
    "$@"
}

my_cmd_bad_  ls -l "file with space"
my_cmd_good_ ls -l "file with space"

输出是(文件不存在,这不是这个问题的重点):

» ~/test.sh
ls: cannot access file: No such file or directory
ls: cannot access with: No such file or directory
ls: cannot access space: No such file or directory
ls: cannot access file with space: No such file or directory

我很惊讶第一个版本没有按预期工作:参数没有被引用,而不是处理一个文件,而是处理三个。为什么?

如何保存要执行的命令并正确引用?我需要稍后执行它,我不再有它"$@"了。

对此测试脚本的简单返工将不胜感激。

4

4 回答 4

2

请参阅类似的问题:如何使用存储在单个变量中的引号传递命令行参数?

使用这些实用函数将命令保存到字符串以供以后执行:

bash_escape() {
  # backtick indirection strictly necessary here: we use it to strip the
  # trailing newline from sed's output, which Solaris/BSD sed *always* output
  # (unlike GNU sed, which outputs "test": printf %s test | sed -e s/dummy//)
  out=`echo "$1" | sed -e s/\\'/\\''\\\\'\\'\\'/g`
  printf \'%s\' "$out"
}
append_bash_escape() {
  printf "%s " "$1"
  bash_escape "$2"
}

your_cmd_fixed_ ( ) {
  cmd="$@"
  while [ $# -gt 0 ] ; do
    cmd=`append_bash_escape "$cmd" "$1"` ; shift
  done
  $cmd
}
于 2013-03-12T12:59:54.720 回答
1

您正在将三个以空格分隔的字符串“ls”、“-l”和“带空格的文件”组合成一个以空格分隔的字符串cmd。无法知道最初引用了哪些空格(在“带空格的文件”中)以及在分配给cmd.

通常,尝试将命令行构建到单个字符串中并不是一个好主意。使用函数,或隔离实际命令并将参数保留在$@.

像这样重写命令:

my_cmd_bad_ () {
    cmd=$1; shift
    $cmd "$@"
}
于 2013-03-12T12:50:55.707 回答
1

您可以引用任何单个参数并稍后对其进行评估:

my_cmd_bad_ ( ) {
  j=0
  for i in "$@"; do
    cmd["$j"]=\"$"$i"\"
    j=$(( $j + 1 ))
   done;
  eval ${cmd[*]}
}
于 2013-03-12T14:06:23.543 回答
0

http://mywiki.wooledge.org/BashFAQ/050

请注意,大多数情况下,您的第二个版本非常受欢迎。唯一的例外是如果你需要做一些特别的事情。例如,您不能将分配或重定向或复合命令捆绑到参数列表中。

处理报价问题的正确方法需要非标准功能。涉及模板的半现实示例:

function myWrapper {
    typeset x IFS=$' \t\n'
    { eval "$(</dev/fd/0)"; } <<-EOF
    for x in $(printf '%q ' "$@"); do
        echo "\$x"
    done
EOF
}

myWrapper 'foo bar' $'baz\nbork'

确保您完全了解这里发生的事情,并且您确实有充分的理由这样做。它需要确保副作用不会影响论点。这个具体的例子并没有展示一个很好的用例,因为一切都是硬编码的,所以你可以提前正确地转义,如果你愿意的话,可以扩展引用的参数。

于 2013-03-12T13:24:17.147 回答