grep의 다양한 출력을 얻는 방법은 무엇입니까?

grep의 다양한 출력을 얻는 방법은 무엇입니까?

나는 grep이 많은 답변에 나타나는 것을 보았지만 실제로 그것에 대해 생각해 본 적이 없습니다.

이제 인터넷에서 로컬로 저장된 페이지의 HTML 태그 내부 콘텐츠를 가져오려고 하는데 멈췄습니다. grep을 사용하여 원하는 출력을 식별할 수 있지만 이를 사용 가능하도록 구분하는 것은 불가능합니다.

이것은 내 test.sh 파일 내용입니다.

a=$(awk '/<div class="power-bar-text">/,/<\/div>/' 'Acid Fast.html')
b=$(echo $a | grep -PzTo [0-9\.]+)
echo $a
echo $b

결과는 다음과 같은 터미널 출력입니다.

test.sh: line 4: warning: command substitution: ignored null byte in input
<div class="power-bar-text"> 9 </div> <div class="power-bar-text"> 8 </div> <div class="power-bar-text"> 11.25 </div> <div class="power-bar-text"> 10 </div> <div class="power-bar-text"> 6 </div> <div class="power-bar-text"> 5 </div> <div class="power-bar-text"> 2 (1s) </div> <div class="power-bar-text"> 3 </div> <div class="power-bar-text"> 2.50 </div>
9811.2510652132.50

다음은 사람이 좀 더 읽기 쉬운 이전 반복입니다.

$ awk '/<div class="power-bar-text">/,/<\/div>/' 'Acid Fast.html' | grep -Pzn -C1 [0-9\.]+ -
1:            <div class="power-bar-text">
                9
            </div>
            <div class="power-bar-text">
                8
            </div>
            <div class="power-bar-text">
                11.25
            </div>
            <div class="power-bar-text">
                10
            </div>
            <div class="power-bar-text">
                6
            </div>
            <div class="power-bar-text">
                5
            </div>
            <div class="power-bar-text">
                2 (1s)
            </div>
            <div class="power-bar-text">
                3
            </div>
            <div class="power-bar-text">
                2.50
            </div>

위의 코드 상자에서 색상을 설정하는 방법을 모르지만 터미널은 기본 일치 글꼴 색상인 빨간색을 사용하여 각 숫자와 마침표를 인코딩합니다.

(클래스 이름이 마침표와 일치하기 때문에 "power.bar.text"인 경우 작동하지 않을 수 있습니다. 마침표가 숫자인지 확인하는 데 도움이 되나요? 정규 표현식에 적용될 수 있을 것 같습니다 [0-9]+\.?[0-9]*.)

그러나 Bash의 코드를 사용하여 첫 번째 코드 블록으로 돌아가면, 그것이 제공하는 최종 출력은 9811.2510652132.50. 하지만 나는 다음과 같은 것을 원합니다9,8,11.25,10,6,5,2,1,3,2.50

grep 코드를 작성하면 -d,출력에서 ​​명령에 구분 기호를 설정하도록 선택할 수 있습니다. 불행하게도 제가 시도했을 때 그 아이디어는 효과가 없었습니다.

내가 가지고 있는 끔찍한 아이디어 중 하나는 -m 매개변수 출력을 사용하여 이를 반복적으로 처리하고 허용되는 일치 항목 수를 늘린 다음 각 출력 사이에서 새로운 것을 찾는 것입니다. 다시 말하지만, 이것은 끔찍한 것 같습니다. (예를 들어, -m1은 9를 얻고, -m2는 98을 얻고, -m3은 9811.25를 얻을 것으로 예상했으며, m1의 출력에서 ​​m2의 출력을 "빼서" 8을 얻습니다. m3, 우리는 11.25를 얻습니다.)

실제로 방금 시도했지만 awk가 한 줄로 만든 것 같아서 작동하지 않습니다. 따라서 일치 항목 수에 관계없이 전체 문자열이 9811.2510652132.50첫 번째이자 유일한 일치 항목이므로 전체 문자열을 출력합니다.

확실히 더 좋은 방법이 있을까요?

답변1

댓글에서 언급했듯이, grep(구조화되지 않은 텍스트 문서에서 줄을 추출하는 유틸리티)은 일반적으로 HTML이나 구조화된 문서를 구문 분석하는 데 사용하려는 도구가 아닙니다. 이상적으로는 구조화된 쿼리를 문서에 적용하고 데이터를 추출, 수정 또는 처리할 수 있는 도구를 사용하는 것이 좋습니다. XML 문서의 경우 이러한 명령줄 도구 중 하나는 xmlstarlet이를 사용하여 적용할 수 있다는 것입니다.XPath 쿼리XML 문서로.

divHTML 문서가 올바른 XHTML이라고 가정하면 class값이 속성인 노드의 내용을 추출 power-bar-text하고 양쪽의 공백을 제거할 수 있습니다.

xmlstarlet select --template \
    --match '//div[@class="power-bar-text"]' \
    --value-of 'normalize-space()' -nl file.xml

이는 먼저 div우리가 관심 있는 노드를 일치시킨 다음 normalize-space()이러한 일치하는 노드에 적용된 함수의 결과를 추출합니다. 마지막으로 -nl각 출력을 개행 문자로 구분합니다.

또는 짧은 옵션을 사용하세요.

xmlstarlet sel -t \
    -m '//div[@class="power-bar-text"]' \
    -v 'normalize-space()' -n file.xml

귀하가 표시한 문서의 일부를 고려하면 다음과 같이 출력될 수 있습니다.

9
8
11.25
10
6
5
2 (1s)
3
2.50

이 작업은 쉼표로 구분된 한 줄에 전달하여 수행할 수 있습니다.

paste -d , -s -

...이와 같이:

$ xmlstarlet sel -t -m '//div[@class="power-bar-text"]' -v 'normalize-space()' -n file.xml | paste -d , -s -
9,8,11.25,10,6,5,2 (1s),3,2.50

명령의 각 출력 줄에 있는 첫 번째 공백 이전에만 작업이 수행되도록 하려면 몇 가지 추가 처리를 추가하십시오 xmlstarlet.

$ xmlstarlet sel -t -m '//div[@class="power-bar-text"]' -v 'normalize-space()' -n file.xml | sed 's/ .*//' | paste -d , -s -
9,8,11.25,10,6,5,2,3,2.50

파일이 XHTML이 아닌 경우 다음을 사용하여 사용할 수 있는 파일로 변환할 수 있습니다.

xmlstarlet format --recover --html file.html >file.xml

답변2

쿠살라난다에 추가답변, 보다 일반적인 HTML이 있는 경우 XML로 변환하는 대신 BeautifulSoup을 사용하는 것이 좋습니다(다른 XML 파서 자체를 사용하지 않는다는 의미는 아니며 구문 분석을 처리하는 방식이 사용 사례에 더 우아할 수 있습니다). .

스크립트를 작성하게 됩니다. 그러나 bash스크립트는 아니지만 Python 스크립트(처음부터 직접 작성했으며 피상적으로 테스트함)를 작성합니다.

#! /usr/bin/env python3

from bs4 import BeautifulSoup
import sys

if not len(sys.argv) == 2:
    print(
        f"expected one argument, got {len(sys.argv) - 1}:\n {' '.join(sys.argv)}",
        file=sys.stderr,
    )
    sys.exit(-1)

with open(sys.argv[1]) as inputfile:
    soup = BeautifulSoup(inputfile)

hits = soup.find_all("div", class_="power-bar-text")

for hit in hits:
    content = hit.contents[0].strip()
    print(f"found value {content}")

예를 들어 파일에 저장하고 myparser.py실행 가능하게 만들고( chmod 755 myparser.py) HTML 파일 이름을 인수( )로 사용하여 실행합니다 /path/to/myparser.py /path/to/input.html.

아름답고 설명이 필요한 코드는 이쯤으로 하겠습니다. 이 작업을 셸에서 수행해야 한다고 생각되면 한 줄로 압축할 수 있습니다. (이렇게 하지 말 것을 권합니다. 위의 완전한 읽기 가능하고 합리적인 오류 생성 Python 코드를 bash 스크립트의 여러 줄 문자열/HEREDOC에 포함할 수 있습니다):

infile="foo.html"
python3 -c "from bs4 import BeautifulSoup as BS;soup=BS(open('${infile}'));print('\n'.join(tag.contents[0].strip() for tag in soup.find_all('div', class_='power-bar-text')))"

관련 정보