如何跳过模式的第一次出现并从第二次出现中替换字符串的其余部分

时间:2018-08-02 05:45:56

标签: perl awk sed

我想跳过第一次出现的模式(在此示例中为_),然后将包含模式的字符串的其余部分替换为tab。例如:

T004_7_entry_00007_conf_01

所需的输出:

T004_7

我在sed中尝试了以下方法:

sed 's/_.*\t/\t/'

3 个答案:

答案 0 :(得分:3)

对于给定的样本,有多种解决方法

$ echo 'T004_7_entry_00007_conf_01' | sed 's/\(_[^_]*\).*/\1\t/'
T004_7  
  • \(_[^_]*\)是用于保存第一次出现的捕获组
    • \(\)是用于指定捕获组的元字符,如果使用ERE,则不需要\
    • [^_]*表示零个或多个非_字符,因此该解决方案仅适用于单个字符定界符
  • \1是对第一个捕获组的反向引用
  • 有关正则表达式的更多详细信息,请参见manual

其他方式:

$ echo 'T004_7_entry_00007_conf_01' | perl -pe 's/_[^_]*\K.*/\t/'
T004_7  

$ echo 'T004_7_entry_00007_conf_01' | awk -F_ '{print $1 FS $2 "\t" }'
T004_7  

如果分隔符不是单个字符,则基于字段的解决方案更合适

$ echo 'foo_:_baz_:_123_:_456' | awk -F_:_ '{print $1 FS $2 "\t" }'
foo_:_baz   

答案 1 :(得分:0)

请您尝试以下。

sed 's/\([^_]*\)\(_[^_]*\)\(.*\)/\1\2\t/'  Input_file

如果要将输出保存到Input_file本身,请使用ii.bak(备份以前的Input_file)。

使用awk

awk 'match($0,/^[[:alnum:]]+_[0-9]+/){print substr($0,RSTART,RLENGTH)\t}' Input_file 

答案 2 :(得分:0)

非常仔细地编写了 sed 基于后向引用的答案-接近@ Sundeep 的答案,但这可以确保您实际上第二次出现{{1 }}:

_

借助 awk ,我们可以提供更多富有创意的解决方案。这是使用正则表达式的直接 awk 实现:

sed 's/\(_[^_]*\)_.*/\1\t/'

在此示例中,充分利用(滥用)字段分隔符功能,完全避开了正则表达式模式匹配以执行所需的操作:

awk 'match($0, /[^_]*_[^_]*_/) { $0 = substr($0, 1, RLENGTH - 1) "\t" } 1'

请注意,以上所有内容都认真遵循了您的示例,以模拟sed会产生的默认行为-即通过并打印所有行,包括不匹配的行。所有这些都小心地替换了第二次出现的awk -F_ 'NF > 2 { $0 = $1 FS $2 "\t" } 1' 以及后面带有_的文本-所有不超过一个\t的行都以未经编辑的方式通过。

如果我们希望将输出限制为仅打印匹配的行,则可以将两个示例中的_替换为$0 =,然后删除结尾的print