在脚本中提取正则表达式捕获组

Extract Regex Capture Group in Script

提问人:Sarah Weinberger 提问时间:11/14/2023 最后编辑:Wiktor StribiżewSarah Weinberger 更新时间:11/18/2023 访问量:75

问:

我正在编写一个 CSH 脚本并尝试从给定键的源字符串中提取文本。

!/bin/csh -f
set source = "Smurfs\n\tPapa\nStar Trek\n\tRenegades\n\tStar Wars\n\tThe Empire Strikes Back\n"
set toFind = "Star Trek"
set regex = "$toFind[\s]*?(.*?)[\s]*?"
set match = `expr $source : $regex`
echo $match

上面的代码不起作用,所以我遗漏了一些东西。我尝试将“星际迷航”目录放在里面而不是变量。我应该看到答案。如果我把“星球大战”而不是“星际迷航”放在一起,我应该看到.RegenagesThe Empire Strikes Back

谷歌搜索显示了一个使用 grep 的可能解决方案,例如

match = `grep -Po '<something>' <<< $source

我不知道该放什么,我也不是grep的专家。<something>

在实际代码中,我正在从文件中读取文本。我只是在这里简化了事情。

思潮?

sed grep csh

评论

0赞 CAAHS 11/14/2023
grep 用于匹配,sed 能够编辑流,这是一个很好的介绍: grymoire.com/Unix/Sed.html - 也有关于如何与 shell 脚本(包括 CSH)结合的示例。
1赞 Sarah Weinberger 11/14/2023
@mandy8055 你的 bash 脚本返回“星际迷航”而不是“叛徒”,所以直接写不。话虽如此,我对 bash 解决方案持开放态度,尽管仍然会保留我最初的问题,因为我很好奇在 csh 中是否有可能的解决方案。
0赞 shellter 11/15/2023
“想法?”...你的reg-ex看起来非常像perl reg-ex(但我没有这方面的经验)。所以,如果这是一个perl-reg ex,你可以肯定,除非你有一个支持perl-regex的版本,否则它永远不会起作用。但是现在我正在阅读您最初的问题描述,“试图从给定键的源字符串中提取文本”。??键/值。你为什么要使用这样一个无用的解决方案?为什么不,甚至只是?啊,“我正在阅读文件中的文本”,把它放在你的 Q 顶部附近可能会有所帮助.....。exprkey[str]="value"myKey=Renegades
0赞 shellter 11/15/2023
接着,正如你所说”。我只是在这里简化了事情。我宁愿把时间花在将 2 行输入转换为变量赋值上,但似乎您必须处理 var-names 中的空格,所以 nix 到 )-; .做快速研究,我看不出可以做数组,只能做数组,然后引用为等。如果你正在处理一个带有扩展实用程序的文件,这很好,但会给你更易于理解的代码。现在很忙,所以这就是我现在能想到的。cshStar Trek="Renegade"csharr[key]="value"set arr = (one two three)echo $arr[1] $arr[3]sedawk
0赞 shellter 11/15/2023
回到 perl-regex 这个东西,有一小组 perl-regexps 特殊语法可以用长手基本正则表达式重写。我不得不相信该实用程序仅使用基本正则表达式,但它没有记录在 的版本中。(也许在?您知道使用是一只手在背后计时的 shell 脚本吗?好吧,作为一个学习挑战,但是工作/工作,你会做得更好,或者或者更新的东西(鱼?(搜索ERE是我能找到的最好的)。祝你好运。exprGNU coreutils 8.30man exprinfo '(coreutils) expr invocation'cshbashzshman grep

答:

0赞 Sarah Weinberger 11/15/2023 #1

以下是我问题的字面答案,因为我问了 csh 的问题,但是我使用 bash 编写了一个解决方案。

匹配正则表达式捕获组

匹配空格 如何在 Bash 中将空格与正则表达式匹配?

我使用Tutorial Point进行调试。

mystring1='  asdf1@wxyz2  @@a!s#d@f@@  asdf2@wxyz2 b!t#e@g '

tofind='asdf1@wxyz2'
regex="${tofind}[[:space:]]*([.!@\#a-zA-Z0-9]+)"

[[ $mystring1 =~ $regex ]]

echo $'\n'
echo $'\n'
echo '***********************'
echo ${BASH_REMATCH[1]}
echo '***********************'

评论

0赞 Ed Morton 11/16/2023
mystring1=' asdf1@wxyz2 @@a!s#d@f@@ asdf2@wxyz2 b!t#e@g '与问题中的文本不同,采用换行符+制表符分隔的格式。这是对与您提出的问题不同的问题的可能答案。set source = "Smurfs\n\tPapa\nStar Trek\n\tRenegades\n\tStar Wars\n\tThe Empire Strikes Back\n"
0赞 Sarah Weinberger 11/15/2023 #2

真正的解决方案使用文件作为源,因此:

set valueCapture=`cat /mypath/filename | grep -A1 "${tofind}" | grep -v "${tofind}" | xargs`

从字符串中查找捕获值的代码应为(未对其进行测试):

set valueCapture=`cat $source | grep -A1 "${tofind}" | grep -v "${tofind}" | xargs`

在这两种情况下,我希望找到的是:

设置为 find='asdf1@wxyz2'

零件将修剪掉空格。xargs

评论

0赞 Ed Morton 11/16/2023
当您几乎应该进行整行或全字段字符串匹配时,这是在整行中执行部分正则表达式匹配,如果两行中都出现相同的目标字符串,则会失败。
0赞 Paul Hodges 11/18/2023
此外,UUoC.放下并回声“$source”cat”。cat filegrep -A1 "${tofind}" file. From a string you might use , but not
0赞 Ed Morton 11/16/2023 #3

既然你说你的实际输入在一个文件中,那么下面是你输出的文件:printf

$ cat file
Smurfs
        Papa
Star Trek
        Renegades
        Star Wars
        The Empire Strikes Back

以下是匹配和打印您想要的字符串的方法:

$ awk -v tgt='Star Trek' '{gsub(/^[[:space:]]+|[[:space:]]+$/,"")} $0==tgt{n=NR+1} NR==n' file
Renegades

$ awk -v tgt='Star Wars' '{gsub(/^[[:space:]]+|[[:space:]]+$/,"")} $0==tgt{n=NR+1} NR==n' file
The Empire Strikes Back

请参阅为什么使用-shell-loop-to-process-text-considered-bad-practice

0赞 Paul Hodges 11/18/2023 #4

流水线可以做到,虽然不如 Ed 的单一进程。awk

$: toFind="Star Wars"; echo "$source" |  grep -EA1 "$toFind" | tail -1
        The Empire Strikes Back

$: toFind="Star Trek"; echo "$source" |  grep -EA1 "$toFind" | tail -1
        Renegades

$: echo "$source">file; toFind="Star Trek"; grep -EA1 "$toFind" file | tail -1
        Renegades

A 会起作用。sed

$: toFind="Star Trek"; sed -n "/$toFind/{n
                                         p}" file # should work with any version
        Renegades

$: toFind="Star Wars"; sed -n "/$toFind/{n;p}" file # semicolon is GNU
        The Empire Strikes Back

所有这些可能都值得细化您的正则表达式。

$: toFind="Star"; sed -n "/$toFind/{n;p}" file
        Renegades
        The Empire Strikes Back

$: toFind="Star"; sed -n "/^$toFind$/{n;p}" file

$: toFind="Star Trek"; sed -n "/^$toFind$/{n;p}" file
        Renegades

$: toFind="Star Wars"; sed -n "/^$toFind$/{n;p}" file # fails because of the leading tab

最后一个可能意味着您必须允许第一个。
测试你的逻辑。