【问题标题】:What does grep -Po '...\K...' do? How else can that effect be achieved?grep -Po '...\K...' 有什么作用?怎样才能达到这种效果?
【发布时间】:2017-06-13 14:39:57
【问题描述】:

我有这个脚本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 命令)?


提前谢谢你。

【问题讨论】:

  • 每个问题一个问题,请。
  • @jwodder 我已经编辑过了。
  • 您是否尝试过阅读man grep,并寻找-P-o-P 的描述实际上很可能使其余部分变得相当明显。
  • 顺便说一句,我已经编辑了问题标题以使该标题特定于所提出的问题。以后请尝试自己做。
  • 另外——没有引用$file_path实际上意味着你有一些错误——传递一个名为My Documents的目录中的文件名会很糟糕。始终引用您的扩展:"$file_path"

标签: linux bash shell


【解决方案1】:
  • grep -P 启用 PCRE 语法。 (这是一个非标准的扩展——甚至不是所有的 GNU grep 版本都支持它,因为它取决于可选的 libpcre 库,并且是否链接它是一个编译时选项)。
  • grep -o 在输出中仅发出匹配的文本,而不是包含所述文本的整行。 (这也是非标准的,但比 -P 更广泛可用)。
  • \K 是正则表达式语法的 PCRE 扩展,在匹配输出中包含该点之前丢弃内容。

由于您的 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,有关BASH_REMATCH 的讨论,请参阅http://wiki.bash-hackers.org/syntax/shellvars#bash_rematch。有关使用 while read 循环逐行读取文件的讨论,请参阅 BashFAQ #1

【讨论】:

  • 不应该是value="([^"]*)"'来模仿不贪婪吗?
  • @CharlesDuffy 您已将结果变量用作数组。您能否编辑您的代码,使结果变量与此类型相同:result=$(grep -Po 'value="\K.*?(?=")' $file_path)(我猜是一个字符串)?对不起我的英语
  • @OrdinaryUser,我非常有意地使用了一个数组——如果您要使用像echo $result 这样的结果,那么您将对其进行字符串拆分(在空白字符上转换为单词)并评估由拆分操作生成的每个单词都是一个 glob。这是一个天生容易出错的操作,这意味着您无法区分拥有一个值"hello world" 和两个单独的值,其中第一个是hello,第二个是world。使用数组,边界划分是已知的并且是固定的,以后您总是可以从该数组一个字符串。
  • @OrdinaryUser, ...so,例如:if (( ${#results[@]} )); then printf -v result '%s\n' "${results[@]}"; else result=''; fi 将生成一个名为 result 的字符串,其边界由换行符分隔。 echo $result 不会显示这些换行符,但 echo "$result" 会 - 这可能是原始代码的最佳并行。
  • 对 bash 的非常非常好的建议只能替代 GNU 核心工具。
猜你喜欢
  • 2017-10-04
  • 2020-12-02
  • 1970-01-01
  • 1970-01-01
  • 2023-03-26
  • 2014-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多