[Linux/Sed] sed and regex
1. Overview
sed 기본적인 사용 방법과 regex를 활용한 몇몇 유용한 패턴을 정리한다
2. 기본 사용법
다음과 같은 Document가 있다고 가정하고,
기본적인 사용방법을 예시로 든다.
변경된 결과만 언급하기로 한다.
1
2
3
4
5
6
7
8
9
10
$ cat /tmp/sed/before.txt
Hello world!
My name is ${NAME}
This time is 'How to use sed'
Through practice, You can enhance editing text document!
-
Ref. My blog addr is //dhkim900331.github.com
My email is ks900331@naver.com
My phone is 010-1234-5678
2.1 간단한 문자 변경
-
1 2 3
$ sed "s/\!/\!\!/g" before.txt Hello world!! Through practice, You can enhance editing text document!!
- 느낌표(!) 두개 만들기
- 느낌표(!, Exclamation Mark)는 Linux에서 특수문자이기 때문에 문자마다 Escape(\, back-slash)를 사용해야 한다. 참고
- 추가로 확인해보니, single quote(‘)를 사용하여 특수문자를 제거할 수 있다.
sed 's,!,!!,g' before.txt
-
1 2
$ sed "s/world\!/world\!\!/g" before.txt Hello world!!
- 원하는 글자의 느낌표만 두개로 변경하였다.
2.2 특수 문자 변경
slash, quote와 같은 특수문자를 변경할 때는 escape가 필요하다고 하였는데, 다음과 같이 복잡하게 구성이 되는 경우가 있다.
1
2
$ sed "s/\/\//https:\/\//g" before.txt
Ref. My blog addr is https://dhkim900331.github.com
// → https:// 변경하기 위해 slash와 escape를 섞다보니 복잡하다.
1
2
$ sed "s|//|https://|g" before.txt
Ref. My blog addr is https://dhkim900331.github.com
이렇게 delimiter(구분자) 를 변경하면 가독성이 좋아진다.
또는 sed "s,//,https://,g" before.txt
, sed "s@//@https://@g" before.txt
2.3 라인 변경
특정 라인을 편집하려면,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat -n before.txt
1 Hello world!
2 My name is ${NAME}
3
4 This time is 'How to use sed'
5 Through practice, You can enhance editing text document!
6 -
7 Ref. My blog addr is //dhkim900331.github.com
8 My email is ks900331@naver.com
9 My phone is 010-1234-5678
$ sed "6s,-,------------," before.txt
Through practice, You can enhance editing text document!
------------
Ref. My blog addr is //dhkim900331.github.com
6번째 줄의 Dash(-
)를 여러개 복사했다.
2.4 라인 전체 변경
라인 전체를 통째로 변경하려면,
1
2
3
4
$ sed "/My/c\privacy" before.txt
privacy
privacy
privacy
Blog, Email, Phone 정보가 있던 줄 전체를 변경했다.
1
2
3
4
$ sed "/^My/c\privacy" before.txt
Ref. My blog addr is //dhkim900331.github.com
privacy
privacy
Regex로 시작(^
) 문자를 지정하여, 일부만 변경했다.
이 명령어 패턴에서는, delimiter가 slash만 되는 것 같다.
2.5 추가 및 삭제
1
2
3
4
5
6
7
$ sed "1p" before.txt
Hello world!
Hello world!
My name is ${NAME}
$ sed "1d" before.txt
My name is ${NAME}
P(print), D(delete) 으로 첫번째 줄을 복사 또는 첫번째 줄 삭제
1
2
$ sed "1,3d" before.txt
This time is 'How to use sed'
1~3줄 삭제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ cat -n before.txt
1 Hello world!
2 My name is ${NAME}
3
4 This time is 'How to use sed'
5 Through practice, You can enhance editing text document!
6 -
7 Ref. My blog addr is //dhkim900331.github.com
8 My email is ks900331@naver.com
9 My phone is 010-1234-5678
$ sed "1~3d" before.txt
My name is ${NAME}
Through practice, You can enhance editing text document!
-
My email is ks900331@naver.com
My phone is 010-1234-5678
Line 1 삭제, Line 3 까지 건너 뛰기를 반복한다.
반복하므로, Line 1, 4(1+3), 7(1+3+3), 10(1+3+3+3) 을 삭제한다.
1
2
$ sed '1,1!d' before.txt
Hello world!
1~1 라인을 제외하고 모두 삭제한다.
2.6 정규식 변경
개인정보 중에 전화번호만 검출하여 변경한다.
1
2
$ sed 's/[0-9]\{1,3\}-[0-9]\{1,4\}-[0-9]\{1,4\}/privacy/g' before.txt
My phone is privacy
[0-9] : 0~9 숫자
\{1,3\} : 1~3 자리이며 escape brace 필요
복잡하므로 강조 표시를 해보자면,,,
$ sed ‘s/[0-9]\{1,3\}-[0-9]\{1,4\}-[0-9]\{1,4\}/privacy/g’ before.txt
사실 이 부분은,, 정규식을 알면 되는 내용이다.
2.7 Group Capture
Group Catpure라는 것은, 정규식으로 검색된 특정 부분을 Capture(촬영, 변수화)하여 원하는 대로 꺼내어 쓸 수 있게 해주는 것이다.
사실 이 부분은 sed의 범위를 넘어선다.
다음의 예제를 보면,
1
2
3
4
$ TEXT="0 a A"
$ echo ${TEXT} | sed "s#\([0-9]\) \([a-z]\) \([A-Z]\)#numeric(\1) lower(\2) upper(\3)#"
numeric(0) lower(a) upper(A)
0 a A 문자를 정규식으로 숫자, 소문자, 대문자 구분을 하고 소괄호로 group capture 하여 \1, \2, \3 으로 꺼내어 쓸 수 있다.
숫자,소문자,대문자 인지를 구별하는 것은 정규식의 기능이고, 구분된 문자들을 특정 그룹으로 묶어 (묶을 때 소괄호) group capture를 하였고,
capture를 back-slash 숫자로 갯수만큼 꺼내어 쓸 수 있다는 것.
위 예제를 변형시켜 보면,
1
2
3
$ TEXT="a 0 A"
$ echo ${TEXT} | sed "s#\([0-9]\) \([a-z]\) \([A-Z]\)#numeric(\1) lower(\2) upper(\3)#"
a 0 A
TEXT 변수안의 글자의 순서를 뒤바꿨을 뿐인데, sed 정규식이 실행되지 않아 원본 그대로 출력이 되었다.
내가 지금까지 알기로는, 순서 까지 스마트하게 capture하는 방법은 모르겠다.
위의 정규식은 항상 “숫자 v 소문자 v 대문자”가 위치할 것이라는 가정이 들어있다.
v 는 뛰어 쓰기를 의미한다.
3. 유용한 패턴
기본 사용법을 가지고, 필드에서 바로 써먹을 수 있는 몇가지 유용한 패턴을 정리해본다.
3.1 Group Capture
다음과 같이 이미 정리된 문서가 있다.
1
2
3
4
$ cat phone_email_name.txt
010-1234-5678 Jason@Jason.com Jason
010-2555-2323 Daniel@Daniel.com
013-5555-1234 bakeuion@bakeuion.com bakeuion
- 1단계, Phone number 정규식 만들기
[0-9]{1,4}
: 0~9 숫자가 1~4자리 있다는 의미-
: Phone 중간 번호 마디로써, 그냥 단순 문자.( ~ )
: 소괄호 단위로 Group이라고 하며, Capture가 된다. 아래에서 설명하지만 Capture를 하면 변수로 꺼내어 쓸 수 있게 된다.
- 2단계, 불필요한 공백 제거 및 소,중괄호 Escape 처리
- 불필요한 공백이라는 의미는, 1단계에서 정규식 코드를 가독성있게 하여 설명하기 위하여 공백을 추가하였는데, 그것을 제거한다는 의미
- 소,중괄호만 Back-slash로 escape 처리해야 하는 것으로 보여진다, 대괄호는 하지 않아도 되는 것으로 확인됨, 구체적은 설명된 문서는 못찾음.
- 변수로 담아 3단계에서 가독성을 높인다.
- 3단계, 다음의 sed syntax에 삽입한다.
\1
: 1단계에서 말한 Group Capture 순서대로 꺼낼 수 있는 변수다. 소괄호 묶음 마다 숫자를 증가시켜 꺼내 쓸 수 있다.
결과는,,,
1
2
3
4
5
$ PHONE_REGEX="\([0-9]\{1,4\}-[0-9]\{1,4\}-[0-9]\{1,4\}\)"
sed "s#${PHONE_REGEX}#Phone(\1)#" phone_email_name.txt
Phone(010-1234-5678) Jason@Jason.com Jason
Phone(010-2555-2323) Daniel@Daniel.com Daniel
Phone(013-5555-1234) bakeuion@bakeuion.com bakeuion
최종적으로는 다음처럼 정리할 수 있다.
1
2
3
4
5
6
7
8
$ PHONE_REGEX="\([0-9]\{1,4\}-[0-9]\{1,4\}-[0-9]\{1,4\}\)"
$ EMAIL_REGEX="\([0-9a-zA-Z].*@[0-9a-zA-Z].*[.com|.kr]\)"
$ NAME_REGEX="\([a-zA-Z].*\)"
$ sed "s#${PHONE_REGEX} ${EMAIL_REGEX} ${NAME_REGEX}# Phone(\1) Email(\2) Name(\3) #" phone_email_name.txt
Phone(010-1234-5678) Email(Jason@Jason.com) Name(Jason)
Phone(010-2555-2323) Email(Daniel@Daniel.com) Name(Daniel)
Phone(013-5555-1234) Email(bakeuion@bakeuion.com) Name(bakeuion)
어떤 데이터 순서를 가지고 있던 간에, 이메일 데이터만 제대로 뽑을 수 있는 regex를 만들다가 어려워서 포기했다~