あるテキストファイルの行を別のテキストファイルの行に置き換える

あるテキストファイルの行を別のテキストファイルの行に置き換える

次の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
....

これには、次のlink()を含むファイルがあります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

編集する:.tsfileにリストされている各ファイルはplaylist.m3u8現在のディレクトリにあります。ループには実際のファイルを使用するので、for問題なくプレイリストファイルを変更できます。

その理由は、-i ''macOSとLinuxの両方で実行する必要があるからです。

文字列をエコーし​​てみるsedと、文字列拡張が期待どおりに機能することがわかります。

ただし、スクリプトを実行すると、次のエラーが発生します(1行のスクリプト)。

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.tspl

.tsこれらの文字列は、各URLをスラッシュまたは疑問符で区切られた文字列として扱い、その文字列から4番目のフィールドを選択することで見つけることができます。

その後、コードは.m3u8ファイルから行を読み取り、文字で始まらない各行について、その行が配列のキーであるかどうかを#テストしますpl。その場合、現在の行は配列の対応するURLに置き換えられます。.m3u8次に、ファイルのすべての行を印刷します(今説明したように変更可能)。

上記の例では、プレイリストファイルの最後のエントリは、そのエントリのURLがファイルにないために置き換えられていないことを示していますurls

答え2

whileテキストを処理するためにシェルやforを使用しないでくださいloop。バラよりシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?理由があります。

代わりに、テキスト処理のためperlまたは非シェル言語をawk使用してください。python

signurlsこれは、ファイル-sを読み取って処理できるようにファイルを指定できるように、Getopt :: Stdモジュール(Perlに含まれるコアライブラリモジュール)を使用するPerlシングルライナーです。それぞれプレイリストファイルから - 私たちが望んでいないので、これは重要ですそれ変更するオプションファイルです-i

$signurls = shift;これは(最初のパラメータ)または(最後のパラメータ)のように実行できますが、$signurls = pop;a)これはsignurlsファイルを意味します。持つ最初の(または最後の)引数になります(柔軟ではありませんが、速くて汚いハッカーにとって必ずしも悪いわけではありません)。 b)$ signurlsにデフォルトのファイル名を提供することは、より複雑で信頼性が低くなります。 c) 難しくない。これを使用しておりGetopt::Std、使い方がわかる便利なライブラリモジュールです。

後続のすべてのパラメータはプレイリストファイルとして扱われます。として処理されるため、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:.bakAfter-iは、Perlに各ソース入力(プレイリスト)ファイル名のバックアップコピー(.bak拡張子を含む)を作成させます。

注2:BEGIN { ... }コードブロックが実行されます。一度ファイルを開いたり処理したりする前。 BEGINブロック外の残りのスクリプトは、入力ファイルの各データ行に対して1回実行されます。

実行後のサンプル出力:

$ 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(<>)- 参考資料を参照)を使用するように作成すると、Perlのオプションを使用でき、スクリプトが直接作成することなくプレイリストファイルをその場で編集できるようになります。できます。内部編集コード。また、追加コードなしで複数の入力ファイルを処理するためのサポートも追加されました。 2つの便利な機能は無料です。man perlrun-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

関連情報