解析GETOPTS中带引号的字符串

时间:2019-04-11 00:34:51

标签: linux bash shell scripting

我试图在解析选项时接受以空格分隔的字符串代替$ OPTARG

例如

./script -k '1 2 ad'ias'

如图所示,第三个字符串可以包含任何特殊字符。我想解析整个字符串并处理一些选项时,是否有一种方法可以忽略两者之间的引号

试图插入\字符,但由于我无法在字符串中插入任何字符,因此不适用于我的情况。


while getopts "a:k:" option
do
   echo "${option}"
   case ${option} in
     a)
        function_a ${OPTARG}   # <-- no quotes
      ;;
     k)
        function_k "${OPTARG}" # <-- quotes
      ;;
   esac
done

1 个答案:

答案 0 :(得分:2)

我不确定我是否完全理解困难所在;处理带有特殊字符的字符串有点棘手,但是(除了NUL字符之外)基本上是可行的。要注意的主要事情是:

  • 在表示字符串文字时(在脚本中,或将参数传递给脚本时),必须使用该字符串的有效外壳表示形式,而不仅仅是原始字符串。例如,假设您要传递/使用以下字符串:

    12 34 kla#42@!' 2 M$" rtqas;::#
    

    有多种方式表示 该字符串以用于Shell脚本或命令行。您可以不加引号,但是转义各个特殊字符,例如:

    12\ 34\ kla\#42@\!\'\ 2\ M\$\"\ rtqas\;::\#
    

    或者您可以将其用双引号引起来,并转义那些在双引号内保留特殊含义的字符(即双引号,反引号和美元符号,如果它是bash交互式shell感叹号) :

    "12 34 kla#42@!' 2 M\$\" rtqas;::#"    # For a non-interactive shell
    "12 34 kla#42@\!' 2 M\$\" rtqas;::#"   # For an interactive shell
    

    如果不包含单引号,则可以将其单引号。既然如此,您将无法使用该方法。但是您可以混合使用多种方法,例如在不包含单引号的部分周围使用单引号,并对单引号进行转义或双引号:

    '12 34 kla#42@!'\'' 2 M$" rtqas;::#'    # Single-quote is escaped
    '12 34 kla#42@!'"'"' 2 M$" rtqas;::#'   # Single-quote is double-quoted
    

    在bash(但没有其他一些shell)中,还有用$' ... '编写的ANSI-C转义字符串:

    $'12 34 kla#42@!\' 2 M$" rtqas;::#'    # Single-quote is the only character that needs escaping
    

    请注意,以上所有都是表示完全相同的字符串的不同方式;一旦外壳解析了它,它们中的任何一个都会得到相同的结果。您可以使用任何方便的方法,但是必须使用字符串的语法有效表示形式。

  • 一旦字符串存储在参数/变量中,则必须在对该变量的引用两边加上双引号。在大多数Shell上下文中,当使用不带引号的变量时,Shell会将其拆分为多个单词(基于空格或IFS中的任何内容),并尝试扩展任何类似于文件通配符的内容;你不要这个但是,如果用双引号引起来,则变量将被扩展,并且不做进一步的解析,它将直接进行无干扰的传递。

    实际上,即使您不希望变量引用包含特殊字符,也应该几乎总是在shell脚本中双引号。我们在这里看到很多shell问题,这些问题的答案是“如果将变量引用双引号,就不会有这个问题” ...

这是一个基于您的脚本的示例:

#!/bin/bash

printopt() {
   printf '%s value is: <<%s>>\n' "$1" "$2"    # Double-quotes required here
}

while getopts "a:k:" option
do
   case "${option}" in    # This is one of the few places it's safe to leave off double-quotes. But they don't hurt.
     a)
        printopt "-a" "${OPTARG}"    # Double-quotes required here
      ;;
     k)
        printopt "-k" "${OPTARG}"    # Double-quotes required here
      ;;
   esac
done

并使用各种字符串表示形式运行它:

$ ./argtest.sh -a 12\ 34\ kla\#42@\!\'\ 2\ M\$\"\ rtqas\;::\# -k "1 2 ad'ias"
-a value is: <<12 34 kla#42@!' 2 M$" rtqas;::#>>
-k value is: <<1 2 ad'ias>>
$ ./argtest.sh -a '12 34 kla#42@!'"'"' 2 M$" rtqas;::#' -k $'1 2 ad\'ias'
-a value is: <<12 34 kla#42@!' 2 M$" rtqas;::#>>
-k value is: <<1 2 ad'ias>>

好的,好的,在某些情况下,它比这更复杂:

  • 在某些情况下,字符串将在外壳解析过程中多次运行,例如在ssh上运行时(命令由本地外壳处理,并传递给远程计算机,然后由 that 外壳处理并执行),或用作外壳aliasalias命令将被解析,存储结果,然后在使用时再次解析) 。在这些情况下,您实际上需要两个(或可能更多)进行引号/转义:取原始字符串,通过上述任何一种方法引号/转义,然后取那个 >字符串并用引号/转义 (可能是通过其他方法)。

  • 某些版本的echo将解析字符串中的转义(反斜杠)序列(使用与Shell本身不同的规则),这可能导致混淆。我建议您在可能会出现问题的情况下改用printf;唯一的问题是它比echo更复杂:它不仅打印其参数,它使用的第一个参数是一个格式字符串,用于控制其余参数的打印方式。请参阅上面脚本中的示例。

  • 如果将字符串传递给另一个脚本,该脚本在其参数和变量引用周围不使用双引号,那么您就注定了。在这种情况下,唯一可以做的就是修复其他脚本。