웹 응답에서 원하는 것만 뽑아내기 - grep, sed, tr 조합
curl로 받은 HTML은 수만 글자입니다. 여기서 제목 하나, 개수 하나를 꺼내려면
파이프로 몇 개를 엮으면 됩니다. 별도 도구를 설치할 필요가 없습니다.
결론부터 — 이 네 개 조합이면 웬만한 건 됩니다
grep -o '패턴' # 매치된 부분만 출력 (줄 전체가 아니라)
grep -c '패턴' # 개수만 세기
sed 's/<[^>]*>//g' # HTML 태그 제거
tr -s ' \n' ' ' # 중복 공백·줄바꿈 압축
grep -o — 이게 핵심입니다
보통 grep은 매치된 줄 전체를 보여줍니다. HTML은 한 줄이 수천 글자라 쓸모가 없습니다.-o를 붙이면 매치된 부분만 나옵니다.
# 제목 뽑기
curl -s https://example.com/ | grep -o '<title>[^<]*</title>'
# <title>사이트 제목</title>
# 태그까지 벗기려면 sed를 붙인다
curl -s https://example.com/ | grep -o '<title>[^<]*</title>' | sed 's/<[^>]*>//g'
# 사이트 제목
[^<]* 는 "<가 아닌 문자들" 이라는 뜻입니다. 여는 태그 다음부터 닫는 태그 직전까지만 집어냅니다. HTML에서 값을 뽑을 때 가장 많이 쓰는 패턴입니다.
grep -c — 세기
# 사이트맵에 URL이 몇 개인지
curl -s https://example.com/sitemap.xml | grep -c '<loc>'
# 특정 스크립트가 들어있는지 (0이면 없음)
curl -s https://example.com/ | grep -c 'analytics'
⚠️
grep -c는 매치된 "줄" 수를 셉니다.
HTML이 한 줄로 뭉쳐 있으면 10개가 있어도1이 나옵니다.
정확히 세려면grep -o로 쪼갠 뒤wc -l을 씁니다.curl -s https://example.com/ | grep -o 'adsbygoogle' | wc -l
이 차이 때문에 개수를 잘못 세는 일이 흔합니다.
sed — 태그 지우고 정리하기
# 모든 HTML 태그 제거
sed 's/<[^>]*>//g'
# 스크립트 블록까지 통째로 제거하고 본문만
curl -s https://example.com/ \
| sed 's/<script[^>]*>.*<\/script>//g; s/<[^>]*>/ /g' \
| tr -s ' \n' ' '
s/찾을것/바꿀것/g 형태입니다. g는 "한 줄에서 여러 번 나와도 전부"라는 뜻입니다.
tr — 공백 정리
태그를 지우면 공백과 빈 줄이 잔뜩 남습니다.
tr -s ' \n' ' ' # 연속된 공백·줄바꿈을 하나로 압축
tr -d '\r' # 윈도우 줄바꿈 문자 제거
tr '\n' ' ' # 줄바꿈을 공백으로
-s는 squeeze, 반복을 하나로 줄입니다.
curl -sI로 헤더를 받으면 줄 끝에\r이 붙어 있습니다.
비교나 조합이 이상하게 동작하면tr -d '\r'를 의심하세요.
실전 조합
사이트맵에서 글 제목만 (한글 디코딩까지)
curl -s https://example.com/sitemap.xml \
| grep -o '<loc>[^<]*entry[^<]*</loc>' \
| sed 's/<[^>]*>//g' \
| grep -v '/m/' \
| python3 -c "
import sys, urllib.parse
for l in sys.stdin:
print(urllib.parse.unquote(l.strip()).split('/entry/')[-1].replace('-',' '))
"
한글 주소는 %EB%8D%B0... 로 인코딩돼 있어서 디코딩해야 읽힙니다.
여러 사이트를 표처럼 점검
for h in a.example.com b.example.com c.example.com; do
page=$(curl -s -H 'Cache-Control: no-cache' "https://$h/?nc=$$")
printf "%-24s HTTP=%s 제목=%s\n" "$h" \
"$(curl -s -o /dev/null -w '%{http_code}' https://$h/)" \
"$(echo "$page" | grep -o '<title>[^<]*</title>' | sed 's/<[^>]*>//g')"
done
printf로 폭을 지정하면 세로줄이 맞아서 한눈에 들어옵니다. %-24s는 "왼쪽 정렬 24칸"입니다.
저는 grep -c 때문에 개수를 잘못 세고 있었습니다.
여러 사이트에 특정 스크립트가 들어있는지 확인하는 작업이었는데, grep -c로 세니 전부 1이 나왔습니다. "하나씩 들어있구나" 하고 넘어갔습니다.
나중에 grep -o ... | wc -l로 다시 세어보니 어떤 곳은 10개, 어떤 곳은 1개로 제각각이었습니다. HTML이 한 줄로 뭉쳐 있어서 grep -c가 "그 줄 하나"만 센 것이었습니다.
숫자가 나왔다고 맞는 게 아니었습니다. 셈의 단위가 무엇인지를 확인해야 했습니다.
주의 — 이 방법의 한계
HTML 파싱을 정규식으로 하는 건 원칙적으로 옳지 않습니다.
중첩 구조나 속성 안의 꺾쇠 같은 예외에서 깨집니다.
- 일회성 확인·진단에는 충분합니다. 빠르고 설치할 게 없습니다
- 반복 실행되는 스크립트나 정확성이 중요한 작업에는 제대로 된 파서를 쓰세요
(PythonBeautifulSoup,jq(JSON),xmllint(XML) 등)
JSON이라면 jq가 훨씬 편합니다.
curl -s https://example.com/api/items | jq '.items[].name'
정리
grep -o로 매치된 부분만 뽑습니다.[^<]*패턴을 기억하세요grep -c는 줄 수를 셉니다. 정확히 세려면grep -o ... | wc -lsed 's/<[^>]*>//g'로 태그를 벗깁니다tr -s로 공백을 정리하고, 헤더를 다룰 땐tr -d '\r'- 진단용으로만 쓰세요. 반복 실행되는 코드에는 제대로 된 파서를