BASH:如何在 sed 命令中对字符串使用 Regex Negative Lookahead?

Posted

技术标签:

【中文标题】BASH:如何在 sed 命令中对字符串使用 Regex Negative Lookahead?【英文标题】:BASH: How to use Regex Negative Lookahead in sed command for a string? 【发布时间】:2019-06-01 17:03:01 【问题描述】:

我还没有找到一种方法在 sed 命令中使用这个正则表达式 - .+?(?=,) 来提取这个字符串的一部分(使用第一个字符实例的 Lookbehind)。

用简单的英语,我想提取位于第一个逗号之前的字符串部分。由于我计划将来提取字符串的特定文件名,因此我不能依赖 cut 命令(我最终将不得不使用 sed 命令),:-

name='ERROR 1: /home/rphillips/Desktop/empties/BN23_2303.tif, band 1: 无法计算统计数据,采样中找不到有效像素。'

这些是我使用过的变体,包括一个有效的测试 - sed 's/band/rose/'。然而,我使用的其他变体(如下所示)将空格作为输出。

while read -r line; do
    name="$line"
    echo $name
    #file_path=$(echo $name | cut -d "," -f 1)
    #file_path=$(echo $name | sed -e '/s\/.+?(?=,)///')
    #file_path=$(echo $name | sed 's/band/rose/')
    file_path=$(echo $name | sed '/s\/.+?(?=, )///')
    #file_path=$(echo $name | grep -P '.+?(?=,)')
    #file_path=$(echo $name | sed 
    #file_path=$(echo $name | awk '/.+?(?=,)/print $name'
    echo $file_path
done < "$filename"

预期结果 - 错误 1:/home/rphillips/Desktop/empties/BN25_2303.tif

实际结果 - '很多空间'

我还注意到,根据 Regex101 网站,我使用的正则表达式具有不同的“匹配”,具体取决于我是在 Windows 上使用 Firefox 还是 Ubuntu 16.04LTS

Windows - https://regex101.com/r/WWGf8F/1 Ubuntu - https://regex101.com/r/NpL2Oa/1

我不确定这是否导致 sed -e 无法识别表达式?

我已经将这些引用用于上面代码中使用的不同表达式

https://likegeeks.com/regex-tutorial-linux/

How to match "anything up until this sequence of characters" in a regular expression?

https://www.regular-expressions.info/lookaround.html?wlr=1

https://linux.die.net/man/1/sed

【问题讨论】:

sed 's/,.*//' 将带来所需的输出,尽管我不确定这是否是您想要做的。你会用 English 指定你想要执行的过程吗,因为你的命令sed '/s\/.+?(?=, )///' 不起作用并且不清楚你想要做什么。请注意,我不是反对者。 同意,sed 's/,.*$//'sed 's/^\([^,][^,]*\).*$/\1/'。要么做你想做的事。 (或grep -o '^[^,]*'awk -F, 'print $1' sed 仅保证支持 BRE(“POSIX 基本正则表达式”),并且许多版本还提供了访问 ERE 语法的扩展。 Lookahead 和lookbehind 是PCRE 扩展,不是任何一个标准的一部分。见pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html 顺便说一句,echo $name 本质上是错误的——参见BashPitfalls #14。按优先顺序使用&lt;&lt;&lt;"$name"printf '%s\n' "$name"echo "$name"带引号 ...你不需要sed 来做一些简单的事情,比如在字符串中修剪逗号后面的所有内容。如果string=foo,bar,则$string%%,* 将评估为foo 【参考方案1】:

用简单的英语我想提取字符串中存在的部分 在第一个逗号之前。因为我计划在未来提取 字符串的特定文件名,我不能依赖 cut 命令(我 最终将不得不使用sed 命令)

输入字符串

ERROR 1: /home/rphillips/Desktop/empties/BN23_2303.tif, band 1: Failed to compute statistics, no valid pixels found in sampling.

预期结果

ERROR 1: /home/rphillips/Desktop/empties/BN25_2303.tif

在我们了解您的sed 命令不起作用的可能原因之前,让我们看看您上面的实际问题。如果您只是想提取第一个逗号之前的文本,那么您只需要:

sed 's/,.*//'

(简单地说删除从第一个逗号到结尾的所有内容)

您也可以使用反向引用(这将有助于实现提取文件名的最终目标),例如

sed 's/^\([^,][^,]*\).*$/\1/'

(表示'^' 从开头开始,\([^,][^,]*\) 捕获至少 1 个非逗号字符的所有文本,包括零个或多个非逗号字符,.*$ 丢弃所有文本到结尾和\1 仅使用反向引用替换捕获的文本)

要达到仅提取文件名的目标,您只需修改上述内容以使用第一个正斜杠开始捕获,例如

sed 's/^[^/]*\([^,][^,]*\).*$/\1/'

使用/输出示例

$ sed 's/^[^/]*\([^,][^,]*\).*$/\1/' <<< $name
/home/rphillips/Desktop/empties/BN23_2303.tif

我不确定这是否会导致表达式无法被 sed -e?

sed 没有-E (--regexp-extended) 选项使用基本 正则表达式(不包括向后或向前)。

如果您打算使用逗号分隔值的剩余字段,您可能需要考虑使用awk 来解析这些字段。您可以轻松获取所有指定 -F 字段分隔符的字段和一个简单的循环。

$ awk -F', ' 'for (i = 1; i <= NF; i++) printf "field %d - %s\n", i, $i' <<< $name
field 1 - ERROR 1: /home/rphillips/Desktop/empties/BN23_2303.tif
field 2 - band 1: Failed to compute statistics
field 3 - no valid pixels found in sampling.

(您也可以在循环中使用条件进一步解析每个字段)

在 Bash 中 - 只需要参数扩展

不要只见树木不见森林,因为您指定了bash,如果您只是想从name 中提取文件名,您所需要的只是带有子字符串删除的参数扩展 (先从右,再从左),例如

tmp=$name%%,*    ## trim to (and including) the 1st comma from the right
echo "/$tmp#*/"  ## trim to and including the first / from the left
/home/rphillips/Desktop/empties/BN23_2303.tif

(一种更有效的方法)

查看一下,如果您还有其他问题,请告诉我。

【讨论】:

以上是关于BASH:如何在 sed 命令中对字符串使用 Regex Negative Lookahead?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 BASH 中的 sed 命令从 Json 文件中删除 Json 对象

如何在bash脚本中使用Bash / Sed / Awk / Perl删除分隔字符串的最后一个元素[duplicate]

如何在bash中对逗号分隔的值进行排序?

如何在bash中将空格替换为新行[重复]

sed命令和正则表达式

bash 中 sed 未终止的 s 命令出错