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

时间:2015-12-19 03:53:02

标签: bash bash4

我希望我的脚本被调用为:

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

其中-a -b "foo bar"是传递给我的脚本内部执行的命令(例如grep)的命令行参数。

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

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

2 个答案:

答案 0 :(得分:4)

没有非常好的方法来实现这一点,只有解决方法。几乎所有常见的解决方案都涉及将单个参数作为单独的参数传递,因为替代方法 - 将参数列表作为单个参数传递 - 使您每次想要传递一个甚至稍微复杂的参数时都会跳过火圈(其中,它结果将是常见的;你的例子只是你即将陷入的变元化的开始。请参阅this Bash FAQ entry了解更多见解。)

可能最常见的解决方案是将参数放在参数列表的末尾。这意味着您需要知道实际用于脚本的参数的结尾,这或多或少意味着存在固定数量的位置参数。通常,固定数字是1.这个策略的一个例子就是任何脚本解释器(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

以下是find的示例代码 - 类似建议:

# 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 :(得分:1)

我找到了一个较短的方法,只需将脚本参数作为参数传递给命令:只应该评估一个命令。

在您的问题中,以下script将执行您想要的操作

#!/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

然后,您可以将此script调用包含空格的参数传递给grep

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

./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