이 시리즈의 원 저자인 Tom Ryder의 허락을 받고 올리는 번역글입니다. IDE가 할 수 있는 기능을 UNIX 계열의 shell 안에서도 원활하게 할 수 있는 비결을 초보자도 알기 쉽게 잘 설명한 글일 뿐만 아니라 UNIX 자체의 철학이나 기본 사용법을 따라잡기에도 굉장히 좋은 글이라 생각되어 우리말로 옮기고자 합니다. 프로그래밍 용어는 웬만하면 원래 영단어로 쓰겠습니다. 언제든지 더 좋은 표현에 대한 의견은 감사합니다.



UNIX as IDE: Files

2012년 2월 10일 Tom Ryder가 작성


IDE의 한 가지 두드러진 특징은 파일 관리를 위한 내장 시스템이다. 여기서 파일 관리는 이동, 이름 바꾸기 및 삭제와 같은 기본 기능과 compiling 및 syntax 검사와 같은 개발 관련 기능을 포함한다. 또한 확장자나 파일 크기에 따라 여러 파일을 찾는다든지, 특정 패턴을 갖고 있는 파일을 검색하는 것도 유용한 기능이 될 것이다. 이 글에서는 대부분의 GNU/Linux 사용자가 익숙하게 사용할 수 있는 도구를 사용해서 프로젝트의 여러 파일을 다루는 방법을 살펴보려 한다.



파일 리스트 보기

ls는 아마도 사용자가 디렉토리 내 파일 목록을 보기 위해 배우게 되는 가장 첫 번째 명령일 것이다. 그리고 -a-l 옵션은 대부분 알텐데, 숨겨진 파일(dot files)을 포함하고 상세 정보를 표시하는 것이다.


-al 보다는 덜 사용되는 옵션들이긴 하지만, 프로그래밍을 하면서 꽤나 유용하게 사용할 수 있는 것들이 있다.

  • -t — 최근 변경일 기준으로 최신 순서로 정렬, 가장 최근에 변경된 파일의 quick list를 보는 데 유용한데, 특히 headseq 10q와 pipe로 연결될 수 있다. 아마도 -l과 궁합이 제일 잘 맞을 듯. 가장 오래된 파일을 보고 싶다면 -r로 정렬 순서를 뒤집으면 된다.
  • -X — 확장자 별로 그룹을 묶는다. Polyglot 프로젝트에서 잘 쓰일 수 있다. 헤더와 소스 파일, 소스 파일과 빌드 파일 또는 디렉토리 등을 구분 짓고 싶을 때도 쓰인다.
  • -v — 파일명에 있는 버전 숫자대로 정렬한다.
  • -S — 파일 크기 별로 정렬.
  • -R — 하위 폴더까지 모두 리스트. -l과 함께 less와 같은 pager에 pipe 연결로 잘 쓰인다.


명령의 결과로 볼 수 있는 목록은 결국 텍스트다. 다른 텍스트와 다를 것이 전혀 없다. 따라서 이 명령의 결과를 Vim 프로세스에 넘겨서(pipe) 각 파일들에 대한 추가 사항을 적어서 README로 저장할 수도 있다.

$ ls -XR | vim -


이런 작업들은 조금만 공을 들이면 자동화할 수 있는데, 나중에 다루도록 하겠다.



파일 찾기

(위에서 ls와 pipe을 통해서 여러 작업을 했지만) 재밌게도, 하위 폴더를 포함한 모든 파일 리스트를 군더더기 없이 깔끔하게 find . 만으로도 확인할 수 있다. 아래 예제는 그 결과를 다시 sort에 pipe로 넘긴 결과이다.

$ find . | sort
.
./Makefile
./README
./build
./client.c
./client.h
./common.h
./project.c
./server.c
./server.h
./tests
./tests/suite1.pl
./tests/suite2.pl
./tests/suite3.pl
./tests/suite4.pl



ls -l 스타일의 목록을 보고 싶다면, -ls 옵션을 줘서 GNU find(1) 결과물에 어떤 action을 줄 수도 있다.

$ find . -ls | sort -k11,11
1155096    4 drwxr-xr-x   4 tom      tom          4096 Feb 10 09:37 .
1155152    4 drwxr-xr-x   2 tom      tom          4096 Feb 10 09:17 ./build
1155155    4 -rw-r--r--   1 tom      tom          2290 Jan 11 07:21 ./client.c
1155157    4 -rw-r--r--   1 tom      tom          1871 Jan 11 16:41 ./client.h
1155159   32 -rw-r--r--   1 tom      tom         30390 Jan 10 15:29 ./common.h
1155153   24 -rw-r--r--   1 tom      tom         21170 Jan 11 05:43 ./Makefile
1155154   16 -rw-r--r--   1 tom      tom         13966 Jan 14 07:39 ./project.c
1155080   28 -rw-r--r--   1 tom      tom         25840 Jan 15 22:28 ./README
1155156   32 -rw-r--r--   1 tom      tom         31124 Jan 11 02:34 ./server.c
1155158    4 -rw-r--r--   1 tom      tom          3599 Jan 16 05:27 ./server.h
1155160    4 drwxr-xr-x   2 tom      tom          4096 Feb 10 09:29 ./tests
1155161    4 -rw-r--r--   1 tom      tom           288 Jan 13 03:04 ./tests/suite1.pl
1155162    4 -rw-r--r--   1 tom      tom          1792 Jan 13 10:06 ./tests/suite2.pl
1155163    4 -rw-r--r--   1 tom      tom           112 Jan  9 23:42 ./tests/suite3.pl
1155164    4 -rw-r--r--   1 tom      tom           144 Jan 15 02:10 ./tests/suite4.pl

-k 옵션을 통해서 11번째 열을 기준으로 (파일 이름 기준) 정렬한 것이다.


find는 복잡한 필터 문맥을 가지고 있다. 아래에 가장 유용할 수 있는 예시를 모아봤다.

  • find . -name '*.c' — shell-style 패턴에 맞는 파일 이름으로 찾는다. 대소문자를 구분하지 않으려면 -iname
  • find . -path '*test*' — shell-style 패턴에 맞는 파일 경로로 찾는다. 대소문자를 구분하지 않으려면 -ipath
  • find . -mtime -5 — 변경 날짜가 최근 5일 안에 있는 파일을 찾는다. 그 반대로, 변경된 지 5일이 지난 파일을 찾으려면 +5
  • find . -newer server.cserver.c보다 최근에 변경된 파일을 찾는다.
  • find . -type d — 디렉토리를 대상으로 찾는다. -type f는 파일 대상으로,-type l는 바로가기를 대상으로 찾는다.

이 명령 옵션들이 모두 조합될 수 있다는 점도 알아두자. 예를 들어 ...

$ find . -name '*.c' -mtime -2


find의 기본 action으로 그 결과를 standard output에 보여주지만, 다른 유용한 action도 있다.

  • -ls — 위에서 소개한 대로 ls -l 스타일로 리스트 출력, (GNU find(1)) 참조
  • -delete — 찾은 파일 삭제
  • -exec — 파일에 대해 원하는 명령을 실행시키는데, {}를 찾은 파일로 대체해서 명령에게 알려준다. 그리고 \;로 명령의 끝을 알려준다. 예를 들면

    $ find . -name '*.pl' -exec perl -c {} \;


    명령어를 단 한 번만 실행시키면서 find에서 찾은 모든 파일을 넘겨주고 싶다면 +로 명령의 끝을 알리면 된다. 아래 예시는 찾은 파일을 모두 vim의 쪼개진 창으로 여는 명령이다.

    $ find . -name '*.c' -exec vim {} +


필자의 추가 사항: 과거에 find의 결과물에 이은 action을 위해서 xargs를 소개했었다. 하지만 대부분의 경우에는 xargs가 필요하지 않을 뿐 더러, -exec 또는 while read -r 반복문을 이용하는 것이 훨씬 안전한 방법이다.



파일 검색

파일 자체의 내용으로 검색하기 보다는 파일 내용에 대해 검색하고 싶을 때가 더 많다. 이럴 때 grep -R을 쓸 수 있는데, 하위 폴더를 포함해서 someVar가 쓰여진 파일들을 찾는다.

$ grep -FR someVar .


grep은 기본적으로 대소문자를 구별하기 때문에, 대소문자를 구분하지 않는 옵션도 잊으면 안된다.

$ grep -iR somevar .


또한 -l 옵션을 통해 상세 정보 없이 파일 경로만 출력할 수 있다.

$ grep -lR someVar .


위 명령의 결과를 이용해서 스크립트를 짠다거나 batch job을 돌린다면, while loop에 read를 통해 파일 이름에 있는 공백 및 여타 특수 문자를 다룰 수 있다.

grep -lR someVar | while IFS= read -r file; do
    head "$file"
done



버전 관리 도구를 이용해서 프로젝트를 진행하고 있다면, .svn, .git, .hg와 같은 버전 메타데이터 디렉토리가 grep 결과에 포함된다. 이를 해결하기 위해서는 -F로 메타데이터 폴더를 찾고 -v를 통해 그 결과를 제외시키면 된다.

$ grep -R someVar . | grep -vF .svn

--exclude--exclude-dir 옵션을 탑재한 버전의 grep이라면, 위 명령보다는 훨씬 깔끔하게 쓸 수 있을 것이다.


한편, 새로운 grepack라는 이미 유명한 프로그램도 있다. ack은 기본적으로 버전 관리 도구가 만들어내는 메타데이터 내용을 기본적으로 제외시킨다. 많은 프로그래머들이 선호하는 Perl 호환 정규 표현식 (PCRE)을 사용할 수도 있다. 만약 ack를 설치할 수 있는 환경이라면 ack을 꽤나 추천하고 싶다. 물론 우리의 오래된 친구 grep이 잘못한 것은 없고 항상 grep은 우리 곁에 있겠지만, ack은 소스 코드로 작업하는 일반적인 상황에서 유용한 기능이 많기 때문이다. 데비안에는 ack-grep이라는 데비안 패키지가 있는데, Perl 스크립트라는 것이 신경쓰이지만 않는다면 매우 간단하게 설치할 수 있다.


UNIX 순수주의자(purist)들은 클래식 grep에 비해서 새로운 Perl 스크립트를 언급하는 것만으로도 불만스러울 지 모르지만, 나는 UNIX 정신을 가지면서 새로운 문제를 해결할 수 있는 대안이 있는데 옛 것을 고집하는 것이 유닉스 철학이라고 생각하지 않으며, UNIX as IDE에 맞는 태도라고도 생각하지 않는다.



파일 metadata

file 도구는 파일의 확장자, 헤더 등을 기반으로 한 줄 요약 정보를 보여준다. find와 연계해서 낯선 파일을 조사할 때 유용하게 쓰인다.

$ find . -exec file {} +
.:            directory
./hanoi:      Perl script, ASCII text executable
./.hanoi.swp: Vim swap file, version 7.3
./factorial:  Perl script, ASCII text executable
./bits.c:     C source, ASCII text
./bits:       ELF 32-bit LSB executable, Intel 80386, version ...




파일 매칭

마지막 팁을 주자면, bash의 패턴 찾는 방법과 {}를 이용한 확장 방법을 알아두는 것을 추천한다. 이와 관련한 필자의 다른 글 Bash shell expansion을 참조바란다.

이 글에서 다룬 기능을 통해 클래식 UNIX shell이 프로젝트 파일을 관리하는 데 꽤나 강력한 모습을 보여줄 수 있다.


2017년 4월 필자의 추가사항: 대부분의 find 예제를 POSIX 호환되도록 변경했음



UNIX as IDE




'GNU-Linux' 카테고리의 다른 글

UNIX as IDE: 4. Compiling  (0) 2017.11.06
UNIX as IDE: 3. Editing  (0) 2017.11.06
UNIX as IDE: 1. Introduction  (0) 2017.11.06
Vim 사용자가 되기 위한 첫 걸음 (.feat tmux)  (1) 2017.10.06
Linux 초보의 tmux (terminal multiplexer) 입문  (6) 2017.10.05