sed 로 처리하기 어려운 경우, perl 로는 손쉽게 가능.

shell script 를 사용해서, 완벽(효율면은 아니고 효과에서)하게 처리했다고 생각했는데.. 엉뚱한 쪽에서 오류가 발생했다.

m3u 파일을 편집/수정하는 스크립트를 만들었다.
m3u 를 사용하다보면, 실제로는 이미 지워진 음원이 m3u 안에 그대로 남아있는 경우가 종종 생긴다. 이런 ‘유령’이 몇몇 남아있다고 해도 mp3 재생기에서 오류를 내지는 않지만, 만약 m3u 내에 있는 목록 모두가 실제로 존재하지 않을 경우엔 결국은 에러가 표시된다.

아무튼 이런 상황을 정리하기 위해 스크립트를 만들었다.
생각한 방식은 다음과 같다.

  • m3u 에 있는 파일 목록을 읽고, find 로 실제 그 파일이 존재하는지 확인한다.
  • 존재하지 않을 경우 sed 를 사용해 그 목록을 지운다.

아주 간단한데, 문자열을 취급하다보니 역시나 이스케이핑이 문제가 됐다.


내게 골치를 안겨준 파일이 하나 있었는데, 그 파일명은 이랬다.

Hits [Kim Carnes] – Will you remember me.mp3

파일명에 대괄호가 들어가 있는게 나를 괴롭힌 원인이 됐다.

먼저, find 는 저 파일을 찾질 못한다.

find . -name 'Hits [Kim Carnes] - Will you remember me.mp3'

현재 디렉토리에 저 파일이 틀림없이 존재함에도 불구하고 찾질 못한다. 구글에서 이 부분을 열심히 찾아봤지만 답을 얻어내진 못했다.
어쩔 수 없이 Escaping 하여 처리했더니 제대로 작동했다.

foo=$(printf "%q" "Hits [Kim Carnes] - Will you remember me.mp3")
find . -name $foo
./Hits [Kim Carnes] - Will you remember me.mp3

이 글을 쓰고 난 뒤 문득 떠올랐다.
파일의 존재 여부를 find 로 찾으려했던 내가 얼마나 무식했었는지..
Shell 에는 이 경우를 대비한 test 가 있다.
[[ -f "$full_song_list" ]] 등으로 하면 되는 걸..


그 뒤, 만약 이 파일이 없다면 sed 명령을 사용해서 지우려고 했는데, 역시 동일한 이유(대괄호)로 오류가 발생했다.

regex 에서 pattern 에 사용되는 대괄호는 Character Class 로 사용되어 특수한 의미를 띄게 된다. 따라서 역시 이스케이핑 해야 하는데, printf %q 로 만든 문자열로는 sed 의 시험(?)을 통과하지 못했다.

sed 에서는, 앞 대괄호만 이스케이프를 해줘야 하고, 뒤 대괄호는 그냥 놔둬야 한다.
즉,

실패 : Hits\ \[Kim\ Carnes\]\ -\ Will\ you\ remember\ me.mp3
성공 : Hits\ \[Kim\ Carnes]\ -\ Will\ you\ remember\ me.mp3

이렇게 되어야만 하는데, 이걸 자동으로 해줄 수 있는 방법을 찾질 못했다. 문자열을 내가 손으로 입력한다면 저게 가능하겠지만, 자동화해야 하는데, 도무지 해법을 찾을 수가 없었다.

그러다가 발견한게 Perl 이다.
Perl 에선, pattern 내에 \Q 를 써줌으로써 이스케이프 문제를 해결할 수 있다.
\Q 이후에 나오는 모든 문자는 문자 그대로 인식한다. (regex 특수 문자로 인식하지 않는다.)

따라서 위 sed 명령을 Perl 로 하면 다음과 같이 바꿔줄 수 있다.

perl -pi -e "s/\QHits [Kim Carnes] - Will you remember me.mp3\E//" file

위 명령의 경우 \E 는 쓰지 않아도 되지만, \E 는 \Q 를 끝내라는 뜻이 된다.
\E 뒤에는 다시 일반 regex 문자들을 사용할 수 있게 된다.


Perl 은 이제 막 시작했고, 그야말로 속성 흡입 및 수박 겉핥기만 했으므로, 아직 뭔가가 잡히진 않지만, Perl 로 인해 적어도 sed 를 고집할 이유는 없게 됐다.

앞으로 또 어떤 흥미진진한 일들이 생길지..?

Author: 아무도안

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