【问题标题】:Bash Regex Pattern Matching: How To Use Variables Inside of Pattern Variable?Bash 正则表达式模式匹配:如何在模式变量中使用变量?
【发布时间】:2022-01-18 14:03:56
【问题描述】:

我正在尝试 a) 扫描字符串以查找模式匹配 b) 将匹配项保存到列表 c) 过滤匹配原始字符串。

无论我使用什么正则表达式,它都不会匹配。我认为问题在于我在正则表达式模式变量中使用变量的方式,和/或我定义 [space] 的方式。

疑似问题代码:

trig_chars='\s\s' # Double space # Can be numbers, spaces, a-z, A-Z, or special chars.
        
pat_before_after="^[^\s]$trig_chars[^\s]" # Any characters before or after trig chars until space or beginning/end of query hit.
    
if [[ "$accum" =~ "$pat_before_after" ]] ; then  # Trig char found.  

我是否在 $pat_before_after 变量中定义了 $trig_chars?我是否在正则表达式比较语句中正确使用了变量?

更多上下文:

这就是我想要做的:

示例:

raw_query='A[space][space] 这是一个 Bcd[space][space] 测试 e$F[空格][空格]查询[空格][空格] G'

hotstrings=('A' 'Bcd' 'e$F' 'G')

filtered_query='这是一个测试查询'

从 $raw_query 获取 $hotstrings 和 $filtered_query。

这是我的尝试:

递归搜索每个字符串字符,直到找到匹配项。删除匹配。并根据不匹配的字符构建过滤后的查询变量。

(我怀疑这不是这样做的方法,但没有成功通过其他尝试使测试通过。)

这是完整上下文中的相关代码:

#!/bin/bash
# Gets hotstrings from raw query.
# Returns hotstrings and filtered query, with trigger characters and hotstrings removed.

#Global vars
raw_query='' # User input thru Zenity
# trig_chars='  ' # Double space
trig_chars='\s\s' # Double space 
hotstrings=() # Any chars in raw_query before/after trig chars until space or beginning/end of query hit.
filtered_query='' # Raw_query with hotstrings & trig chars removed.

pat_before_after="^[^\s]$trig_chars[^\s]" # Any characters before or after trig chars until space or beginning/end of query hit.

# Scans raw query for trig chars & hotstrings.
# Filters trig chars & hotstrings from filtered_query var.
get_hotstrings(){
    # Guard clause edge case, no trigger chars defined.
    if [ "$trig_chars" == "" ]; then 
        filtered_query="$raw_query"
    return; fi

    local scan_text="$raw_query"
    local accum='' # Used to accumulate unmatched chars.
    filtered_query='' # Blank out filtered query before scan.

    while [ "$scan_text" != "" ]; do # Recursive search thru each char.
        local next_char=${scan_text:0:1}
        local accum="$accum$next_char" 

        if [[ "$accum" =~ "$pat_before_after" ]] ; then  # Trig char found.  

            local l=${#accum}
            local scan_text=${scan_text:$l} # Remove chars & hotscan_texts from scan_text.

            hotstring=${BASH_REMATCH[1]} # Get hotstring from pattern match.
             
            hotstrings+=("$hotstring") # Update hotstrings list
            local accum='' # Start new accumulator.
        else
            filtered_query="$filtered_query$next_char" # Build the filtered query from non-matched chars.
            scan_text=${scan_text:1} # remove char.
        fi
    done
    
}


################### TESTS ##################

test_messages(){
    #$1=test_name
    #$2=expected result.
    #$3=actual result.

    msg="Test name:$1
    Expected result:$2
    Actual result:$3"

    if [ "$2" == "$3" ]; then
        msg2='SUCCESS'
    else
        msg2='FAILED'
    fi
    echo "$msg"
    echo "$msg2"
}

hotstring_scan_test(){
    #$1=Test name
    #$2=raw query
    #$3=trig chars
    #$4=Expected hotstrings.
    #$5=Expected filtered query.

    raw_query="$2"
    trig_chars="$3"
    
    get_hotstrings
    r="${hotstrings[*]}"
    
    msg1="Test name: $1 
    Raw query: $2 
    Trig chars: $3" 

    if [ "$r" == "$4" ]; then
        msg2="SUCCESS"
    else
        msg2="Expected hotstring result:$4 
        Actual hotstring result:$r
        FAILED"
    fi
    echo "$msg1"
    echo "$msg2"
    echo ""

    msg1="Filtered query test:
    Filtered query: $filtered_query"

    if [ "$filtered_query" == "$5" ]; then
        msg2="SUCCESS"
    else
        msg2="Expected result:$5 
        Actual result:$filtered_query 
        FAILED"
    fi
    echo "$msg1"
    echo "$msg2" 
    echo ""
}
    
hotstring_scan_tests(){
    local t_name='No trigger chars defined'
    local q='Test query without hotstrings or trigger chars'
    raw_query="$q"
    local eq='Test query without hotstrings or trigger chars'
    local tc=''
    local eh=''
    hotstring_scan_test "$t_name" "$q" "$tc" "$eh" "$eq"
    filtered_query=''

    local t_name='No trigger chars defined'
    local q='Test query without hotstrings or trigger chars'
    raw_query="$q"
    local eq='Test query without hotstrings or trigger chars'
    local tc='**'
    local eh=''
    hotstring_scan_test "$t_name" "$q" "$tc" "$eh" "$eq"
    filtered_query=''

    local t_name='Double space trigger chars'
    local q='  aabc Test   b Query with Hotstrings removed   c'
    raw_query="$q"
    local eq='Test Query with Hotstrings removed'
    local tc='  '
    local eh='aabc b c'
    hotstring_scan_test "$t_name" "$q" "$tc" "$eh" "$eq"
    filtered_query=''

    local t_name='Double space after trigger chars'
    local q='aabc   Test b   Query with Hotstrings removed c  '
    raw_query="$q"
    local eq='Test Query with Hotstrings removed'
    local tc='  '
    local eh='aabc b c'
    hotstring_scan_test "$t_name" "$q" "$tc" "$eh" "$eq"
    filtered_query=''


    local t_name='Trigger chars before hotstring'
    local q='**aabc Test **b Query with Hotstrings removed **c'
    raw_query="$q"
    local eq='Test Query with Hotstrings removed'
    local tc='**'
    local eh='aabc b c'
    hotstring_scan_test "$t_name" "$q" "$tc" "$eh" "$eq"
    filtered_query=''

    local t_name='Trigger chars after hotstring'
    local q='aabc** Test b** Query with Hotstrings removed c**'
    raw_query="$q"
    local eq='Test Query with Hotstrings removed'
    local tc='**'
    local eh='aabc b c'
    hotstring_scan_test "$t_name" "$q" "$tc" "$eh" "$eq"
    filtered_query=''

    local t_name='Trigger chars before & after hotstring'
    local q='aabc** Test **B$! Query with Hotstrings removed **c123'
    raw_query="$q"
    local eq='Test Query with Hotstrings removed'
    local tc='**'
    local eh='aabc B$1! c123'
    hotstring_scan_test "$t_name" "$q" "$tc" "$eh" "$eq"
    filtered_query=''


}

performance_test(){
    start=$(($(date +%s%N)/1000000))

    for i in {1..10000}; do
        run_all_tests
    done

    end=$(($(date +%s%N)/1000000))
    execution_time=$(expr $end - $start)

    echo "Execution time in nano seconds: $execution_time"
}

run_all_tests(){
    set -x
    hotstring_scan_tests

}

run_all_tests
# performance_test

【问题讨论】:

  • 这有帮助吗(来自 bash 手册):如果模式存储在 shell 变量中,引用变量扩展会强制将整个模式作为字符串匹配

标签: regex bash regex-group


【解决方案1】:

根据man bash 使用"(双引号)和"$pat_before_after" 将强制整个字符串与变量中的模式匹配。

在同一个man bash中,模式是man 7 regex中定义的正则表达式:我的意思是\s不是空格的占位符,尝试[[:blank:]][[:space:]]代替(EDIT 见 cmets)。

一个测试(EDIT参见 cmets):

trig_chars='[[:space:]][[:space:]]'
pat_before_after="^[^[:space:]]${trig_chars}[^[:space:]]"
accum='f  daaa'
if [[ $accum =~ $pat_before_after ]] ; then
  printf 'Match!\n'
fi

结果:

Match!

【讨论】:

  • 不错。如果 OP 中的 \s[[:space:]] 的替代品,如经常看到的那样,请将 blank 替换为 space。注意:[[ ... ]] 中不需要双引号和大括号。
  • 超级!谢谢你!我没有找到这些信息,我应该阅读什么?
  • 但我想说的是,您并不能通过在早上喝咖啡时从头到尾阅读 bash 手册来真正了解所有这些。有一天,您看到一个您不知道或直觉上认为很奇怪的令人惊讶的构造,您在手册中挖掘了一下并发现了一个新方面。大多数实用程序相同(awksedgrepfind...)
  • 使用${var} 而不仅仅是$var,并且在[[ ]] 中使用双引号变量也不错,只是没有必要(=~ 右侧的双引号模式除外)或= 比较)。即使没有必要,我也倾向于使用双引号,因为准确记住何时可以安全地省略双引号比总是使用它们更麻烦。
  • @GordonDavisson 是的,我同意。这是你记住的知识量和代码的优雅之间的一种权衡。我的意思是${var} 而不是$var 很好,但(( "${n}" * "${m}" )) 而不是(( n * m )) 对我来说有点太多了。所以我很高兴始终记住,例如,在 [算术评估] 表达式中,shell 变量也可以通过名称引用而不使用参数扩展语法,也就是说,也可以不使用引号和花括号。
猜你喜欢
  • 1970-01-01
  • 2016-02-07
  • 2015-07-06
  • 2016-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多