Bash 传递命令行参数和命令行参数

Bash 传递命令行参数和命令行参数,bash,bash4,Bash,Bash4,我希望我的脚本的调用方式如下: ./script -f "-a -b "foo bar"" baz 其中-a-b“foo-bar”是传递给脚本内部执行的命令(例如grep)的命令行参数 这里的问题当然与引用有关:天真的方法(将命令行参数扩展为script)将foo和bar视为单独的参数,这当然是不可取的 实现这一点的最佳方法是什么?没有真正伟大的方法,只有变通办法。几乎所有常见的解决方案都涉及将单个参数作为单个参数传递,因为另一种方法——将一系列参数作为单个参数传递——会让您在每次传递一个甚至

我希望我的脚本的调用方式如下:

./script -f "-a -b "foo bar"" baz
其中
-a-b“foo-bar”
是传递给脚本内部执行的命令(例如
grep
)的命令行参数

这里的问题当然与引用有关:天真的方法(将命令行参数扩展为
script
)将
foo
bar
视为单独的参数,这当然是不可取的


实现这一点的最佳方法是什么?

没有真正伟大的方法,只有变通办法。几乎所有常见的解决方案都涉及将单个参数作为单个参数传递,因为另一种方法——将一系列参数作为单个参数传递——会让您在每次传递一个甚至稍微复杂的参数时都能跳过火圈(事实证明,这是很常见的;您的示例只是您即将陷入的元引用泥潭的开始。有关更多信息,请参阅)

最常见的解决方案可能是将要传递的参数放在参数列表的末尾。这意味着您需要知道实际用于脚本的参数的末尾,这或多或少意味着存在固定数量的位置参数。通常,固定数量为1。此str的一个示例ategy非常适合任何脚本解释器(
bash
本身,
python
等),它只接受一个位置参数,即脚本的名称。这将使您的调用:

./script baz -a -b "foo bar"
这并不总是很方便。例如,
find
命令有一个
-exec
选项,在下面的参数中后跟一个实际的命令。要做到这一点,您必须知道要传递的单词的结束位置;
find
通过使用一个特定的分隔符参数来解决这个问题:一个分号。(这是一个任意的选择,但它很少作为脚本参数使用,所以它通常是有效的。)在这种情况下,您的调用如下所示:

./script -f -a -b "foo bar" \; baz
显然,需要引用
,否则它将终止命令,但这不是一个复杂的引用问题

可以通过允许用户显式指定分隔符字来扩展:

./script --arguments=--end -a -b "foo bar" --end baz
下面是一些类似于
的查找建议的示例代码:

# Use an array to accumulate the arguments to be passed through
declare -a passthrough=()

while getopts f:xyz opt; do
  case "$opt" in
    f)
       passthrough+=("$OPTARG")
       while [[ ${!OPTIND} != ';' ]]; do
         if ((OPTIND > $#)); then
           echo "Unterminated -f argument" >>/dev/stderr
           exit 1
         fi
         passthrough+=("${!OPTIND}")
         ((++OPTIND))
       done
       ((++OPTIND))
     ;;
    # Handle other options
  esac
done
# Handle positional arguments

# Use the passthrough arguments
grep "${passthrough[@]}" # Other grep arguments

实现这一点没有什么好办法,只有变通办法。几乎所有常见的解决方案都涉及将单个参数作为单个参数传递,因为另一种方法——将一系列参数作为单个参数传递——会让你在每次想传递一个甚至稍微复杂的参数时,都能跳过火圈(事实证明,这是很常见的;您的示例只是您即将陷入的元引用泥潭的开始。有关更多信息,请参阅)

最常见的解决方案可能是将要传递的参数放在参数列表的末尾。这意味着您需要知道实际用于脚本的参数的末尾,这或多或少意味着存在固定数量的位置参数。通常,固定数量为1。此str的一个示例ategy非常适合任何脚本解释器(
bash
本身,
python
等),它只接受一个位置参数,即脚本的名称。这将使您的调用:

./script baz -a -b "foo bar"
这并不总是很方便。例如,
find
命令有一个
-exec
选项,在下面的参数中后跟一个实际的命令。要做到这一点,您必须知道要传递的单词的结束位置;
find
通过使用一个特定的分隔符参数来解决这个问题:一个分号。(这是一个任意的选择,但它很少作为脚本参数使用,所以它通常是有效的。)在这种情况下,您的调用如下所示:

./script -f -a -b "foo bar" \; baz
显然,需要引用
,否则它将终止命令,但这不是一个复杂的引用问题

可以通过允许用户显式指定分隔符字来扩展:

./script --arguments=--end -a -b "foo bar" --end baz
下面是一些类似于
的查找建议的示例代码:

# Use an array to accumulate the arguments to be passed through
declare -a passthrough=()

while getopts f:xyz opt; do
  case "$opt" in
    f)
       passthrough+=("$OPTARG")
       while [[ ${!OPTIND} != ';' ]]; do
         if ((OPTIND > $#)); then
           echo "Unterminated -f argument" >>/dev/stderr
           exit 1
         fi
         passthrough+=("${!OPTIND}")
         ((++OPTIND))
       done
       ((++OPTIND))
     ;;
    # Handle other options
  esac
done
# Handle positional arguments

# Use the passthrough arguments
grep "${passthrough[@]}" # Other grep arguments

我发现了一个较短的方法,可以将脚本参数作为参数传递给命令,但只有一个限制:应该对命令求值

在您的问题中,以下
脚本将满足您的要求

#!/bin/bash

# Do whatever you want ...
# Just claim that 2nd quoted argument of script should be passed as
# a set of parameters of grep
eval grep $2
然后你可以调用这个
脚本
,空格中包含要传递给
grep的参数

./script -f "-a -b \"foo bar\"" baz

瞧!将使用正确的参数调用
grep

grep -a -b "foo bar"
例如,打电话

./script -f '-i "system message" /etc/passwd' baz
然后得到正确的输出

dbus:x:81:81:System message bus:/:/sbin/nologin

我发现了一个较短的方法,可以将脚本参数作为参数传递给命令,但只有一个限制:应该对命令求值

在您的问题中,以下
脚本将满足您的要求

#!/bin/bash

# Do whatever you want ...
# Just claim that 2nd quoted argument of script should be passed as
# a set of parameters of grep
eval grep $2
然后你可以调用这个
脚本
,空格中包含要传递给
grep的参数

./script -f "-a -b \"foo bar\"" baz

瞧!将使用正确的参数调用
grep

grep -a -b "foo bar"
例如,打电话

./script -f '-i "system message" /etc/passwd' baz
然后得到正确的输出

dbus:x:81:81:System message bus:/:/sbin/nologin

这显然不能避免复杂的元引号;请阅读我答案中的bash常见问题解答。此外,您应该仔细考虑
eval grep“$2”之间的区别
eval grep$2
;你可能是指第一个,但都有一些特殊情况。我同意。这不是一个常见的解决方案。这只是一个简单的快速解决方案,大多数问题都需要传递,比如说,以空格为参数的字符串。这显然不能避免复杂的元引号;请阅读我答案中的bash常见问题解答。此外,你还需要你应该仔细考虑一下
eval grep“$2”
eval grep$2
之间的区别;你可能指的是第一个,但两者都有不同的情况。我同意。这不是一个常见的解决方案。这只是一个简单的快速解决方案