sed를 이용한 텍스트 파일 바꾸기

쉘 스크립트를 만들 때, 텍스트 파일의 내용을 바꾸는 기능이 필요할 때가 있다. 이 경우 sed를 쓰는데, sed의 사용 방법이 헷갈릴 때가 많아 이 기회에 정리를 해 보고자 한다. 이 글에서는 기본적인 사용 방법과 내가 주로 사용하는 기능만 정리하였다. sed가 지원하는 전체 기능이 궁금하다면, man page(man sed)를 확인하는 것이 최선이다.

텍스트 바꾸기

기본적으로 sed를 이용해서 텍스트를 바꾸는 방법은 다음과 같다.

$ sed -i 's/<원래 내용>/<바꿀 내용>/g' <파일 이름>

여기서 -i, s/<원래 내용>/<바꿀 내용>/, g가 의미하는 내용은 다음과 같다.

  • -i: 변경할 내용을 그 파일에 바로 반영해서 저장한다.
  • s/<원래 내용>/<바꿀 내용>/: 원래 내용에 적은 것을 바꿀 내용으로 치환한다. 일반 텍스트 및 정규표현식을 쓸 수 있다.
  • g: 한 행에 같은 내용이 여러 번 나타나면, 모두 바꾼다. (없으면 그 행의 맨 처음으로 나오는 것만 바꾼다.)

정규표현식을 이용한 텍스트 바꾸기

위에서도 이야기 했듯 sed는 일반 텍스트 및 정규표현식을 이용하여 텍스트를 바꿀 수 있다. 정규표현식에 익숙하지 않다면 RegExr에서 연습해 보기를 추천한다.

예제 1: 패턴을 사용해서 텍스트 바꾸기

이런 파일이 있고, npm와 nvm을 모두 aaa로 바꾸고 싶다고 가정하자. (파일 이름은 test.txt로 저장했다)

 1021  nvm list
 1022  nvm use stable
 1023  npm list
 1024  npm list --global

nvm이나 npm의 차이는 중간에 있는 vp이다. 중간에 있는 한 글자 차이는 .을 이용해서 바꾸면 되므로 다음과 같이 실행하면 된다.

$ sed -i 's/n.m/aaa/g' test.txt

결과는 다음과 같다.

$ cat test.txt
 1021  aaa list
 1022  aaa use stable
 1023  aaa list
 1024  aaa list --global

예제 2: 패턴 안에 /와 같은 특수문자가 들어가는 경우

그런데, 내가 대체하려는 패턴 안에 /와 같은 특수문자가 들어가면 어떻게 해야 할까? 이 경우 \를 앞에 붙여주면 된다.

test.txt 파일에서 com/examplenet/example로 바꾸려고 한다.

원본 파일은 다음과 같다.

$ cat test.txt 
 1084  git clone https://github.com/example/caliper-data-example
 1085  git clone https://github.com/example/caliper-data-test.git
 1126  git clone https://github.com/example/caliper-dashboard-test.git
 1133  git remote add upstream  https://gitlab.com/example/caliper-python-example.git
 1138  git clone https://gitlab.com/example/caliper-python-example.git
 1139  git clone https://gitlab.com/example/caliper-dashboard-test.git

이 경우 sed를 실행할 때 /를 그대로 쓰게 되면 바꿀 패턴으로 인식하여 엉뚱한 결과가 발생할 것이다. 다음과 같이 / 앞에 \를 붙여서 바꿔준다.

$ sed -i 's/com\/example/net\/example/g' test.txt

결과는 다음과 같다.

$ cat test.txt
 1084  git clone https://github.net/example/caliper-data-example
 1085  git clone https://github.net/example/caliper-data-test.git
 1126  git clone https://github.net/example/caliper-dashboard-test.git
 1133  git remote add upstream  https://gitlab.net/example/caliper-python-example.git
 1138  git clone https://gitlab.net/example/caliper-python-example.git
 1139  git clone https://gitlab.net/example/caliper-dashboard-test.git

예제 3: 검색 결과의 일부를 대체할 내용에서 사용하고 싶을 때

패턴으로 검색한 결과의 일부를 대체할 내용에도 쓰고 싶을 때가 있을 것이다. 이 경우 그룹으로 만들 부분을 괄호로 묶어준다. (\(<패턴>\)과 같이 묶어주면 된다. ) 그리고 묶은 그룹은 \1과 같이 순서대로 쓰면 된다. (man page에서는 일치하는 하위 패턴을 가리킬 때 \1에서부터 \9까지 쓸 수 있다고 나와 있다.)

다음과 같은 텍스트 파일에서 pippython -m pip으로 바꾸려고 한다. (더 편한 방법도 있겠지만, 설명을 위해 다음 예제를 사용한다.)

$ cat test.txt
 1253  pip install selenium
 1254  pip freeze | grep selenium
 1255  pip install pytz

pip 부분을 \(pip\)으로 묶고, 대체할 내용에 \1을 넣어 찾은 패턴의 결과가 들어가도록 만들어 준다. 아래 내용을 실행한다.

$ sed -i 's/\(pip\)/python -m \1/g' test.txt

결과는 다음과 같다.

$ cat test.txt
 1253  python -m pip install selenium
 1254  python -m pip freeze | grep selenium
 1255  python -m pip install pytz

여담: macOS에서 invalid command code ., despite escaping periods 오류가 발생하는 경우

예전에 macOS에서 sed를 썼을 때, 위와 같은 명령을 썼음에도 불구하고 invalid command code ., despite escaping periods 오류가 발생한 적이 있다.

이 경우 다음과 같이 -i 뒤에 ''를 넣고, 패턴 앞에 -e 옵션을 넣어 해결하면 된다.

원래 명령: sed -i 's/n.m/aaa/g' test.txt macOS에서 쓰는 방법: sed -i '' -e 's/n.m/aaa/g' test.txt

여담2: ‘unterminated `s’ command’ 오류가 발생하는 경우

다음과 같이 썼을 때 ‘unterminated `s’ command’ 오류가 발생하는 경우가 있다.

sudo sed -i 's/#GRUB_GFXMODE=640x480/GRUB_GFXMODE=1024x768' /etc/default/grub

이 경우 아래와 같이 조건식 끝에 /를 하나 더 추가한다.

sudo sed -i 's/#GRUB_GFXMODE=640x480/GRUB_GFXMODE=1024x768/' /etc/default/grub

참고한 내용들