정규 표현식을 사용한 텍스트 처리

LinuxBeginner
지금 연습하기

소개

정규 표현식은 하나의 정확한 문자열이 아니라 구조를 사용해 텍스트를 표현합니다. 동일한 패턴 언어가 여러 Linux 도구에 사용되지만, 도구마다 활용 방식은 다릅니다. grep은 일치하는 항목을 선택하거나 추출하고, sed는 스트림을 변환하며, awk는 패턴과 필드 기반 작업을 결합합니다.

먼저 앵커와 문자 클래스를 사용하고, 확장 반복과 추출로 진행합니다. 그런 다음 변환 결과를 저장하기 전에 미리 확인하고, 마지막으로 필드 선택과 집계를 수행합니다. 셸이 특수 문자를 직접 해석하지 않고 텍스트 처리 도구에 전달하도록 패턴을 따옴표로 묶습니다.

기본 정규 표현식 구조로 행 일치시키기

이 단계에서는 리터럴 텍스트, 앵커, 문자 클래스를 사용해 행 전체에서 특정 위치를 표현합니다.

실습 파일을 확인합니다.

cd /home/labex/project/regex-lab
nl -ba contacts.txt

리터럴 텍스트는 행의 어느 위치에서든 일치합니다.

grep 'active' contacts.txt

^는 패턴을 행의 시작 부분에 고정하고, $는 패턴을 행의 끝 부분에 고정합니다. Alice 레코드와 두 자리 숫자로 끝나는 레코드를 찾습니다.

grep '^Alice|' contacts.txt
grep '[0-9][0-9]$' contacts.txt

대괄호 안의 문자 클래스는 문자 하나와 일치합니다. [ABC]는 A, B 또는 C 를 의미합니다.

grep '^[ABC]' contacts.txt | tee abc-contacts.txt

이 명령은 Alice, Bob, Carol 을 출력합니다. 앵커를 사용한 숫자 일치 결과도 저장합니다.

grep '[0-9][0-9]$' contacts.txt > two-digit-values.txt

확장 정규 표현식 사용 및 일치 항목 추출

이 단계에서는 대안과 반복을 표현하기 위해 확장 정규 표현식을 사용한 다음, 일치한 부분 문자열만 추출합니다.

grep -E를 사용하면 +(하나 이상), ?(없거나 하나), |(대안) 같은 연산자를 사용할 수 있습니다. 완전한 active 또는 pending 상태 필드를 찾습니다.

cd /home/labex/project/regex-lab
grep -E '\|(active|pending)\|' contacts.txt

이메일과 비슷한 패턴은 여러 부분으로 구성할 수 있습니다.

  • [[:alnum:]._-]+는 허용되는 로컬 부분 문자가 하나 이상 있는지 일치시킵니다.
  • @는 리터럴 문자입니다.
  • [[:alnum:].-]+는 도메인 본문과 일치합니다.
  • \.[[:alpha:]]{2,}는 점과 두 글자 이상으로 된 접미사와 일치합니다.

-o를 사용해 일치하는 부분 문자열만 출력합니다.

grep -Eo '[[:alnum:]._-]+@[[:alnum:].-]+\.[[:alpha:]]{2,}' contacts.txt | tee emails.txt

일치하는 행에 번호를 표시하고 추출한 주소의 개수를 계산합니다.

grep -En '\|(active|pending)\|' contacts.txt
wc -l < emails.txt | tr -d ' ' > email-count.txt
cat email-count.txt

형식이 잘못된 dave_at_example.com@가 없으므로 일치하지 않습니다.

Sed 로 변환 결과 미리 보기

이 단계에서는 입력 파일을 변경하지 않고 sed로 스트림을 변환합니다.

치환 명령의 형식은 s/pattern/replacement/flags입니다. 각 행에서 첫 번째 a만 바꾼 다음, g 플래그를 사용해 소문자 a를 모두 바꿉니다.

cd /home/labex/project/regex-lab
sed 's/a/A/' contacts.txt
sed 's/a/A/g' contacts.txt

텍스트 자체에 슬래시가 포함되어 있거나 다른 구분 기호가 더 명확한 경우에는 다른 구분 기호를 사용할 수 있습니다. 이메일 도메인을 redacted.invalid로 바꿉니다.

sed -E 's#@[[:alnum:].-]+\.[[:alpha:]]{2,}#@redacted.invalid#g' contacts.txt

Sed 주소를 사용하면 명령을 적용할 행을 선택할 수 있습니다. 정확히 |inactive| 필드가 포함된 행에만 상태를 바꿉니다.

sed '/|inactive|/s/|inactive|/|disabled|/' contacts.txt | tee contacts-preview.txt

원본 파일에 여전히 inactive가 포함되어 있는지 확인합니다.

grep '|inactive|' contacts.txt

Sed 로 정리된 파일 만들기

이 단계에서는 여러 sed 명령을 결합하고, 변환된 스트림을 새 파일로 저장합니다.

services.csv의 헤더는 변경하지 않습니다. 데이터 행에서는 warnwarning으로 정규화하고 마지막 숫자 필드에 ms 접미사를 추가합니다. -e는 여러 명령을 지정하며, 주소 2,$는 2 번째 행부터 마지막 행까지 적용 범위를 제한합니다.

cd /home/labex/project/regex-lab
sed -E -e '2,$ s/,warn,/,warning,/' -e '2,$ s/([0-9]+)$/\1ms/' services.csv | tee services-clean.csv

여기서 괄호는 마지막 숫자를 캡처하고, \1은 치환 문자열에서 해당 캡처를 다시 사용합니다.

원본 파일과 새 파일을 비교합니다.

diff -u services.csv services-clean.csv || true

출력이 의도적으로 변경되었으므로 diff가 0 이 아닌 상태를 반환하는 것은 정상입니다. || true는 복사한 명령 시퀀스가 계속 실행되도록 합니다.

Awk 로 필드 선택 및 집계하기

이 단계에서는 필드 구조와 계산이 모두 필요한 작업에 awk를 사용합니다.

-F,는 입력 구분 기호를 쉼표로 설정합니다. 데이터 행만 대상으로 서비스와 상태를 출력합니다. NR은 현재 레코드 번호입니다.

cd /home/labex/project/regex-lab
awk -F, 'NR > 1 {print $1, $2}' services.csv

숫자로 된 세 번째 필드가 200 보다 큰 행을 선택합니다.

awk -F, 'NR > 1 && $3 > 200 {print $1, $3}' services.csv | tee slow-services.txt

합계와 개수를 누적한 다음 END 블록에서 평균을 계산합니다. printf는 숫자 형식을 제어합니다.

awk -F, 'NR > 1 {sum += $3; count++} END {printf "average_latency=%.1f\n", sum/count}' services.csv | tee latency-summary.txt

Awk 패턴은 작업을 실행할 레코드를 결정합니다. $1, $3과 같은 필드를 사용하면 구조화된 계산을 읽기 쉽게 작성할 수 있습니다.

요약

앵커, 클래스, 대안, 반복을 조합해 정규 표현식 패턴을 만들었습니다. grep으로 항목을 선택하고 추출했으며, sed로 변환 결과를 미리 보고 저장했습니다. 또한 awk를 사용해 필드 기반 선택과 집계를 수행했습니다. 다음 Challenge 에서는 전체 명령 시퀀스가 제공되지 않은 상태에서 이러한 개념을 적용합니다.