regex 更好的正则表达式以获取括号中的值

w8rqjzmb  于 2023-05-01  发布在  其他
关注(0)|答案(6)|浏览(388)

我有一个M3 U播放列表,看起来像这样:

#EXTM3U
#EXTINF:-1 tvg-id="wsoc.us" tvg-name="ABC 9 (Something) (WSOC)" tvg-logo="" group-title="US Locals",ABC 9 (Something) WSOC (WSOC) 
http://some.url/1
#EXTINF:-1 tvg-id="wbtv.us" tvg-name="CBS 3 WBTV (WBTV)" tvg-logo="" group-title="US Locals",CBS 3 WBTV (WBTV)
http://some.url/2
#EXTINF:-1 tvg-id="wcnc.us" tvg-name="NBC (Hey) 36 WCNC (WCNC)" tvg-logo="" group-title="US Locals (Something here)",NBC 36 (Hey) WCNC (WCNC)
http://some.url/3
#EXTINF:-1 tvg-id="wjzy.us" tvg-name="FOX 46 WJZY (Shout Out) (WJZY)" tvg-logo="" group-title="US Locals",FOX 46 WJZY (Shout Out) (WJZY)
http://some.url/4

我希望得到的最后一个条目在tvg-name字段没有括号-例如,WSOC和WBTV和WCNC等。
这是可行的:

grep -Po 'tvg-name=\".*?\"'  Playlist.m3u | awk -F'(' '{print $NF}' | cut -f1 -d")" | sort -u

但我知道一定有比使用grep,awk和cut更好的方法。我都快疯了。

31moq8wy

31moq8wy1#

只使用GNUgrep的正则表达式:
grep -oP 'tvg-name.*\(\K\w+(?=\))' /tmp/file.m3u
正则表达式匹配如下:
节点解释
tvg-name'tvg-name'
.*除\n以外的任何字符(0次或更多次(匹配最大可能量))
\((一)
\K重置匹配的开始(K ept)作为使用回头Assert的更短替代方案:look aroundsSupport of K in regex
\w+单词字符(a-z,A-Z,0-9,_)(1次或多次(匹配最大可能量))
(?=look ahead查看是否存在:
\))的情况下进行
)先行结束
或者使用a proper m3u parser

需要安装CPAN模块

cpan Parse::M3U::Extended

#!/usr/bin/env perl

use strict; use warnings;

use Parse::M3U::Extended qw(m3u_parser);
use File::Slurp;
use feature 'say';
my $m3u = read_file('/tmp/file.m3u');
my @items = m3u_parser($m3u);

foreach my $item (@items) {
    if ($item->{type} eq "directive" and $item->{tag} eq "EXTINF") {
        $_ = $item->{value};
        s/.*\((\w+)\)/$1/;
        say;
    }
}

这具有以可靠的方式可重复用于其他用例的优点,这不是随机awksed等的情况。..

输出:
WSOC 
WBTV
WCNC
WJZY
nle07wnf

nle07wnf2#

GNU awk使用()作为字段分隔符,并在包含tvg-name倒数第二个字段($(NF-1))的每一行中打印。

awk -F '[()]' '/tvg-name/{print $(NF-1)}' Playlist.m3u

输出:

WSOC
WBTV
WCNC
WJZY
daolsyd0

daolsyd03#

您可以使用()字段分隔符,因此不需要最后一个cut
在单引号字符串中不需要转义双引号。

grep -Po 'tvg-name=".*?"'  Playlist.m3u | awk -F'[()]' '{print $(NF-1)}'

如果您使用的是GNU awk,您还可以使用捕获组来获取tvg-name=".*"部分,因此您不需要grep

awk 'match($0, /tvg-name="[^"]*\(([^)]*)/, m) { print m[1] }' Playlist.m3u

参见AWK: Access captured group from line pattern

dddzy1tm

dddzy1tm4#

使用您显示的示例和尝试,请尝试以下awk代码。应该可以在任何POSIX awk版本中工作。这里使用match函数和substr功能。使用正则表达式tvg-name=".*\([^)]*匹配值,并只输出所需的值。

awk '
match($0,/ tvg-name=".*\([^)]*/){
  val=substr($0,RSTART,RLENGTH)
  sub(/.*\(/,"",val)
  print val
}
'  Input_file
rkue9o1l

rkue9o1l5#

在每个Unix机器上的任何shell中使用任何sed:

$ sed -n 's/.*tvg-name="[^"]*(\([^)]*\).*/\1/p' file
WSOC
WBTV
WCNC
WJZY
9vw9lbht

9vw9lbht6#

你可以用非常复杂的perlsed方法来实现,包括捕获组、预测和边界Assert等等,
或者你可以用awk的方式来做:

mawk 'NF *= /tvg-name/' FS='.+[(]|[)][^(]*$' OFS=

gawk '$_ = $(NF -= NF^!/tvg-name/)' FS='[#-?]+'
WSOC
WBTV
WCNC
WJZY

如果您绝对确定没有任何东西超出最终),那么更简洁:

mawk 'NF *= /tvg-name/' FS='.+[(]|.$' OFS=

如果你只修改regex一点点,你甚至可以得到广播网络。..

awk '(NF*=/tvg-name/) && $-_=$--NF' FS='[#-?]+'
WSOC
WBTV
WCNC
WJZY
  • ...简单地把(#)改成(+)。..
awk '(NF*=/tvg-name/) && $-_=$--NF' FS='[+-?]+'
ABC 
CBS 
NBC 
FOX

相关问题