【问题标题】:In bash script, what is the use of =~ with " " in the right side?在 bash 脚本中,右边有“”的 =~ 有什么用?
【发布时间】:2020-12-11 23:20:14
【问题描述】:

这是 bash 脚本的一部分,其中显示了行号。我可以理解 getopts 在 bash 中的工作原理,但无法理解第 116 行。if [[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG " ]]; then 部分。在脚本的前面有

#!/usr/bin/env bash
FS_OPTIONS=("ubuntu" "busybox")
while getopts "hsf:" opt; do
    case $opt in
        f)
            if [[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG "  ]]; then
                echo "Unsupported filesystem $OPTARG"
                echo "Use ubuntu/busybox"
                exit -1
            else
                echo "ok!"
                export FILESYSTEM=$OPTARG
            fi
            ;;
    esac
done

ckim@chan-ubuntu:~/testbash$ test3.sh -f ubuntu
ok!
ckim@chan-ubuntu:~/testbash$ test3.sh -f busybox
ok!
ckim@chan-ubuntu:~/testbash$ test3.sh -f ubunt.
Unsupported filesystem ubunt.
Use ubuntu/busybox

我把条件行改成if [[ ! " ${FS_OPTIONS[@]} " =~ $OPTARG ]]; then,可以看到右边是正则表达式。现在我可以看到了

ckim@chan-ubuntu:~/testbash$ test3.sh -f ubunt.
ok!

这是因为 " " 被删除了,并且参数被视为正则表达式。

bash 手册说

另外一个二元运算符 =~ 可用,其优先级与 == 和 != 相同。使用时, 运算符右侧的字符串被视为扩展正则表达式并进行相应匹配 (如正则表达式(3))。如果字符串与模式匹配,则返回值为 0,否则为 1。

这在我的问题之前很久了。我的第一个问题是:根据我上面观察到的情况,当模式匹配时,=~ 返回 1。(它之前有 !)而不是手册。我错过了什么吗?
我的第二个问题:使用原始if [[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG " ]]; then 有哪些用例?利用正则表达式?因为它有“”,所以它将参数作为字符串(不将其作为正则表达式)。 =~ 和 " " 有什么用处吗?

【问题讨论】:

  • (it has ! before) as opposed to the manual. 这怎么“反对”?有! expression True if expression is false
  • 也就是说,请注意关于语言设计的“为什么”问题在这里通常是题外话;见meta.stackexchange.com/a/170415/144918
  • @KamilCuk 例如,当我给-f ubuntu 时,显示它匹配(上面打印了ok)。这意味着第一个 if ( ! ... =~ ... ) 部分没有被占用,这意味着 =~ 部分返回 1。所以我给出了一个匹配模式,但 =~ 返回 1。(手册说它在匹配时返回 0)。我错过了什么吗?
  • (顺便说一句,exit -1 在 shell 中通常没有意义:UNIX 退出状态是一个 无符号 整数;因此,负数总是被转换为正数)。
  • ...另外,[[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG " ]] 不是很好的形式。在不允许字符串扩展为多个单词的上下文中,它应该使用[*] 而不是[@]

标签: bash


【解决方案1】:

你问的具体行是什么意思

分解[[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG " ]] 所做的工作:

  • 在这种情况下," ${FS_OPTIONS[@]} "" ${FS_OPTIONS[*]} " 的等价不太清楚(因为它们之间的通常区别会将[@] 扩展到多个单词,这在这种情况下是不合法的)。因此,像${FS_OPTIONS[*]} 一样,我们将扩展到FS_OPTIONS 中的完整单词列表,并在它们之间添加分隔符(IFS 中的第一个字符,默认情况下为单个空格)。另外请注意,我们在开头和结尾添加了空格(因此我们的 OPTARG 可以在这些位置匹配,而不仅仅是在中间)。
  • " $OPTARG " 用两边的空格填充我们要查找的字符串,因此我们无法匹配子字符串。
  • 在引用右侧的情况下,我们正在执行子字符串搜索,因此我们正在检查 $OPTARG 的内容(在开头和结尾添加了空格)是否存在于完整展开列表中的任何位置由${FS_OPTIONS[*]}生成。
  • ! 反转表达式其余部分的逻辑退出状态(将 0 更改为 1 或将 1 更改为 0)。

该语法一般有什么用处?

  • RHS 上的带引号的字符串充当常规的未锚定 子字符串搜索。也就是说,[[ $foo = "bar" ]] 仅当变量 foo 完全扩展为 bar 时为真,但如果变量 foo 扩展为 contain bar 在其任何位置,[[ $foo =~ "bar" ]] 为真内容。这本身就是一个有用的语义。

  • 引用是逐个字符确定的,而不是完整的字符串。作为如何使用它的示例:

    regex_pre='([[:space:]]|^)'
    literal_string='** match this exactly **'
    regex_post='([[:space:]]|$)'
    
    [[ $foo =~ ${regex_pre}"${literal_string}"${regex_post} ]]
    #          |            |                 |
    #          |            |                 \-> unquoted: acts like a regex
    #          |            \-> quoted: acts like a literal string
    #          \-> unquoted: acts like a regex
    

    ...匹配** match this exactly ** 仅当它位于字符串的开头或紧跟在空格之前,也可以在结尾或紧随其后的是空格,而无需为中间的文字字符串编写正则表达式,并且不需要执行问题中显示的 pad-with-whitespace 技巧。

【讨论】:

  • 哇,非常感谢您的友好解释。最后一部分(regex_pre~regex_post)我现在不能,但我稍后会检查它。(只是因为这里太晚了)。
【解决方案2】:
ckim@chan-ubuntu:~/testbash$ test3.sh -f ubuntu
ok!
ckim@chan-ubuntu:~/testbash$ test3.sh -f ubunt.
Unsupported filesystem ubunt.
Use ubuntu/busybox

bash 手册说(在关于[[...]] 的讨论中):

模式的任何部分都可以被引用以强制将引用的部分匹配为字符串

因此,要将输入视为正则表达式,并在末尾添加文字空格,引用空格但不要引用变量:

[[ " ${FS_OPTIONS[*]} " =~ " "$OPTARG" "  ]]
# .........................^^^-------^^^

演示:

$ OPTARG=ubuntu
$ [[ " ${FS_OPTIONS[*]} " =~ " "$OPTARG" "  ]] && echo Y || echo N
Y

$ OPTARG=ubunt.
$ [[ " ${FS_OPTIONS[*]} " =~ " "$OPTARG" "  ]] && echo Y || echo N
Y

$ OPTARG=ubunt
$ [[ " ${FS_OPTIONS[*]} " =~ " "$OPTARG" "  ]] && echo Y || echo N
N

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多