grep -Po '...\K...'是什么意思?还有其他什么方法可以达到同样的效果吗?

8

我有这个脚本 script.sh

#!/bin/bash
file_path=$1
result=$(grep -Po 'value="\K.*?(?=")' $file_path)
echo $result

还有这个文件 text.txt

value="a"
value="b"
value="c"

当我运行./script.sh /file/directory/text.txt命令时,终端的输出如下:
a b c

我知道这个脚本是做什么的,但我不明白它是如何工作的,所以我需要一份详细的解释关于这部分指令:

-Po 'value="\K.*?(?=")'

如果我理解正确,\K是Perl命令。你能否给我一个在shell中的替代方案(例如使用awk命令)?
提前感谢。

2
请翻译以下与编程有关的内容:每个问题只回答一次。返回仅翻译的文本: - jwodder
@jwodder 我已经编辑过了。 - Ordinary User
1
你有没有尝试阅读man grep并查找-P-o-P的描述实际上很可能会让其他内容变得显而易见。 - Charles Duffy
1
顺便说一下,我已经编辑了问题标题,使其与所提出的问题具体相关。请在以后尝试自己这样做。 - Charles Duffy
另外一件事 - 没有引用$file_path实际上意味着你有一些错误 - 传递一个位于名为“我的文档”的目录中的文件名将会出现问题。始终引用您的扩展:"$file_path" - Charles Duffy
1个回答

10
  • grep -P 启用PCRE语法。这是一种非标准扩展——甚至不是所有GNU grep的版本都支持它,因为它依赖于可选的libpcre库,是否链接到此库是编译时选项。
  • grep -o 仅在输出中发出匹配文本,而不是包含该文本的整行。(这也是非标准的,但比-P更广泛地可用)。
  • \K 是PCRE扩展到regex语法,从匹配输出中排除该点之前的内容。

由于您的shell是bash,因此您内置了ERE支持。作为一种只使用内置功能(没有外部工具、grepawk或其他)的替代方法:

#!/usr/bin/env bash
regex='value="([^"]*)"'                    # store regex (w/ match group) in a variable
results=( )                             # define an empty array to store results
while IFS= read -r line; do             # iterate over lines on input
  if [[ $line =~ $regex ]]; then        # ...and, when one matches the regex...
    results+=( "${BASH_REMATCH[1]}" )   # ...put the group's contents in the array
  fi
done <"$1"                              # with stdin coming from the file named in $1
printf '%s\n' "${results[*]}"           # combine array results with spaces and print

请参见 http://wiki.bash-hackers.org/syntax/ccmd/conditional_expression 以了解关于=~的讨论,和http://wiki.bash-hackers.org/syntax/shellvars#bash_rematch 以了解关于BASH_REMATCH的讨论。请参见 BashFAQ #1 以了解如何使用while read循环逐行读取文件的讨论。

难道不应该是 value="([^"]*)"' 来模拟非贪婪匹配吗? - Benjamin W.
@CharlesDuffy,您已将result变量用作数组。您能否编辑您的代码,使得result变量与此相同类型:result=$(grep -Po 'value="\K.*?(?=")' $file_path)(我猜是一个字符串)?对不起我的英语。 - Ordinary User
@OrdinaryUser,我故意使用了一个数组--如果你要像echo $result那样使用你的结果,那么你就会将它分割成单词,并将由分割操作生成的每个单词作为全局变量进行评估。这是一种本质上容易出错的操作,这意味着你无法区分一个值为“hello world”的情况和两个不同值的情况,其中第一个是“hello”,第二个是“world”。使用数组,边界划分是已知且固定的,你总是可以从该数组转换为字符串。 - Charles Duffy
@普通用户,...因此,例如:if (( ${#results[@]} )); then printf -v result '%s\n' "${results[@]}"; else result=''; fi将生成一个名为result的单个字符串,其中边界由换行符分隔。 echo $result不会显示这些换行符,但是echo "$result"会--这可能是原始代码的最佳并行处理方式。 - Charles Duffy
非常好的建议,适用于Bash替代GNU核心工具。 - Cymatical
@Cymatical,即使在使用GNU工具的系统上也可能需要这个;GNU grep对libpcre(用于提供“-P”)的依赖是编译时选项,可以关闭。 - Charles Duffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接