파일 내에서 특정 문자열을 찾아 바꾸고 싶은데.. 첫번째.

이게 말은 쉬운데, 생각보다 꽤 까다로운 작업이다.
간단하게 생각하면, grep 로 할 수는 있다.

grep -rw '/path/' -e 'pattern'
  • -r : Recursive
  • -w : 단어만 매치. (문구를 포함하는 단어 찾지 않음)

경로에 있는 모든 파일이 텍스트 파일일 경우엔 별 문제가 없다. 그러나, 만약 이진파일까지 포함되어 있다면??


저기까지 생각하고 나서, grep 를 좀 더 뒤져봤어야 했는데, 엉뚱한 방향으로 틀게 된 바람에 딴 우물만 들입다 파버렸다.
늘 기억하라. 파랑새는 언제나 가까이 있는 법.

정리!
경로 내 모든 ‘텍스트’ 파일 내에서 특정 문자열을 찾고 싶다면?

grep -rwI '/path/' -e 'pattern'

-I 를 추가하면 된다. --binary-files=without-match 와 같은 뜻으로, 이진파일은 넘기라는 뜻이다.

위 명령은 해당 조건을 만족하는 파일명과, 파일 내용(줄(行))까지 표시를 해준다. 내용은 필요없고, 파일명만 원한다면, l 을 추가한다.

grep -rwlI '/path/' -e 'pattern'

복잡한 Regex 를 쓰기 위해선 w 가 오히려 방해가 될 수도 있다. 또, Perl Regex 를 쓴다든가, 기타등등 다른 Regex 를 쓴다면, 이런 형식이 요구된다. 아래는 Perl 을 사용할 때의 예다.

grep -rIP '/path/' -e 'pattern'

여기에 l 을 붙이느냐 마느냐 정도를 택할 수 있겠다.
여기서 끝?


‘찾기’만을 한다면 위 명령으로 끝이다. 그런데, ‘바꾸기’까지 해야한다면?
이제 sed 가 등장할 차례다.

sed 도 찾기 기능이 있는데, 굳이 grep 를 먼저 쓴 까닭은, 이진파일을 걸러내기 위함이다. sed 에는 이진파일을 구분해주는 기능이 없는 듯 하다. 따라서, grep 로 먼저 작업을 해주고, 문자열 교체만 sed 로 넘겨주는게 훨씬 효율면에서 나아 보인다.

기본 명령은 위와 같은데, 파일명만 표시되게끔 l 을 사용한다. 그리고 sed 로 ‘바꾸기’ 작업을 시행한다.
경로 위치가 위와 다른데, 어디 있으나 별 관계는 없다.

grep 'path' -rlIe 'pattern' | xargs sed 's/pattern/pattern2/g'

위 명령은, 실제로 파일에 변경 내용을 쓰지는 않는다. 그냥 Standard Out, 즉 화면으로만 해당 내용을 출력해줄 뿐이다. 실제로 저장을 하기 위해선 다음과 같이 -i 를 붙여줘야 한다.

grep ~/.atom/ -rlIe '/oldid\b' | xargs sed -i 's./oldid\b./newid.g'

위 내용은, 새롭게 우분투를 설치하고, ID 가 바뀌었을 때, 기존에 사용하던 atom 설정파일을 새 위치에 옮긴 후, 설정파일을 검색하여 예전 ID 를 새 ID 로 바꿔주는 명령이다.

  • /oldid\b 를 찾아, /oldid 를 찾되, /oldid 로 시작하는 다른 문구들은 찾지 않게 한다. /oldid1, /oldid_abc 등등은 찾지 않는다. 앞에 / 를 붙인 이유는, 경로로만 사용된 oldid 를 찾기 위함이다. 즉, /home/oldid 등으로만 쓰여진 oldid 만을 찾는다. 좀 더 확실하게 /home/oldid\b 로 해도 될 듯 하다.
  • l이 있기 때문에, 찾은 결과의 파일명이 stdout 으로 출력되는데, Pipeline 을 사용하여 stdin 으로 바꿔준다. 이때, xargs 를 사용하지 않고 그냥 sed 를 사용하면, 넘어온 stdin 을 ‘경로명’이 아닌 그냥 문자열로 인식한다.
  • sed 로 바꾸기를 시행하고, 내용을 저장한다. s 이후 구분자는 보통 슬래시를 많이 쓰는데, 여기서는 슬래시 자체를 찾고 바꿔야 하므로, 구분자를 마침표로 했다. 슬래시를 구분자로 하려면 복잡한 이스케이핑이 들어가야 해서 많이 헷갈린다.

다만, 위 명령에는 어디선가 엉뚱한 데이터를 바꿔치기할 가능성은 있다. 위험을 최소화하려면 Regex 를 좀 더 정교하게 제련(?)할 필요가 있다.

‘파일 내에서 특정 문자열을 찾아 바꾸고 싶은데.. 두번째’에선, grep 의 사용법을 제대로 알기 전, 다소 단순 무식하게 해보려고 했던 작업에 대해 아주 간단하게만 정리해보도록 하겠다.

2 Comments

안녕하세요. 글 남겨주셔서 고맙습니다.