如何在 sed 脚本中使用 sed 删除括号内的字符串?

Posted

技术标签:

【中文标题】如何在 sed 脚本中使用 sed 删除括号内的字符串?【英文标题】:How can I delete a string inside parentheses using sed, inside a sed script? 【发布时间】:2021-12-12 18:21:58 【问题描述】:

我想在 sed 脚本中使用 sed 删除括号内的文本(包括括号)。例如,我想删除短语 (Chris Pratt) 和 (Chris-Pratt) 并保留 (Chris_Pratt)。 (他们都在同一条线上)。并对整个文件执行此操作。例如,该行如下所示:

Star Lord (Chris Pratt), (Chris-Pratt), age 42, actor, (Chris_Pratt)

这是我希望在 sed 脚本中执行 sed 命令后的样子:

Star Lord, age 42, actor, (Chris_Pratt)

这就是我想要对每一行执行的操作(有多行具有其他名称)。

我已经试过了:

s/[(][^)]*[)]//g

这个可行,但它也删除了括号包括下划线,还有:

s/\([[:alpha:]]1, [[:alpha:] ]1,\)\ //g

当我在命令行中正常使用 sed 运行它时,它确实有效,但由于某种原因在脚本中运行它时它不起作用。

【问题讨论】:

【参考方案1】:

你可以使用

sed 's/ *([^()_]*)//g' file > outputfile

与 POSIX ERE 语法相同的模式:

sed -E 's/ *\([^()_]*\)//g' file > outputfile

详情

* ( - 文字 ( 字符(因为它是 POSIX BRE 模式),当使用 POSIX ERE 时,必须使用 \( [^()_]* - 除了()_ 之外的零个或多个字符 ) - 文字 ) 字符(因为它是 POSIX BRE 模式),当使用 POSIX ERE 时,必须使用 \)

见online demo:

#!/bin/bash
s='Star Lord (Chris Pratt), age 42, actor, (Chris_Pratt)'
sed 's/ *([^()_]*)//g' <<< "$s"
# => Star Lord, age 42, actor, (Chris_Pratt)
sed -E 's/ *\([^()_]*\)//g' <<< "$s"
# => Star Lord, age 42, actor, (Chris_Pratt)

演示截图:

【讨论】:

嗯。当我运行这个命令时,它会删除我文件中的所有文本,只留下括号和下划线。不确定发生了什么? @JoshuaBorden 你没有使用我的代码。 我确实在使用确切的代码。你在说什么? @JoshuaBorden 如果您使用sed 's/ *([^()_]*)//g' file &gt; outputfileoutputfile 将具有预期的文本。 @JoshuaBorden 那么,它现在是否适用于 POSIX ERE 模式?您必须有-r-E 选项。【参考方案2】:

第一个解决方案(sed 解决方案): 使用您展示的示例,请尝试遵循sed 程序。在此处使用sed 的反向引用功能。

sed -E 's/(^[^(]*) \([^)]*\), \([^)]*\)(.*)/\1\2/' Input_file

解释:在这里使用sed-E 选项在我们的程序中启用ERE(扩展正则表达式)。然后在主程序中使用seds 选项进行替换操作。我们提到了(^[^(]*) \([^)]*\), \([^)]*\)(.*),它在这里创建了 2 个反向引用(内存中的一个临时空间,用于稍后在程序中检索捕获的值)。同时使用\1(第一个反向引用)和\2(第二个反向引用)来获得OP提到的预期输出。

正则表达式的解释:

(^[^(]*)               ##Creating 1st capturing group which captures values from starting of line to till 1st occurrence of ( here.
 \([^)]*\), \([^)]*\)  ##Matching space ( till next occurrence of ) here followed by comma then space followed by ( till next occurrence of ).
(.*)                   ##Creating 2nd capturing group which has everything after previous match.


第二个解决方案(awk 解决方案): 在这里添加了一个awk 解决方案,使用awkmatch 函数。使用的正则表达式与上面提到的相同,因此对该程序的简单解释是;通过此功能匹配不需要的部分并仅打印需要的部分。

awk '
match($0,/[[:space:]]*\([^)]*\),[[:space:]]*\([^)]*\)/)
  print substr($0,1,RSTART-1) substr($0,RSTART+RLENGTH)

' Input_file

【讨论】:

很抱歉我忘了提这个!还有一个带有“-”的括号,就像这样“(Earth-16)”我也需要摆脱它。您的代码完美运行。有没有办法删除那个也有“-”的? @JoshuaBorden,肯定不是问题,请尝试以下代码:sed -E 's/(^[^(]*) \([^)]*\), \([^)]*\)(.*)/\1\2/' Input_file 一次,然后告诉我进展如何,干杯。【参考方案3】:

这可能对你有用(GNU sed):

sed -E ':a;s/ *\([^()]*\),(.*\(.*\))/\n\1/;ta;s/,*\n+/,/g' file

用换行符替换括号中的所有文字(包括括号),前面有一个可能的空格,后面是,,后面至少还有一个括号内的文字。

然后用单个, 替换前面可能有, 的多个换行符。

【讨论】:

【参考方案4】:

使用sed

$ sed 's/,\? ([^)][A-Za-z -]*)//g' input_file
Star Lord, age 42, actor, (Chris_Pratt)

【讨论】:

【参考方案5】:

您应该澄清您是否真的在为“扩展”正则表达式使用-E选项。

启用-E 后,括号 (()) 必须转义 (\() 以匹配字面意思。没有转义,它们被解释为组/字段分隔符(没有-E,这是相反的)。

我的解决方案假设 -E 已开启。

对于一个可执行的sed 脚​​本,你的shebang 应该类似于#!/bin/sed -Ef#!/usr/bin/env -Ssed -Ef

对于不可执行的sed 脚本,像这样调用它:sed -Ef my-script

解决方案:

如果所有行都具有相同的布局,请使用:

s/([[:space:]]+\([^)]+\),)2/,/

如果布局不同,请尝试以下:

s/[[:space:]]*\([^)]*[- ][^)]+\)//
s/[[:space:]]*\([^)]*[- ][^)]+\),?//g

所有带括号的字段包含一个空格和/或破折号被删除,还有前导空格和尾随逗号(如果存在)。仅对于第一个匹配项,所有逗号都会保留。

s/[[:space:]]*\([^)_]+\)//
s/[[:space:]]*\([^)_]+\),?//g

所有包含下划线的括号字段都将被删除(包括(Madonna))。用空格和逗号同上(如上)。

编辑:我稍微误读了您的预期输出。你会得到Star Lord age 42, actor, (Chris_Pratt) 而不是Star Lord, age 42, actor, (Chris_Pratt)(注意第一个逗号)。我相应地修复了解决方案。

【讨论】:

以上是关于如何在 sed 脚本中使用 sed 删除括号内的字符串?的主要内容,如果未能解决你的问题,请参考以下文章

sed

sed多行文本处理

如何在 shell 脚本中使用 sed 从文件的每一行中删除单词? [复制]

linux下shell脚本中sed命令的用法

sed的分组用法

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