[쉘 스크립트] 쉘 스크립트 기초 - 고급 sed (10)

업데이트:     Updated:

카테고리:

태그:

🎯고급 sed

기본 sed 편집 명령을 사용하다 보면 뭔가 제한이 있다는 것을 눈치챌 수 있을 것이다.

멀티 라인 명령

sed 편집기는 여러 줄의 텍스트를 처리할 때 쓸 수 있는 세 가지 특별한 명령을 포함하고 있다.

  • N(next): 멀티라인 그룹을 만들기 위해 데이터 스트림에서 다음 줄을 추가
  • D(delete): 멀티라인 그룹에서 한 줄을 삭제
  • P(print): 멀티라인 그룹의 한 줄을 인쇄

단일 줄 버전의 다음 줄 명령


멀티라인 다음 줄 명령을 써보기 전에 먼저 단일 줄 버전의 다음 줄 명령이 어떻게 동작하는지 살펴볼 필요가 있다.

소문자 n 명령은 sed 편집기에서 명령의 시작 부분으로 돌아가지 않고 데이터 스트림 텍스트의 다음 줄로 가라고 지시한다.(sed 편집기는 데이터 스트림에서 텍스트의 다음 줄로 옮겨가기 전에 한 줄에 정의된 모든 명령을 처리한다는 점을 상기하라.)

예제: 다섯 줄을 포함하는 데이터 파일에서 머리글 이후에 있는 빈 줄만 제거

$ cat data1.txt 
This is the header line.

This is a data line.

This is the last line.

$ sed '/header/{ n; d }' data1.txt  # /header/: 주소 줄 패턴, { n; d }: 명령 리스트
This is the header line.
This is a data line.

This is the last line.
  1. sed 편집기는 단어 header를 포함하는 고유한 줄을 찾는다.
  2. 스크립트가 그 줄을 식별하고 나면 n 명령은 텍스트의 다음 줄인 빈 줄로 옮겨간다.
  3. d 명령은 빈 줄을 지운다.
  4. sed 편집기가 명령 스크립트의 끝에 이르면 데이터 스트림에서 다음 텍스트 줄을 읽어 들이고 명령어 스크립트의 시작 부분부터 명령을 처리하기 시작한다.
  5. sed 편집기는 headr 단어가 들어 있는 다른 줄을 찾을 수 없으므로 더 이상 지워지는 줄은 없다.

멀티라인 버전의 다음 줄 명령


한 줄 버전의 다음 줄 명령은 텍스트의 다음 줄을 sed 편집기의 처리 영역(패턴 영역)으로 옮겨 놓는다. 멀티라인 버전의 다음 줄 명령(대문자 N)은 텍스트의 다음 줄을 패턴 영역에 추가시킨다.

이렇게 되면 같은 패턴 영역에 데이터 스트림의 텍스트 두 줄을 추가한 효과가 있다. 텍스트의 줄은 여전히 줄 바꿈 문자로 구분되지만 sed 편집기는 이제 텍스트의 두 줄을 한 줄 처럼 처리할 수 있다.

Note: 단일 줄 버전의 명령(소문자 n)은 텍스트의 다음 줄에 대해서만 패턴 영역에 추가한다. Note: 멀티라인 버전의 명령(대문자 n)은 텍스트의 다음 줄을 결합한 데이터 스트림을 패턴 영역에 추가한다. 패턴 영역에 추가된 다음 줄은 다시 패턴 영역에 추가되지 않는다.

예제 1: 특정 단어가 포함된 텍스트 줄을 찾고 그 줄과 다음 줄을 결합하여 줄바꿈 문자를 빈칸으로 바꾼다.

$ cat data2.txt 
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.

$ $ sed '/first/{ N; s/\n/ /g}' data2.txt
This is the header line.
This is the first data line. This is the second data line.
This is the last line.
  1. sed 편집기는 first라는 단어가 포함되어 있는 텍스트 줄을 검색한다.
  2. 이러한 줄을 발견하면 그 줄과 다음 줄을 결합하는 N 명령을 사용한다.
  3. 바꾸기 명령(s)으로 줄바꿈 문자를 빈 칸으로 바꾼다.

예제 2: 데이터 파일의 두 줄에 나뉘어 있는 텍스트 구문을 찾아 변경

문제
$ cat data3.txt 
On Tuesday, the Linux System
Administrator\'s group meeting will be held.
All System Administrators should attend.
Thank you for your attendance.

$ $ sed 's/System Administrator/Desktop User/' data3.txt 
On Tuesday, the Linux System
Administrator\'s group meeting will be held.
All Desktop Users should attend.
Thank you for your attendance.
  • 문구가 한 줄에 들어 있으면 아무런 문제가 없다. 바꾸기 명령은 텍스트를 바꿀 수 있다.
  • 그러나 문구가 두 줄로 나뉘어 있는 상황이라면 바꾸기 명령은 일치하는 패턴을 인식하지 못한다.
개선 1

N 명령은 이 문제를 해결하는데 도움이 된다.

$ sed '{ N; s/System.Administrator/Desktop User/ }' data3.txt 
On Tuesday, the Linux Desktop User\'s group meeting will be held.
All Desktop Users should attend.
Thank you for your attendance.
  • 첫 단어를 찾은 줄을 다음 줄과 결합하기 위해 N 명령을 사용함으로써 두 줄로 나뉘어 있는 문구를 찾아낼 수 있다.
  • 바꾸기 명령은 SystemAdministrator 단어 사이에 빈 칸 혹은 줄바꿈이 들어있을 경우를 모두 인식할 수 있는 와일드카드 패턴(.)을 사용하고 있다.
  • 와일드카드가 줄바꿈 문자와 일치할 경우에는 이를 문자열에서 제거하며 SystemAdministrator 단어 사이에 빈 칸 문자 이루어진 한 줄로 결합된다.
개선 2

한 줄로 결합된 결과는 정확하게 원하는 바가 아닐 수 있다. 이 문제를 해결하기 위해 두 개의 바꾸기 명령을 사용할 수 있다. 하나는 여러 줄에 걸쳐 있는 검색어를 찾고, 또 하나는 한 줄에 들어 있는 검색어를 찾기 위해서다.

$ sed '
> N
> s/System\nAdministrator/Desktop\nUser/
> s/System Administrator/Desktop User/
> ' data3.txt
On Tuesday, the Linux Desktop
User\'s group meeting will be held.
All Desktop Users should attend.
Thank you for your attendance.
개선 3

하지만 이 스크립트에는 아직 문제가 남아 있다. 텍스트의 마지막 줄에 다다르면 읽을 수 있는 다음 줄이 없으므로 N 명령은 sed 편집기를 중단시킨다. 일치하는 텍스트가 데이터 스트림의 마지막 줄에 있으면 명령은 일치하는 데이터를 잡아낼 수 없다.

$ sed '
> s/System Administrator/Desktop User/
> N
> s/System\nAdministrator/Desktop\nUser/
> ' data4.txt
On Tuesday, the Linux Desktop
User\'s group meeting will be held.
All Desktop Users should attend.
  • 한 줄 버전의 명령을 N 명령 앞으로 옮겨 놓고 멀티라인 명령이 N 명령 다음에 나타나도록 한다.
  • 이제 데이터 스트림 마지막 줄에 있는 구문을 찾는 명령이 잘 동작하고 멀티라인 바꾸기 명령은 데이터 스트림 중간에 나타나는 구문을 다룬다.

멀티라인 삭제 명령


sed 편집기는 패턴 영역의 첫 번째 줄만을 지우는 멀티라인 삭제 명령(D)을 제공한다. 이 명령은 줄바꿈 문자까지에 있는 모든 문자를 지우며 줄바꿈 문자도 지운다.

$ cat data4.txt 
On Tuesday, the Linux System
Administrator\'s group meeting will be held.
All System Administrators should attend.

$ sed '
> N
> /System\nAdministrator/D
> ' data4.txt
Administrator\'s group meeting will be held.
All System Administrators should attend.

예제 1: 데이터 스트림의 첫 번째 줄 앞에 나타나는 빈 줄을 제거

$ cat data5.txt 

This is the header line.
This is a data line.

This is the last line.
$ sed '/^$/{ N; /header/D }' data5.txt
This is the header line.
This is a data line.

This is the last line.
  • sed 편집기 스크립트는 빈 줄을 찾으면 N 명령으로 다음 줄을 패턴 영역에 추가한다.
  • 패턴 영역의 새로운 내용이 header 단어를 포함하는 경우 D 명령은 패턴 영역의 첫 번째 줄을 없앤다.

멀티라인 출력 명령


이 명령은 패턴 영역의 여러 줄 가운데 첫 번째 줄만 출력한다. 이 명령은 패턴 영역에서 줄바꿈 문자에 이를 때까지 모든 문자를 포함한다.

$ sed -n '
N
/System\nAdministrator/P
' data4.txt
On Tuesday, the Linux System
  • 멀티라인 패턴 일치가 이루어지면 P 명령은 패턴 영역의 첫 줄만 인쇄한다.

대기 영역

패턴 영역은 명령을 처리하는 동안 sed 편집기가 검사할 텍스트를 보유하는 활성 버퍼 영역이다. 그러나 sed 편집기가 텍스트 저장을 위해 쓸 수 있는 공간이 패턴 영역 하나만 있는 것은 아니다.

sed 편집기는 대기 영역이라는 또 다른 버퍼 영역을 이용한다. 패턴 영역에서 텍스트의 줄을 가지고 작업한느 동안 다른 텍스트 줄을 임시로 보관하기 위해 대기 영역에 쓸 수 있다. 아래에 대기 영역 운영과 관련된 다섯 가지 명령어가 나와있다.

  • h: 패턴 영역을 대기 영역으로 복사한다.
  • H: 패턴 영역을 대기 영역에 추가한다.
  • g: 대기 영역을 패턴 영역으로 복사한다.
  • G: 대기 영역을 패턴 영역에 추가한다.
  • x: 패턴 영역과 대기 영역을 맞바꾼다.

다음은 sed 편집기의 버퍼 영역과 대기 영역사이에서 데이터를 주고 받기 위해 h 및 g 명령을 사용하는 방법을 보여주는 간단한 예제이다.

$ cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
$ $ sed -n '/first/{ h; p; n; p; g; p }' data2.txt
This is the first data line.
This is the second data line.
This is the first data line.
  1. sed 스크립트는 단어 first를 포함하는 줄을 찾기 위한 정규표현식을 사용한다.
  2. 단어 first를 포함하는 줄이 나타나면 {} 안의 첫 명령인 h 명령은 이 줄을 대기 영역에 둔다.
  3. 다음 명령인 p 명령은 패턴 영역에 있는 내용을 출력한다.
  4. n 명령은 데이터 스트림에서 다음 줄을 가져온 다음 패턴 영역에 놓는다.
  5. p 명령은 패턴 영역의 두 번째 줄을 출력한다.
  6. g 명령은 대기 영역의 내용(첫 번째 줄)을 패턴 영역에 놓는다.
  7. p 명령은 패턴 영역에서 현재 내용을 출력한다. 여기서 다시 첫 번째 데이터 줄이 출력된다.

명령을 부정형으로 만들기

느낌표 명령(!)은 명령을 부정형으로 만들기 위해서 쓰인다. 이 명령은 보통이라면 명령이 활성화되어야 할 때 명령이 활성화되지 않는다는 것을 뜻한다.

다음은 이 기능을 보여주는 예제이다.

$ sed -n '/header/!p' data2.txt
This is the first data line.
This is the second data line.
This is the last line
  • 보통의 p 명령이라면 data2 파일 안에서 header 단어가 포함된 줄만 출력할 것이다.
  • 느낌표를 추가하면 결과가 반대가 된다. 즉, 단어 header가 포함된 줄을 제외한 모든 줄이 출력된다.

느낌표는 여러 가지로 유용하게 적용될 수 있다. 앞에서 데이터 스트림 마지막 줄에서는 다음 줄이 없었기 때문에 N 명령이 재구실을 못했던 상황이 있었다. 이 문제를 해결하기 위해 느낌표를 사용할 수 있다.

$ sed 'N
> s/System\nAdministrator/Desktop\nUser/
> s/System Administrator/Desktop User/
> ' data4.txt
On Tuesday, the Linux Desktop
User\'s group meeting will be held.
All System Administrators should attend.

$ sed '$!N
s/System\nAdministrator/Desktop\nUser/
s/System Administrator/Desktop User/
' data4.txt
On Tuesday, the Linux Desktop
User\'s group meeting will be held.
All Desktop Users should attend.
  • 이 예에서는 달러 기호($) 특수 주소 뒤에 N 명령과 함께 느낌표를 썻다.
  • 달러 기호는 데이터 스트림에서 텍스트의 마지막 줄을 뜻하므로 sed 편집기는 마지막 줄에 이르렀을 때 N 명령을 실행하지 않는다.
  • 하지만 다른 모든 줄에 대해서는 명령을 실행한다.

예제 1: 데이터 스트림에서 텍스트 줄의 순서를 뒤집기(마지막 줄이 처음에, 첫 줄이 마지막에)

느낌표 명령을 사용하면 데이터 스트림에서 텍스트 줄의 순서를 반대로 하는데 활용할 수 있다.

이와 같은 일을 하기 위해 필요한 패턴은 다음과 같다.

  1. 패턴 영역에 한 줄을 놓는다.
  2. 이 줄을 대기 영역으로 옮겨놓는다.
  3. 패턴 영역에 다음 줄의 텍스트를 놓는다.
  4. 대기 영역을 패턴 영역에 추가시킨다.
  5. 패턴 영역에 있는 모든 내용을 대기 영역으로 옮겨 놓는다.
  6. 모든 줄을 대기 영역에 역순으로 놓게 될 때가지 3에서 5사이의 작업을 되풀이한다.
  7. 이 줄들을 가져와 출력한다.

⚠️주의할 점

  • 이 기법을 사용할 때에는 처리되는 순서대로 줄을 표시하지 않는 게 좋다. 즉 sed에서 -n 커맨드라인 옵션을 사용한다.
  • 대기 영역의 텍스트를 패턴 영역 텍스트에 추가하는 방법은 G(대문자 g) 명령을 사용한다.
  • 첫 번째 텍스트 줄을 처리할 때에는 대기 영역의 텍스트를 추가하지 않아야 한다. 이는 느낌표 명령을 사용하면 쉽게 해결된다.
  • 새로운 패턴 영역을 대기 영역으로 옮기려면 h 명령을 사용한다.
$ sed -n '{ 1!G; h; $p }' data2.txt
This is the last line.
This is the second data line.
This is the first data line.
This is the header line.
  • 1!G: 첫 번째 줄에서는 대기 영역으로부터 텍스트 줄을 가져오는 G 명령이 활성화되지 않는다.
  • h: 패턴 영역의 텍스트 줄을 대기 영역으로 옮긴다.
  • $p: 패턴 영역에 놓인 텍스트 줄이 마지막 줄이면 출력한다.

Note: bash 쉘 명령 중 tac 명령은 텍스트 파일을 뒤집는 기능을 수행할 수 있다. 아마도 이 명령의 이름이 꽤 센스 있다는 사실을 눈치 챌 수도 있을 것이다. 이 명령은 cat 명령의 기능을 반대로 수행할 수 있기 때문이다.

흐름 바꾸기

sed 편집기 명령 스크립트의 흐름을 변경하는 구조적 프로그래밍 환경과 같은 결과를 만들기 위한 방법을 제공한다.

분기


sed 편집기는 주소, 주소 패턴, 주소 범위에 기반을 두고 명령의 전체 부분을 무효화시키는 방법을 제공한다. 이 기능으로 데이터 스트림의 특정한 부분에만 명령의 세트를 실행하도록 할 수 있다.

분기 명령의 형식은 다음과 같다.

[address]b [label]
  • address 매개변수는 분기 명령을 작동시키는 줄들을 지정한다.
  • label 매개변수는 분기할 위치를 지정한다. label 매개변수가 존재하지 않는다면, 분기 명령은 스크립트의 끝으로 간다.
  • label은 콜론으로 시작하고 길이는 7자까지 쓸 수 있다.
$ cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.

# ㅣable 지정 X
$ sed '{ 2,3b; s/This is/Is this/; s/line./test?/ }' data2.txt
Is this the header test?
This is the first data line.
This is the second data line.
Is this the last test?

# Lable 지정 O
$ sed '{ /first/b jump1; s/This is the/No jump on/
> :jump1
> s/This is the/Jump here on/ }' data2.txt
No jump on header line.
Jump here on first data line.
No jump on second data line.
No jump on last line.
  • label이 지정되지 않은 경우 분기 명령은 스크립트의 끝으로 가며 바꾸기 명령은 실행되지 않는다.
  • label이 지정되면 분기 명령은 지정된 레이블으로 건너뛴다.

테스트


분기 명령과 비슷하게 테스트 명령(t)도 sed 편집기 스크립트의 흐름을 바꾸기 위해 사용된다. 주소를 기반으로 레이블로 건너뛰는 대신 테스트 명령은 바꾸기 명령의 결과에 바탕을 두고 레이블로 건너뛴다.

바꾸기 명령이 성공적으로 일치하는 패턴을 찾고 바꾸기를 수행하면 테스트 명령은 지정된 레이블로 분기한다. 바꾸기 명령은 지정된 패턴과 일치하지 않으면 테스트 명령은 분기되지 않는다.

분기 명령과 마찬가지로 사용자가 레이블을 지정하지 않으면 테스트가 성공했을 때 sed는 스크립트의 끝 지점으로 분기한다.

$ sed '{
> s/first/matched/
> t
> s/This is the/No match on/
> }' data2.txt
No match on header line.
This is the matched data line.
No match on second data line.
No match on last line.
  • 첫 번째 바꾸기 명령이 성공하면 스크립트의 끝 지점으로 분기하고 두 번째 바꾸기 명령은 건너뛴다.
  • 첫 번째 바꾸기 명령이 패턴과 일치하지 않는 경우 분기가 일어나지 않고 두 번째 바꾸기 명령이 수행된다.

패턴으로 바꾸기

예를 들어 줄에서 일치하는 단어 주위에 겹따옴표를 붙이고 싶다고 가정해보자. 대조할 패턴으로 단지 하나의 단어를 찾는 것이라면 무척 간단하다.

$ echo "The cat sleeps in his hat." | sed 's/cat/"cat"/'
The "cat" sleeps in his hat.

하지만 둘 이상의 단어와 일치하는 패턴에 와일드카드 문자(.)를 사용하는 경우라면?

$ echo "The cat sleeps in his hat." | sed 's/.at/".at"/g'
The ".at" sleeps in his ".at".

대체 문자열은 ‘at’ 앞에 무엇이든 한 글자가 오면 일치하는 점(.) 와일드카드 패턴을 사용했다. 하지만 대체 문자열은 일치하는 단어의 값에 대응하지 않는다.

& 사용


이를 위한 해법이 있다. 앰퍼샌드 기호(&)는 바꾸기 명령에서 대응되는 패턴을 표시하기 위해 사용된다. 어떤 텍스트든 정의된 패턴과 일치한다면 앰퍼샌드 기호를 써서 대체 패턴에 이를 불러 올수 있다.

$ echo "The cat sleeps in his hat." | sed 's/.at/"&"/g'
The "cat" sleeps in his "hat".

개별 단어 바꾸기


앰퍼샌드 기호는 바꾸기 명령에서 지정한 패턴과 일치하는 전체 문자열을 검색한다. 그 문자열 가운데 일부만 가져와야 할 떄도 있다.

sed 편집기는 대체 패턴 안에서 일부 문자열을 정의하기 위해 괄호를 사용한다. 그런 다음 대체 패턴에 특수 문자를 사용하면 각각의 문자열 구성 요소를 참조할 수 있다.

대체 문자는 백슬래시와 숫자로 구성되어 있다. 숫자는 각 문자열 구성 요소의 위치를 나타낸다. sed 편집기는 첫 번째 구성요소를 \1 문자에 할당하고, 두 번째 구성요소를 \2에 할당하는 식으로 계속해 나간다.

Note: 대체 명령에 괄호를 사용할 때에는 이것이 일반 괄호 문자가 아니라 문자들을 묶기 위해 쓴다는 뜻으로 이스케이프 문자를 사용해야 한다. 이는 다른 특수문자를 이스케이플할 때와는 반대 개념이다.

$ echo "The System Administrator manual" | sed 's/\(System\) Administrator/\1 User/'
The System User manual
  • 이 바꾸기 명령은 단어 System 주위에 괄호를 두름으로써 부속 문자열의 구성 요소를 식별한다.
  • 그 다음 처음으로 식별된 구성요소를 불러오기 위해서 대체 패턴에 \1을 사용한다.

예제: 와일드카드 패턴으로 작업 할때 부속 문자열의 특정 구성요소만 변경하기

$ echo "That furry hat is pretty" | sed 's/furry \(.at\)/\1/'
That hat is pretty

$ echo "That furry cat is pretty" | sed 's/furry \(.at\)/\1/'
That cat is pretty

이 상황에서 앰퍼샌드 기호를 사용하면 일치하는 패턴 전체를 바꾸므로 사용할 수 없다. 이때 부속 문자열 구성요소는 해답을 제공해준다. 패턴의 어떤 부분을 대체 패턴에서 사용할지 선택할 수 있다.

래퍼 사용하기

sed 편집기 스크립트를 실제 사용하기는 복잡하고 성가신 면이 있는데 특시 스크립트가 길어지면 더욱 귀찮다고 느낄 수 있을 것이다. 사용할 때마다 전체 스크립트를 다시 입력하는 대신 SED 편집기 명령을 쉘 스크립트에 넣을 수 있다.

쉘 스크립트 안에 명령을 넣고 나면 일반적인 쉘 변수와 매개변수를 sed 편집기 스크립트에 쓸 수 있다. 다음은 sed 스크립트의 입력으로 커맨드라인 매개변수를 사용하는 예다.

$ cat reverse.sh 
#!/bin/bash
# Shell wrapper for sed editor script
#               to reverse text file lines.
#
sed -n '{ 1!G; h; $p }' $1

$ ./reverse.sh data2.txt 
This is the last line.
This is the second data line.
This is the first data line.
This is the header line.
  • 쉘 스크립트는 데이터 스트림의 텍스트 줄들을 역순으로 바꾸기 위해 sed 편집기 스크립트를 사용한다.
  • 스크립트는 커맨드라인에서 첫 번째 파라미터를 가져오기 위해 $1 쉘 매개변수를 사용하며, 이는 역순으로 바꿀 파일의 이름이 입력된다.

이제 매번 커맨드라인에 전체 명령을 다시 입력할 필요 없이, 어떤 파일에 대해 sed 편집기 스크립트의 입력으로 사용할 수 있다.

📖출처

리처드 블룸, 크리스틴 브레스, ⌜리눅스 커맨드라인 쉘 스크립트 바이블⌟, 스포트라잇북(2016)


👍 개인 공부 기록용 블로그입니다. 오류나 조언이 있으시면 언제든지 댓글 혹은 메일로 남겨주시면 감사하겠습니다! 😄

맨 위로 이동하기

Linux 카테고리 내 다른 글 보러가기

댓글남기기