xml 파일을 grep하고 결과에 따라 출력을 파일로 리디렉션하는 스크립트

xml 파일을 grep하고 결과에 따라 출력을 파일로 리디렉션하는 스크립트

그래서 저는 XML 파일을 구문 분석하고 해당 파일 아래에 있는 카테고리 이름을 기반으로 출력을 새 파일로 리디렉션하는 간단한 스크립트를 작성하려고 합니다. 예를 들어 XML 파일은 다음과 같습니다.

<category> Music </Category>
<url>https://www.youtube.com/watch?v=waAlgFq9Xq8</url>
<category> Movies </Category>
<url>https://www.youtube.com/watch?v=g4U4BQW9OEk</url>

내 스크립트는 다음과 같습니다

for i in *.xml; do
    name=$(grep -i "<category>" $i | awk '{print $1}')
    line=$(grep -i -A1 "<category>" $i)
    echo "$line" >> $filename
done

예를 들어 Movies.log에는 영화 카테고리에 있는 모든 링크가 포함되고, Music.log에는 음악 카테고리에 있는 모든 링크가 포함됩니다.

답변1

각 카테고리를 반복하는 것을 고려해 보셨나요? 이와 같이:

for i in *.xml; do
    for category in $(sed -rn '/^<category>/{s/[^>]*> *([^ <]*).*/\1/p}' "$i"); do
        sed -rn "/^<category> *$category/,/^<category>/{s/<url> *([^ <]*).*/\1/p}" "$i" > "$category.log"
    done
done

업데이트: awk 사용

awk -v 'RS=<' -v 'cat=none' -F '>' \
'$1 ~ /^category$/ {gsub(/^ *| *$/,"",$2); cat=$2} \
$1 ~ /^url$/ {print $2 >> cat".log"}' \
*.xml
  • 이렇게 하면 입력 파일이 반복되는 것을 방지하고 .log모든 카테고리의 파일에 추가됩니다.

  • awk의 레코드 구분 기호 할당을 사용한다는 것은 -v 'RS=<'카테고리/URL 태그를 어디에서나(줄의 시작 부분뿐만 아니라) 찾을 수 있음을 의미합니다. 개행 문자는 xml 데이터의 어느 곳에나 나타날 수 있습니다.

  • 이를 필드 구분 기호 설정과 결합 '>'하면 각 레코드의 첫 번째 필드가 xml 태그 이름과 동일해집니다.

  • awk는 첫 번째 필드가 "category"인 레코드를 만날 때마다 cat변수를 해당 카테고리의 이름으로 설정합니다.

  • awk는 첫 번째 필드가 "url"인 레코드를 발견하면 해당 URL을 파일에 추가합니다 cat.log.

  • catnone시작 으로 정의됩니다 . 이렇게 하면 <url>앞에 a가 없는 상황에서 오류가 발생하는 것을 방지할 수 있습니다 <category>.

  • 대체 방법은 gsub(/^ *| *$/,"",$2)예제 입력 파일에 나타나는 범주 이름에서 선행/후행 공백을 제거하는 것입니다 .xml.


노트:

위의 어느 것도 완벽하지 않습니다. 올바른 XML 입력 파일의 경우 실제 XML 파서가 더 좋습니다.xmlstarlet. 그러나 이를 위해서는 올바른 형식의 xml 파일도 필요합니다( <category>예를 들어, 예제 입력에는 일치하는 태그가 없습니다).

답변2

저는 다음 솔루션을 준비했습니다.

grep -hP "<category.*>|<url.*>" *.xml | cut -d ">" -f 2 | cut -d "<" -f 1 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | gawk 'BEGIN { category = ""; } { if (!length($0)) { next; } if (length(category)) { printf("\necho -e \"%s\" >> \"%s.log\"", $0, category); category = ""; } else { category = $0; } } END { printf("\n"); }' | bash

현재 디렉터리의 모든 .xml 파일을 검색하고추가URL 앞 줄에 있는 카테고리 이름을 따서 명명된 파일의 URL(끝에서 | bash를 제거하여 출력을 확인할 수 있습니다).

관심 있는 데이터에 대해서만 XML 노드를 추출합니다.

예를 들어 grep을 사용하여 다음 이름의 파일에서 패턴을 검색합니다.*.xml, 파일 이름을 반복할 필요가 없습니다. 옵션-시간grep은 출력에서 ​​파일 이름을 억제합니다. grep에 제공되는 패턴은 Perl 호환 정규식(-피)

관심 있는 노드의 값을 추출합니다.

grep 명령에 의해 반환된 줄은 다음과 같습니다.

    <category> MyMusic </category>
    <url>https://www.youtube.com/watch?v=waAlgFq9Xq8123</url>
    <category> MyMovies </category>
    <url>https://www.youtube.com/watch?v=g4U4BQW9OEk456</url>
    <category>Music</category>
    <url>https://www.youtube.com/watch?v=waAlg</url>
    <category>              Music </category>
    <url>https://www.youtube.com/watch?v=waAlgFq9Xq8</url>
    <category> Movies </category>
    <url>https://www.youtube.com/watch?v=g4U4BQW9OEk</url>

원하지 않는 데이터 행을 필터링했습니다. 이제 노드 내의 값을 추출해야 합니다. 이는 시작 태그와 끝 태그 사이의 데이터, 즉 기호 사이의 데이터를 추출하는 것으로 요약됩니다.>그리고<(우리는 그것이 어떤 노드인지 상관하지 않으므로 "일반" 방법을 사용합니다).

이는 쉽게 달성할 수 있습니다.| cut -d ">" -f 2 | cut -d "<" -f 1

이는 본질적으로 모든 것을 기호 >(-f 2)의 오른쪽으로 가져간 다음, 우리가 얻는 새로운 결과에 따라 기호 <(-f 1)의 왼쪽으로 모든 것을 가져오는 것을 의미합니다.

이는 우리에게 다음과 같은 결과를 남깁니다.

 MyMusic 
https://www.youtube.com/watch?v=waAlgFq9Xq8123
 MyMovies 
https://www.youtube.com/watch?v=g4U4BQW9OEk456
Music
https://www.youtube.com/watch?v=waAlg
                Music 
https://www.youtube.com/watch?v=waAlgFq9Xq8
 Movies 
https://www.youtube.com/watch?v=g4U4BQW9OEk

이제 이러한 값을 정리해야 합니다. 여기에 작은 수정 단계가 있습니다.

트림 값

선행 및 후행 공백 자르기| sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'

그리고-이자형, sed는 추가 sed 명령(또는 다른 경우에는 여러 sed 명령)을 파이프하지 않고도 지정된 순서로 스크립트를 실행할 수 있습니다.

sed에 전달된 첫 번째 스크립트는 선행 공백(즉, 문자열 시작 부분의 모든 [:space:] 문자(한 줄당))을 자르고, 두 번째 스크립트는 후행 공백(즉, 끝 앞의 모든 [:space:])을 자릅니다. ] 문자) 문자열(@각 줄)입니다.

이제 이와 같은 것이 있으므로 거의 완료되었습니다.

MyMusic
https://www.youtube.com/watch?v=waAlgFq9Xq8123
MyMovies
https://www.youtube.com/watch?v=g4U4BQW9OEk456
Music
https://www.youtube.com/watch?v=waAlg
Music
https://www.youtube.com/watch?v=waAlgFq9Xq8
Movies
https://www.youtube.com/watch?v=g4U4BQW9OEk

표준 출력에 파일 추가 명령 쓰기

파일에 데이터를 추가하기 위해 echo 명령을 작성한 것처럼 해당 프로세스를 자동화할 수 있는 것이 필요합니다. 나는 유휴 상태를 선택합니다. gawk는 데이터를 한 줄씩 읽고 범주를 변수로 가져옵니다. 다른 행을 읽을 때 카테고리 변수가 비어 있지 않으면 행에 URL이 포함됩니다. 이 기술을 사용하면 echo -e "current url" >> current_category.log와 같은 명령을 간단히 실행할 수 있습니다.

알아채다비판적인>>를 사용하여 파일에 새 데이터를 추가합니다. >를 사용하면 마지막 URL만 작성되며 카테고리당 행이 하나씩 표시됩니다!

결과적으로 우리는 표준 출력에 다음 데이터를 썼습니다.

echo -e "https://www.youtube.com/watch?v=waAlgFq9Xq8123" >> "MyMusic.log"
echo -e "https://www.youtube.com/watch?v=g4U4BQW9OEk456" >> "MyMovies.log"
echo -e "https://www.youtube.com/watch?v=waAlg" >> "Music.log"
echo -e "https://www.youtube.com/watch?v=waAlgFq9Xq8" >> "Music.log"
echo -e "https://www.youtube.com/watch?v=g4U4BQW9OEk" >> "Movies.log"

bash 실행에 데이터 추가 명령 전달

파이프라인의 마지막 요소는 | bashecho 명령이 실행을 위해 bash에 전달되도록 합니다.

gawk는 파일에 데이터를 쓰거나 추가할 수 있습니다. 그러나 나는 의도적으로 가능한 가장 작은 gawk 스크립트를 갖고 싶었습니다.

관련 정보