유닉스 팁: 10가지 유닉스 사용 습관 (한글)
잘못된 유닉스 사용 패턴 고치기
요약: 유닉스® 명령어(Unix Command Line) 조작에 있어서 효율성을 높일 수
있는 10가지 좋은 습관들을 익히고, 그릇된 사용 패턴을 고쳐봅시다. 본 기사는 유닉스 명령어 조작에 있어서 충분히 유용한
것인데도, 간과되는 기술들을 설명합니다. 또한, 본 기사에서 열거한 일반적인 에러 유형과 이를 해결하는 방법을 배우다 보면, 좋은
습관을 들이는 것이 왜 중요한지를 알게 될 것입니다.
원문 게재일: 2007 년 1 월 30 일
난이도: 중급
페이지뷰: 6459 회
난이도: 중급
페이지뷰: 6459 회
시스템을 사용할 때, 일정한 사용 패턴으로 빠지는 경향이 있고, 가끔은 가장 좋은 방법을 제쳐놓을 때도 있다. 심지어, 좋지 않은 방법을 선택할 때도 있다. 이러한 모순을 타파하는 가장 좋은 방법은 좋은 습관을 의식적으로 익히는 것이다. 이 글에서는, 10 가지 좋은 명령행 사용 습관들을 제안한다. 일반적인 오류를 없앨 수 있는, 효과적인 명령행 작동을 위한 좋은 습관들이다. 이제부터 상세하게 설명하겠다.
10가지 좋은 습관
10가지 좋은 습관은 다음과 같다.
- 단 한번에 디렉토리 트리(directory Tree)를 만들어라.
- 압축 파일을 이동하지 말고, 경로를 변경하라.
- 명령어와 컨트롤 오퍼레이터(control operator)를 함께 사용하라.
- 쿼트 변수(Quote Variable)사용에 유의하라.
- 이스케이프 시퀀스(escape sequence)를 사용하여 긴 명령을 관리한다.
- 명령어들을 하나의 리스트로 그룹핑 한다.
-
find
밖에서xargs
를 사용한다. -
grep
이 카운팅을 수행할 때와, grep을 배제할 때를 알아야 한다. - 라인이 아닌, 아웃풋에 특정 필드를 매치한다.
-
cat
파이핑을 삼가하라.
Listing 1은 유닉스 습관을 보여주고 있다. 디렉토리 트리를 한번에 하나씩 정의하고 있다.
Listing 1. 나쁜 습관 #1: 디렉토리 트리를 개별적으로 정의하고 있다.
~ $ mkdir tmp ~ $ cd tmp ~/tmp $ mkdir a ~/tmp $ cd a ~/tmp/a $ mkdir b ~/tmp/a $ cd b ~/tmp/a/b/ $ mkdir c ~/tmp/a/b/ $ cd c ~/tmp/a/b/c $ |
mkdir
에 -p
옵션을 사용하고, 하나의 명령어에 모든 부모 디렉토리와 자식 디렉토리를 만드는 것이 훨씬 빠르다. 하지만 이 방법을 알고 있는 관리자 조차도 이렇게 하지 않는다. 좋은 습관이 몸에 배도록 노력해야 한다.Listing 2. 좋은 습관 #1: 하나의 명령어로 디렉토리 트리를 정의한다.
~ $ mkdir -p tmp/a/b/c |
이 옵션을 사용하여 전체적인 디렉토리 트리들을 만들 수 있다. 이것은 단순한 계층뿐만 아니라, 스크립트 내에서도 사용하기에 알맞다. 예를 들어:
Listing 3. 좋은 습관 #1: 하나의 명령어로 복잡한 디렉토리 트리를 정의한다.
~ $ mkdir -p project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a} |
디렉토리들을 개별적으로 정의하는 것이 허용되는 때는,
mkdir
가 이 옵션을 지원하지 않았을 때이지만, 대부분의 시스템에서는 이제 지원된다. Single UNIX Specification에 순응하는 IBM, AIX®, mkdir
, GNU mkdir
등은 이제 이 옵션이 지원된다. 이 기능이 없는 일부 시스템의 경우, 같은 기능을 수행하는
mkdir
용 래퍼(wrapper)인, mkdirhier
스크립트 (참고자료)를 사용한다.~ $ mkdirhier project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a} |
압축 파일을 이동하지 말고, 경로를 변경하라.
또 하나의 나쁜 습관은 '.tar'와 같은 압축 파일을 해당 디렉토리에 압축을 풀려고 하는 경우, '.tar' 파일을 디렉토리에 옮겨 놓고 해당 디렉토리에서 압축 푸는 이중 작업을 하는 것이다.절대로 이렇게 해서는 안된다. 원하는 디렉토리에 .tar 압축 파일을 추출할 수 있다.
-C
옵션이 하는 일이 바로 이것이다. 아래 Listing 4에서 알 수 있듯이, tar 명령어에서 -C
옵션은 원하는 디렉토리에 '.tar' 압축 파일을 풀 수 있게 한다.Listing 4. 좋은 습관 #2:
-C
옵션을 사용하여 .tar 압축 파일의 압축을 푼다. ~ $ tar xvf -C tmp/a/b/c newarc.tar.gz |
-C
(-c)는 아카이브 파일을 추출 대상 장소로 옮기고, 그 디렉토리를 변경하고, 내용의 압축을 풀 때 도움이 된다. 압축 파일을 지정 디렉토리에 풀려고 할 때, -c 옵션을 쓰는 습관을 들이는 것이 좋다.명령어와 컨트롤 오퍼레이터(control operator)를 함께 사용하라.
대부분의 쉘에서, 명령어들 사이에 세미콜론(;)이 있다면 이것이 명령어 결합이라는 것쯤은 알고 있을 것이다. 이 세미콜론은 쉘 컨트롤 오퍼레이터(control operator)이 고, 여러 다른 명령어들을 하나의 명령행에 배열하는데 유용하지만, 모든 것에 적용되는 것은 아니다. 예를 들어, 세미콜론을 사용하여 두 개의 명령어들을 결합한다면, 첫 번째 명령어가 성공적으로 완료되었을 경우에만 두 번째 명령어가 올바르게 실행된다. 첫 번째 명령어가 예상했던 대로 종료되지 않았다면, 두 번째 명령어는 실행되지만, 실패하게 된다. 대신, 더 적합한 컨트롤 오퍼레이터(일부는 이 글에서 설명하겠다.)를 사용하라. 쉘이 지원하기만 한다면, 이러한 것을 사용하는 습관을 들이는 것이 좋다.
한 명령어가 Zero Exit Status를 리턴할 경우에만 다른 명령어가 실행된다.
&&
컨트롤 명령어를 사용하여 두 명령어들을 결합하면, 두 번째 명령어는, 첫 번째 명령어가 Zero Exit Status를 리턴할 때에만 실행된다. 다시 말해서, 첫 번째 명령어가 성공적으로 실행되면, 두 번째 명령어도 실행된다. 첫 번째 명령어가 실패하면, 두 번째 명령어는 전혀 실행되지 않는다. 예를 들어,Listing 5. 좋은 습관 #3: 컨트롤 오퍼레이터를 사용하여 명령어들을 결합한다.
~ $ cd tmp/a/b/c && tar xvf ~/archive.tar |
이 예제에서, 아카이브의 내용들은, (디렉토리가 존재한다면) ~/tmp/a/b/c 디렉토리로 추출된다. 이 디렉토리가 없다면,
tar
명령어는 실행되지 않고, 어떤 것도 추출되지 않는다.다른 명령어가 Non-zero Exit Status를 리턴할 경우에만 명령어를 실행한다.
마찬가지로,
||
컨트롤 오퍼레이터는 두 개의 명령어를 분리하고, 첫 번째 명령어가 Non-zero Exit Status를 리턴할 때에만 두 번째 명령어를 실행한다. 다시 말해서, 첫 번째 명령어가 성공하면, 두 번째 명령어는 실행되지 않는다. 첫 번째 명령어가 실패하면, 두 번째 명령어가 실행된다. 이 오퍼레이터는 해당 디렉토리가 존재하는지 여부를 검사할 때 사용되고, 없을 경우에는 디렉토리를 만든다.Listing 6. 좋은 습관 #3: 컨트롤 오퍼레이터로 명령어들을 결합한다.
~ $ cd tmp/a/b/c || mkdir -p tmp/a/b/c |
이 섹션에 설명된 컨트롤 오퍼레이터들을 결합할 수도 있다. 각각 마지막 명령어 실행 시 작동한다.
Listing 7. 좋은 습관 #3: 컨트롤 오퍼레이터로 명령어들을 결합한다.
~ $ cd tmp/a/b/c || mkdir -p tmp/a/b/c && tar xvf -C tmp/a/b/c ~/archive.tar |
쿼트(quote) 변수 사용에 유의하라!
쉘 확장과 변수 이름에 언제나 주의하라. 특별한 이유가 없다면, 더블 쿼테이션 마크로 변수 호출을 표시하는 것이 좋다. 마찬가지로, 변수 이름 뒤에, 영숫자(alphanumeric) 텍스트를 붙인다면, 변수 이름에 중괄호({})를 사용하여 이것을 주위 텍스트와 구분한다. 그렇지 않으면, 뒤따르는 텍스트를 변수 이름의 일부로 인터프리팅 하고, 대게는 null 값을 리턴할 것이다. Listing 8은 변수에 쿼테이션을 붙이거나 붙이지 않을 경우의 예제와, 그 결과이다.
Listing 8. 좋은 습관 #4: 변수에 쿼테이션 마크 달기 또는 달지 않기
~ $ ls tmp/ a b ~ $ VAR="tmp/*" ~ $ echo $VAR tmp/a tmp/b ~ $ echo "$VAR" tmp/* ~ $ echo $VARa ~ $ echo "$VARa" ~ $ echo "${VAR}a" tmp/*a ~ $ echo ${VAR}a tmp/a ~ $ |
이스케이프 시퀀스를 사용하여 긴 인풋을 관리한다.
백슬래시(\)를 사용하여 긴 라인을 다음 라인으로 넘기는 코드 예제를 보았을 것이다. 그리고, 대부분의 쉘에서는 백슬래시가 붙은 연속 라인들을 하나의 긴 라인으로 취급한다는 것도 알고 있다. 하지만, 터미널이 멀티 라인(multi-line) 래핑을 적절하게 다루지 못하거나, 명령어 라인이 평균 이하로 작을 때(프롬프트에 긴 경로가 있을 경우,) 백슬래시는 더욱 유용하게 사용된다. 이처럼 백슬래시는 또한 긴 인풋 라인에도 유용하게 사용된다.
Listing 9. 좋은 습관 #5: 긴 인풋에 백슬래시를 사용한다.
~ $ cd tmp/a/b/c || \ > mkdir -p tmp/a/b/c && \ > tar xvf -C tmp/a/b/c ~/archive.tar |
대신, 다음과 같은 설정도 유효하다.
Listing 10. 좋은 습관 #5: 긴 인풋에 백슬래시를 사용한다.
~ $ cd tmp/a/b/c \ > || \ > mkdir -p tmp/a/b/c \ > && \ > tar xvf -C tmp/a/b/c ~/archive.tar |
인풋 라인이 여러 라인들로 나뉘지만, 쉘은 언제나 이것을 하나의 연속 라인으로 취급한다.
주: 대부분의 쉘에서, 위쪽 화살표 키를 누르면, 전체 멀티 라인 엔트리가 하나의 긴 인풋 라인으로 다시 모아진다.
명령어들을 하나의 리스트로 그룹핑 한다.
대부분의 쉘은 명령어들을 하나의 리스트로 그룹핑 하여, 모든 아웃풋을 파이프라인 밑으로 전달하거나, 스트림의 전체 또는 일부를 같은 장소로 보낸다. 일반적으로, 하위 쉘에서 명령어 리스트를 실행하거나, 현재 쉘에서 명령어 리스트를 실행한다.
하위 쉘에서 명령어 리스트를 실행한다.
명령어 리스트를 하나의 그룹으로 묶을 때 괄호를 사용한다. 명령어를 새로운 하위 쉘에서 실행하고, 아웃풋을 리다이렉션 하거나 모을 수 있다.
Listing 11. 좋은 습관 #6: 하위 쉘에서 명령어 리스트를 실행한다.
~ $ ( cd tmp/a/b/c/ || mkdir -p tmp/a/b/c && \ > VAR=$PWD; cd ~; tar xvf -C $VAR archive.tar ) \ > | mailx admin -S "Archive contents" |
이 예제에서, 아카이브의 콘텐트는 tmp/a/b/c/ 디렉토리로 추출되지만, 추출된 파일 리스트를 포함하여, 그룹으로 나뉜 명령어들의 아웃풋은
admin
주소로 메일링 된다.명령어 리스트의 환경 변수를 다시 정의하고 있고, 이것이 현재 쉘에 적용되는 것을 원치 않을 경우, 하위 쉘을 사용하는 것이 좋다.
현재 쉘에서 명령어 리스트를 실행한다.
현재 쉘에서 실행할 명령어 리스트를 중괄호({})로 묶는다. 괄호 사이에 스페이스와 실제 명령어가 들어가도록 한다. 그렇지 않으면, 쉘은 괄호를 올바르게 인터프리팅 하지 않는다. 또한, 리스트의 마지막 명령어는 세미콜론으로 끝나야 한다.
Listing 12. 좋은 습관 #6: 현재 쉘에서 명령어 리스트를 실행한다.
~ $ { cp ${VAR}a . && chown -R guest.guest a && \ > tar cvf newarchive.tar a; } | mailx admin -S "New archive" |
find 밖에서 xargs를 사용한다.
find
명령어에서 모은 아웃풋을 활용하기 위한 필터로서 xargs
툴을 사용하라. find
는 일정 기준에 맞는 파일 리스트를 제공한다. 이 리스트는 xargs
로 전달되고, 이것은 인자로서 그 파일 리스트와 함께 기타 유용한 명령어를 실행한다.Listing 13.
xargs
툴의 기본적인 사용 예제~ $ find some-file-criteria some-file-path | \ > xargs some-great-command-that-needs-filename-arguments |
하지만,
xargs
를 find
의 헬퍼 정도로만 생각하지 말라. 이것은 활용도가 너무 낮다. 이것을 사용하는 습관을 들이면, 다음과 같은 사용법을 포함하여 무엇이든 할 수 있다.공백으로 구분된(space-delimited) 리스트를 전달한다.
가장 단순한 호출에서,
xargs
는 리스트(한 라인에 각 멤버를 갖고 있음)를 인풋으로서 취하는 필터와 같다. 이 툴은 그러한 멤버들을 공백으로 구분된(space-delimited) 라인에 놓는다.Listing 14.
xargs
툴의 아웃풋 예제~ $ xargs a b c Control-D a b c ~ $ |
xargs
를 통해 파일 이름을 만들어 내는 툴의 아웃풋을 보내서, 파일 이름을 인자로서 취하는 다른 툴에 대한 인자 리스트를 얻을 수 있다.Listing 15.
xargs
툴 사용 예제~/tmp $ ls -1 | xargs December_Report.pdf README a archive.tar mkdirhier.sh ~/tmp $ ls -1 | xargs file December_Report.pdf: PDF document, version 1.3 README: ASCII text a: directory archive.tar: POSIX tar archive mkdirhier.sh: Bourne shell script text executable ~/tmp $ |
xargs
명령어는 파일 이름을 전달하는 것 외에도, 유용하게 쓰인다. 텍스트를 한 라인으로 필터링 해야 한다면 언제든지 사용하라.Listing 16. 좋은 습관 #7:
xargs
툴을 사용하여 텍스트를 한 줄로 필터링 한다.~/tmp $ ls -l | xargs -rw-r--r-- 7 joe joe 12043 Jan 27 20:36 December_Report.pdf -rw-r--r-- 1 \ root root 238 Dec 03 08:19 README drwxr-xr-x 38 joe joe 354082 Nov 02 \ 16:07 a -rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar -rwxr-xr-x 1 \ joe joe 3239 Sep 30 12:40 mkdirhier.sh ~/tmp $ |
xargs
를 사용할 때 주의할 점 드물긴 하지만,
xargs
사용할 때 문제가 생기는 경우도 있다. 기본적으로, "end-of-file" 스트링은 언더스코어(_)이다. 이 문자가 인풋 인자로서 보내지면, 이 뒤에 모든 것이 무시된다. 이것에 대한 대응책으로서, -e
플래그를 사용한다.grep이 카운팅을 수행할 때와, grep을 배제할 때를 알아야 한다.
아웃풋 라인의 수를 세기 위해서
grep
을 wc -l
로 파이핑(piping) 하지 말라. -c
옵션이 특정 패턴과 매치되는 라인의 수를 제공하고, 이것이 일반적으로 wc
파이핑 보다 빠르다.Listing 17. 좋은 습관 #8: grep을 사용하여 라인을 수를 센다.
~ $ time grep and tmp/a/longfile.txt | wc -l 2811 real 0m0.097s user 0m0.006s sys 0m0.032s ~ $ time grep -c and tmp/a/longfile.txt 2811 real 0m0.013s user 0m0.006s sys 0m0.005s ~ $ |
속도 외에도,
-c
옵션은 카운팅 능력도 탁월하다. 여러 파일들이 있을 경우, grep
과 -c
옵션은 각 파일에 대한 개별 카운트를 각 라인에 하나씩 리턴하는 반면, wc
에 대한 파이프는 결합된 모든 파일들에 대한 총계를 리턴한다.하지만, 속도와 상관 없이, 이 예제에는 또 다른 에러가 있다. 이 카운팅 메소드는 매칭(matching) 패턴들을 포함하고 있는 라인의 수만 카운팅 한다. 이것을 원했다면 괜찮다. 하지만, 라인들에 특정 패턴에 대한 여러 인스턴스들이 있는 경우, 이러한 메소드들은 실제 매칭 인스턴스들의 수를 리턴하지 않는다. 인스턴스의 수를 카운트 하려면,
wc
를 사용하여 카운트 해야 한다. 우선, 버전이 지원한다면, grep
명령어와 -o
옵션을 실행하라. 이 옵션은 각 라인에 하나씩, 매칭 패턴만 제공한다. 하지만, -c
옵션과 함께 사용할 수 없기 때문에, wc -l
을 사용하여 라인을 카운트 한다.Listing 18. 좋은 습관 #8: grep으로 패턴 인스턴스를 카운트한다.
~ $ grep -o and tmp/a/longfile.txt | wc -l 3402 ~ $ |
이 경우,
wc
호출은, 각 라인을 찾아 카운팅 하기 위해 사용된 더미 패턴을 이용한 grep
호출(grep -c
) 보다 빠르다. 단순한 라인이 아닌, 아웃풋에 특정 필드를 매치한다.
단순한 라인이 아닌, 아웃풋 라인에 있는 특정 필드에만 있는 패턴과 매치해야 할 때,
grep
보다 awk
같은 툴이 더욱 선호된다.다음 예제는 12월(December)에 수정된 파일들만 리스트 하는 방법이다.
Listing 19. 나쁜 습관 #9: grep을 사용하여 특정 필드에 있는 패턴을 찾는다.
~/tmp $ ls -l /tmp/a/b/c | grep Dec -rw-r--r-- 7 joe joe 12043 Jan 27 20:36 December_Report.pdf -rw-r--r-- 1 root root 238 Dec 03 08:19 README -rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar ~/tmp $ |
이 예제에서,
grep
은 라인을 필터링 하면서, 날짜와 이름에 Dec
가 있는 모든 파일들을 만들어 낸다. 따라서, 1월(January) 이후에는 수정되지도 않은 December_Report.pdf 같은 파일도 매칭된다. 이것은 우리가 원한 것은 아니다. 특정 필드에서 패턴을 찾으려면, awk
를 사용하는 것이 더 낫다. 여기에서 관계형 오퍼레이터가 정확한 필드를 매치한다.Listing 20. 좋은 습관 #9: 특정 필드에서 패턴을 찾을 때
awk
를 사용한다.~/tmp $ ls -l | awk '$6 == "Dec"' -rw-r--r-- 3 joe joe 5096 Dec 14 14:26 archive.tar -rw-r--r-- 1 root root 238 Dec 03 08:19 README ~/tmp $ |
awk
에 대한 자세한 내용은 참고자료를 참조하라.cat 파이핑을 금한다.
가장 기본적이면서도 일반적인
grep
사용에 대한 오류는 단일 파일의 내용을 검색함에 있어서 cat
의 결과를 파이핑하는 예가 있다. 즉, grep
은 파일 이름을 인자로 취하기 때문에 굳이 cat
명령어를 취하는 것은 시간낭비이며 불필요한 습관이다. 아래의 Listing 21은 그의 일례를 보여준다. Listing 21. 좋은 습관과 나쁜 습관 #10:
cat
없이 grep 사용하기와 cat과 함께 grep사용하기~ $ time cat tmp/a/longfile.txt | grep and 2811 real 0m0.015s user 0m0.003s sys 0m0.013s ~ $ time grep and tmp/a/longfile.txt 2811 real 0m0.010s user 0m0.006s sys 0m0.004s ~ $ |
위와 같은 실수는 다른 명령어에도 범하기 쉬운 케이스이다. 대부분의 명령어는 하이픈(-)를 이용하여 표준 입력(standard input)을 인자로 취하기 때문에, 여러 파일을
stdin
에 배치하기 위해 cat
을 사용할 필요가 없는 것이다. 즉, cat
이후 파이핑을 사용할 때는 오로지 "여러 가지 필터링 옵션과 더불어 cat을 사용할 때"만 사용하라.결론: 좋은 습관 들이기
자신의 명령행 사용 패턴을 한번 점검해 보라. 나쁜 습관이 본인의 성장을 방해하고, 예기치 못한 에러를 만들 수 있다. 이 글에서는 일반적인 사용 오류를 타파할 수 있는 새로운 습관들을 소개했다. 이러한 좋은 습관들을 익혀서 여러분의 유닉스 명령행 스킬이 더욱 향상될 수 있기를 바란다.
댓글 없음:
댓글 쓰기