한 텍스트 파일의 줄을 다른 텍스트 파일의 줄로 바꾸기

한 텍스트 파일의 줄을 다른 텍스트 파일의 줄로 바꾸기

다음과 같은 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더니 문자열 확장이 예상대로 작동하는 것을 볼 수 있습니다.

그러나 스크립트를 실행하면 다음 오류가 발생합니다(스크립트 한 줄).

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을 슬래시 또는 물음표로 구분된 문자열로 처리하고 해당 문자열에서 네 번째 필드를 선택하여 찾을 수 있습니다.

그런 다음 코드는 .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 블록 외부의 나머지 스크립트는 입력 파일의 각 데이터 줄에 대해 한 번씩 실행됩니다.

실행 후 샘플 출력:

$ 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의 옵션을 사용할 수 있고 스크립트가 직접 작성할 필요 없이 재생 목록 파일을 그 자리에서 편집하도록 할 수 있습니다. 내부 편집 코드. 또한 추가 코드 없이 여러 입력 파일을 처리하기 위한 지원도 추가되었습니다. 두 가지 유용한 기능이 무료입니다.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

관련 정보