我有一个 HLS 播放列表文件,如下所示:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:12
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:12.500000,
playlist0.ts
#EXTINF:8.333333,
playlist1.ts
#EXTINF:12.500000,
playlist2.ts
....
然后我有一个带有链接 ( ) 的文件,signurls.txt
如下所示:
https://example.com/playlist0.ts?Sign=xyz&Exp=1639139375&AWSAccessKeyId=abc
https://example.com/playlist1.ts?Sign=yzx&Exp=1639139375&AWSAccessKeyId=bca
https://example.com/playlist10.ts?Sign=zyx&Exp=1639139375&AWSAccessKeyId=cab
....
我正在尝试将链接插入到.m3u8
文件中,如下所示:
....
#EXTINF:12.500000,
https://example.com/playlist0.ts?Sign=xyz&Exp=1639139375&AWSAccessKeyId=abc
....
我想出了这段脚本:
for f in *.ts; do
sed -i '' -e "'s|$f|`grep -e $f signurls.txt`|'" playlist.m3u8;
done
编辑:.ts
文件中列出的每个文件都playlist.m3u8
存在于当前目录中。我使用实际文件进行循环for
,以便可以毫无问题地更改播放列表文件。
原因是-i ''
它需要同时在 macOS 和 Linux 上运行。
我尝试回显该sed
字符串,可以看到字符串扩展正在按预期工作。
但是,当我运行脚本时,出现此错误(一行脚本):
sed: 1: "'s|playlist0.ts|https:/ ...": invalid command code '
答案1
我将忽略这样一个事实:您似乎调用了something.ts
您的代码显然正在使用的文件。你在文中没有提到这些,所以我就假装不知道。
$ cat urls
https://example.com/playlist0.ts?Sign=xyz&Exp=1639139375&AWSAccessKeyId=abc
https://example.com/playlist1.ts?Sign=yzx&Exp=1639139375&AWSAccessKeyId=bca
https://example.com/playlist10.ts?Sign=zyx&Exp=1639139375&AWSAccessKeyId=cab
$ cat playlist.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:12
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:12.500000,
playlist0.ts
#EXTINF:8.333333,
playlist1.ts
#EXTINF:12.500000,
playlist2.ts
$ awk -F'[/?]' 'NR==FNR { pl[$4]=$0; next } /^[^#]/ && ($0 in pl) { $0 = pl[$0] }; 1' urls playlist.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:12
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:12.500000,
https://example.com/playlist0.ts?Sign=xyz&Exp=1639139375&AWSAccessKeyId=abc
#EXTINF:8.333333,
https://example.com/playlist1.ts?Sign=yzx&Exp=1639139375&AWSAccessKeyId=bca
#EXTINF:12.500000,
playlist2.ts
上面的命令首先从第一个文件 ( )awk
读取 URL ,并将字符串作为键添加到名为 的关联数组中。完整的 URL 将作为数组的值添加。urls
.ts
pl
.ts
通过将每个 URL 视为斜杠或问号分隔的字符串,并从该字符串中挑选出第四个字段,可以找到这些字符串。
然后,代码从.m3u8
文件中读取行,并且对于不以字符开头的每一行#
,它测试该行是否是数组中的键pl
。如果是,则当前行将替换为数组中相应的 URL。.m3u8
然后打印文件的所有行(可能如刚才所述进行修改)。
在上面的示例中,您可以看到播放列表文件中的最后一个条目未被替换,因为该条目的 URL 不在文件中urls
。
答案2
不要使用 shellwhile
或 for 来loop
处理文本。看为什么使用 shell 循环处理文本被认为是不好的做法?出于原因。
相反,使用perl
orawk
或python
or 任何非 shell 语言进行文本处理。
这是一个 perl 单行代码,它使用 Getopt::Std 模块(一个核心库模块,包含在 perl 中),允许使用signurls
指定文件-s
,以便可以读入并处理它分别地来自播放列表文件 - 这很重要,因为我们不想要那选项要修改的文件-i
。
$signurls = shift;
这可以用(第一个参数) 或(最后一个参数)之类的东西来完成,$signurls = pop;
但是a)这意味着signurls文件有成为第一个(或最后一个)参数(这是不灵活的,但对于快速而肮脏的黑客来说不一定是坏事),b)为 $signurls 提供默认文件名会更复杂且不太可靠,c)使用 来做到这一点并不难Getopt::Std
,并且这是一个了解如何使用的有用的库模块。
所有后续参数都被视为播放列表文件。因为它们是用 a 处理的while(<>)
,所以可以通过 perl 的选项就地修改它们-i
:
$ perl -MGetopt::Std -i.bak -lpe '
BEGIN {
# Parse any command line options.
getopts("s:", \%opts);
my $signurls = $opts{s} // "signurls.txt";
# Read in signurls file and build hash containing patterns
# and replacement strings.
open($fh,"<",$signurls) || die "error opening \"$signurls\": $!\n";
while(<$fh>) {
chomp;
# Extract the "filename" portion of the URL and use it as the hash's key.
# the hash's value is the URL itself.
m=^.*://.*?/([^/]*)[/?].*=;
$urls{$1} = $_;
};
close($fh);
};
foreach my $f (keys %urls) {
if ($_ eq $f) {
$_ = $urls{$f};
last; # we already matched, so there's no need to
# compare this line against the remaining keys.
};
};' -s signurls.txt playlist.m3u38
注 1:.bak
after-i
使 perl 为每个原始输入(播放列表)文件名创建备份副本(带有 .bak 扩展名)。
注2:BEGIN { ... }
代码块被执行一次在打开或处理任何文件之前。对于输入文件中的每一行数据,BEGIN 块之外的脚本其余部分都会执行一次。
运行后的示例输出:
$ cat playlist.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:12
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:12.500000,
https://example.com/playlist0.ts?Sign=xyz&Exp=1639139375&AWSAccessKeyId=abc
#EXTINF:8.333333,
https://example.com/playlist1.ts?Sign=yzx&Exp=1639139375&AWSAccessKeyId=bca
#EXTINF:12.500000,
playlist2.ts
我可以已经写了这个,以便它有一个-P
播放列表文件的选项。事实上,我一开始就是这样写的。但是通过编写它来使用 perl 的-p
选项(这只不过是一个自动while(<>)
循环 - 请参阅 参考资料man perlrun
)来读取和处理播放列表,我可以使用 perl 的-i
选项并让脚本就地编辑播放列表文件,而无需编写我自己的就地编辑代码。它还添加了对处理多个输入文件的支持,无需任何额外的代码。两个有用的功能,免费。
答案3
sed
在循环中使用
$ while read line; do sed -i.bak "s#$(sed 's#.*/\([^?]*\).*#\1#' <<< $line)#$line#" playlist.m3u8; done < signurls.txt
$ cat playlist.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:12
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:12.500000,
https://example.com/playlist0.ts?Sign=xyzplaylist0.tsExp=1639139375playlist0.tsAWSAccessKeyId=abc
#EXTINF:8.333333,
https://example.com/playlist1.ts?Sign=yzxplaylist1.tsExp=1639139375playlist1.tsAWSAccessKeyId=bca
#EXTINF:12.500000,
playlist2.ts
....
答案4
给出的错误信息实际上比我想象的要好,事实证明确实是字符'
的问题。
删除模式字符串'
中的字符sed
解决了问题:
for f in *.ts; do
sed -i "" "s|$f|$(grep -e $f signurls.txt)|g" playlist.m3u8 ;
done