【问题标题】:Parsing a string with quotes in GETOPTS在 GETOPTS 中解析带引号的字符串
【发布时间】:2019-04-11 00:34:51
【问题描述】:

我在解析选项时尝试接受一个空格分隔的字符串来代替 $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

【问题讨论】:

  • 用于运行脚本的命令必须是有效的 shell 语法;您给出的示例是 not 有效的 shell 语法(不正确的引用),并且您的脚本无法改变这一点。如果您尝试将单引号作为参数的一部分传递给脚本,有许多可用的引用方法:您可以使用 ./script -k "1 2 ad'ias"./script -k 1\ 2\ ad\'ias./script -k '1 2 ad'"'"'ias'./script -k $'1 2 ad\'ias' 或.. . 不管怎样,一旦你的脚本得到参数,它真正需要做的就是在使用它时用双引号括起来(例如"${OPTARG}")。
  • 是的,但我的问题是我的输入可以是所有特殊字符的值。这就是为什么我来这里看看是否有其他可以讨论的想法
  • 我不清楚您在哪里遇到了麻烦。是在构造一个将特殊字符传递给脚本的命令,还是在脚本收到参数后如何处理它们?顺便说一句,您不能在参数中传递一个字符:ASCII NUL(十六进制 00)。
  • 哦,我在发送多个带空格的参数时遇到了麻烦。假设我正在发送可能包含上述所有字符的密钥。然后将它们作为字符串处理有点不可能
  • 我可能只想像 ./script -a 12 34 kla#42@!' 2 M rtqas;::#

标签: linux bash shell scripting


【解决方案1】:

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

  • 当你表示一个字符串字面量时(在脚本中,或在向脚本传递参数时),你必须使用该字符串的有效 shell 表示,而不仅仅是原始字符串。例如,假设你想传递/使用这个字符串:

    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 上下文中,当使用不带引号的变量时,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>>

好的,好的,有几种情况比这更复杂:

  • 在某些情况下,一个字符串会在 shell 解析过程中运行多次,例如当它在ssh 上运行时(命令由本地 shell 处理,传递到远程计算机,然后由 that shell 处理并执行),或用作 shell aliasalias 命令被解析,结果存储,然后在使用时再次解析)。在这些情况下,您基本上需要两个(或可能更多)的引用/转义:获取原始字符串,通过上述任何方法引用/转义,然后获取 那个 字符串和引号/转义 那个(可能通过不同的方法)。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多