무작정 따라해 볼 것들

이제 시스템에 들어가고 나오고 하는 일을 할 수 있게 되었다. 그런데 그냥 나오는 것을 목적으로 어디에 들어가는 경우는 없을 것이다. 또 칼잡이에 비유하자면, 이왕 칼을 뽑은 김에 모기라도 쳐야 하지 않겠는가? 앞으로 만나게 될 악당 내지 괴물들을 상상하며, 여기서는 몇몇 작은 벌레들이라도 잡아보도록 하자.

쉘 프롬프트

조금 전에 $라는 문자가 US dollar를 의미하는 것이 아니라 쉘 프롬프트를 의미한다고 강조했다. $ 문자 만으로 전체 쉘 프롬프트를 가리키기도 하지만 이 문자 왼쪽에 있는 문자와 기호들도 매우 기본적이면서 중요한 정보를 알려준다.

쉘 프롬프트 [jedi@cpu jedi]$
[ ]

이건 그냥 보기 좋으라고 출력된 것 같다 ;-)

jedi@cpu

jedi at cpu, 즉 cpu라는 시스템에 jedi라는 이용자가 접속되었다는 것을 의미한다. C P U 시스템은 실제로 cpu.phys.cau.ac.kr라는 도메인으로 등록된 GNU/Linux 시스템이며 그 첫 식별자인 cpu가 프롬프트에 포함되는 것이다. GNU/Linux 시스템을 설치하며 특별히 이름을 붙여주지 않았다면 그것은 localhost.localdomain이라는 기본 이름을 가지게 된다. 즉, 프롬프트의 시작은 jedi@localhost.

jedi

이것은 현재 디렉토리를 알려준다. 실제 로그인 직후의 디렉토리는 /home/jedi - 강조한 것처럼 긴 디렉토리 경로의 마지막 식별자만을 보여주고 있다. 만약 $ cd /usr/local/share/doc라는 명령을 실행했다면 (cd - change directory) 마지막의 jedi는 doc으로 바뀌게 된다.

$

bash(Bourne Again SHell)가 이렇게 널리 쓰이고 있는 상황에도 불구하고, 이 세상에는 bash 말고도 수많은 쉘이 있어서 본연의 임무를 다하고 있다. $ 문자는 bash 쉘 임을 나타내며 참고로 % 문자는 csh, tcsh 계열임을 나타낸다.

명령어의 동작

command / option / parameter(file, string)

유닉스 계열 시스템의 명령어는 KISS - Keep It Simple and Small 주의에 입각하여(?) 만들어졌다. 예를들어 한 멍청한 로봇에게 "여기와서 이 부품을 저기로 옮기라"는 작업을 지시하기 위해 do_it_all이라는 기본 명령어를 만들지 않는다. 유닉스 명령어를 만드려면 이 모든 과정을 위해 come / get / hold / go / set / report (오고/집고/들고/가고/놓고/보고함) 등을 각각 따로 구현하려는 생각을 해야 한다. 이 "작고 단순한" 명령들이 입출력에 일정한 규칙을 가지고 있어서 서로 연계되어 동작할 수 있다면 더 좋을 것이다.[4]

여기에서 끝나면 너무 아쉬울 것 같아 한 가지 더, 유닉스 명령어들의 특징을 알려 주겠다. 이것들이 원칙적으로 "한 번에 한 가지씩 일만" 한다고 했는데, 불행히도 한 가지 일이라도 제대로 하려면 온갖 세부적인 사항들을 챙겨야 한다; 이를테면 "온다(come)"라는 동작 하나만 봐도, 뛰어 올 것인지 걸어 올 것인지, 앞으로 올 것인지 뒤로 올 것인지, 심지어 다 왔다는 보고를 할 것인지 말 것인지, ... 아무튼 너무나 따져줄 것이 많다. 유닉스 명령어마다 다양하게 추가적으로 가질 수 있는 이런 정보들은 옵션(options) 및 대상파일(files)로 구성되는 명령행인자(command-line arguments)로 주어진다.

명령어를 이용하는 형식들
$ ls

이것은 가장 단순한 이용이다. 만약 어떤 명령어 또는 어플리케이션이 아무런 선택사항 없이 동작할 수 없다면 이렇게 단순히 쓰는 것은 확실히 잘못된 이용이다. 이때는 대개 정확한 이용법을 알려주는 메시지를 출력한다. 예를들어 $ cp 명령을 해보라.

$ ls -a 또는 $ ls --all

그냥 이용해도 되는 명령어 ls를 추가적인 옵션과 함께 이용한 경우다. 각 명령어는 자신이 처리할 수 있는 옵션일 경우엔 정의되어 있는 동작을 하지만, 처리할 수 없는 엉뚱한 옵션일 경우엔 역시 올바른 이용법을 상기시켜 주려 한다.

-a 옵션과 --all 옵션은 완전히 똑같은 동작을 수행한다. 똑같은 일을 하면서 왜 두 가지 다 되도록 만들었을까? 개인적인 생각; -a는 성미 급한 이용자들을 위해, 그리고 --all은 탐미주의적인 이용자들을 위해 준비된 것 같다. $ rm -fr / 라고 하는 것보다는 $ rm --force --recursive / 라고 하는 것이 더 아름답지 않은지?! (이 명령을 함부로 따라해선 안된다 ;-)

$ ls -l /usr

이번에는 명령어, 옵션, 그리고 적용대상 세 가지가 다 쓰였다. 적용대상이 잘못 되었을 경우에도 명령어 또는 쉘은 자상하게 그 사실을 지적해 준다. 대부분 명령어는 옵션과 적용대상의 순서에 개의치 않지만 그렇지 않은 것도 있다. (아래를 참조)

$ ls -al /usr /home

많은 명령어가 이렇게, 옵션과 대상을 복합적으로 쓸 수 있도록 만들어졌다. 또다른 예로 $ ps aux와 같이, 옵션을 의미하는 - 문자 없이 복합적인 옵션을 허용하는 경우도 있다.[5]

여러 개의 대상이나 복잡한 옵션을 이용할 때는 종종 명령을 하는 순서에도 주의해야 한다. 예를들어 어떤 대상을 새로운 대상으로 복사하거나 옮겨갈 때는 소스와 타겟을 명확히 해야 한다. 분명 대부분의 명령어들이 일정한 관례를 따르려고는 하지만 워낙 많은 경우의 수가 있어서 딱 잘라 말할 철칙은 없다. 익숙해질 때까지는 알아서 주의하길 바란다!

시스템에 대한 정보 알아내기

uname / ls cd / w who finger / free / ps top / uptime / df / set export alias

지금까지 쉘 프롬프트와 명령어의 이용형식에 대해 살펴 보았다. 이것으로 사실상 우리가 이용하는 칼에 대한 이해가 끝난 것이다. 실제로 이것을 휘둘러 보기 위해 기본 동작부터 익히자. 어떤 시스템에 처음 로그인했다면 마땅히 살펴보아야만 하는 몇 가지 내용이 있다. 시스템의 이름과 주소, 그리고 자기 자신의 이용자명과 패스워드는 이미 알고 있을 거고(잘 기억이 나지 않는다면 uname -a 명령으로 언제든 확인할 수 있음), 자신의 쉘도 일단 bash로 해두자.

시스템에 대한 기본 정보를 알아내는 명령들
$ ls

접속 후 최초의 명령, 명령 중의 명령, 앞으로 가장 많이 이용하게 될 명령이다!

대부분 시스템에서 ls -l, 즉 -l 옵션을 주는 경우에 한해 ll이라고만 해도 되도록 설정되어 있다. ll이라고 하면 좀 부담스러운(?) 결과가 나오는데, 지금은 일단 알만한 것들만 알아두자.

$ ls -l
drwxr-xr-x  2 luke  luke    4096  5월 20 00:21 driver/
drwxr-xr-x  2 luke  luke    4096  5월 20 00:21 fontmetric/
-rw-rw-r--  1 luke  luke   16762  6월  8 14:27 pspfontcache
*---------  ? user  group   size      date     name

ls -l 명령의 결과는 그 디렉토리의 내용물에 대한 자세한 정보며, 대개 다음의 순서로(한 행에 대해) 나타난다.

  • * (종류, kinds)
  • --------- (접근권한, permissions)
  • ? (하드 링크 갯수, hard links)
  • user (소유권자, owner)
  • group (그룹, group owner)

이들은 이 스터디를 진행하며 적당한 단계에서 하나씩 소개할 것이다. 뒤를 잇는 크기, 날짜/시간, 이름 등은 다들 짐작한 그대로다.

한편 ls -a라고 해보면 . 문자로 시작하는 파일과 디렉토리들도 함께 출력됨을 알 수 있는데 이것은 바로 "유닉스 계열 시스템에서 . 문자로 시작하는 파일과 디렉토리는 숨은(hidden) 파일"이기 때문이다. ls 명령 뿐만 아니라 그래픽 모드에서 동작하는 대부분의 어플리케이션도, 이용자가 특별한 옵션을 선택하지 않으면 . 문자로 시작하는 파일과 디렉토리를 보여주지 않는다 - 이것은 사실 어떤 운영체제가 숨은 파일을 비롯한 특수 목적의 파일을 다루는 전형적인 방식이다.

$ cd - 디렉토리 이동

어떤 디렉토리의 내용을 "보는" 명령인 ls와 비교할 수 있는 것은 어떤 디렉토리로 "가는" 명령인 cd다. 먼저 cd /, 즉 최상위 디렉토리로 이동해서 이 두 명령을 연습해 보기 바란다. 참고로 cd라고만 하면 어느 곳에서든 곧바로 자신의 홈디렉토리로 돌아오고, cd ..라고 하면 한 단계 상위 디렉토리로 이동한다.

한 단계 상위 디렉토리로 이동하는 것이 아니라 바로 직전의 cd 명령에 의한 디렉토리로 이동할 수도 있다. cd -라고 해 보자; 어떤 경로가 출력되면서 프롬프트가 바뀔 것이다. cd - 명령을 여러 번 해 보면 두 디렉토리 사이에서 우왕좌왕하는(?) 자신의 모습을 발견할 수 있을 것이다! :-) [6]

$ w

이 시스템에 누가 또 접속해 있는지 보여준다. who, 그리고 finger도 이와 비슷한 역할을 한다. 시스템에 계정을 가진 모든 사람들을 알아보려면 ls /home 정도면 되겠고[7], 특정 이용자에 대해 더 자세한 정보를 보려면 finger 이용자명이라고 해보자.

$ free

결과가 다소 복잡해 보이지만 이것은 시스템이 가진 기본 메모리와 가상 메모리에 대한 정보다. 시스템의 기본(실제) 메모리는 기계에 붙은 RAM 장치의 용량을 의미하며, 가상(스왑) 메모리는 하드디스크의 일부를 잘라서 마치 메모리처럼 이용하고 있는 용량을 말한다. 물론 하드디스크에 대한 - 읽고 쓰는 - 접근 속도는 RAM에 대한 그것보다 단연 느리다.

$ ps

현재 동작하는 프로세스(실행 중인 프로그램 정도로 알고 있자)를 보여주는 명령이다. 자주쓰는 형태는 ps aux, 이것은 시스템 전체의 프로세스에 대한 정보를 보여준다. a, u, x 세 가지의 옵션을 각각 따로 붙여서 연구해 보라.

지금 소개한 freeps 명령을 몇 초마다 한 번씩 계속해서 실행시키는 셈인 top 명령도 있다. 이것은 시스템을 감시하는(mornitoring) 역할을 하며 종료하기 위해 q를 누른다.

$ uptime

시스템이 재부팅 없이 얼마나 오랫동안 켜져 있었는지를 보여준다. 하루에 한 번씩 켜고 끄는 그런 시스템은 개인용도의 컴퓨터, PC이지만, 많은 GNU/Linux 시스템은 서버로서의 역할을 하고 있기 때문에 여느 회원제 웹싸이트 기계들과 마찬가지로 1주일 이상의 업타임을 갖는다.

$ df

시스템의 하드디스크가 어떻게 이용되고 있는지 보여준다. GNU/Linux 시스템은 유닉스와 마찬가지로 디스크 드라이브 개념이 없다. 시스템에는 하나의 논리적 최상위 디렉토리 / 가 있으며 모든 하위 디렉토리가 그것에서 가지쳐 나온다. 특별히 SWAP이라는 부분은 앞서의 free 명령에서도 보인 가상 메모리를 의미한다.

'disk free'를 의미하는 이 명령어도 있지만 'disk usage'를 의미하는 du 명령도 있다. 예를들어 자신의 홈 디렉토리에서 du라고 하면 - 쉽게 의미를 알 수 있을 것이다.

$ set

이것도 좀 부담스러운 결과를 출력하겠지만 자세히 들여다 보자. 분명 몇 가지는 어떤 의미를 가지고 있는지 추측할 수 있을 것이다. 이 set 명령은 시스템의 부팅 및 사용자 로그인 시에 export 명령에 의해 정의된 여러가지 환경 변수를 보여주고 있다. 두 명령은 적절한 문법에 의해 이용되어 새로운 어떤 변수를 정의할 수도 있는데 이렇게 그냥 쓰이면 현재 지정된 모든 값을 출력한다. (모든 값을 '쏟아 붓는' 이런 동작을 'dump'라고 함)

유사한 명령으로 alias라는 것도 있다. 이것은 주로 긴 명령과 옵션을 간단히 줄여서 쓸 수 있도록 해 준다. 앞서 ls -l 대신 ll 명령을 해도 동일한 결과를 얻을 수 있음을 소개했다. 물론 alias 명령을 내리면 이렇게 정의된 내역을 확인할 수 있다.

너무 긴 출력 줄 수

많은 명령어가 터미널 창의 기본 줄 수를 훨씬 넘어가는 출력을 해 버리기 때문에 화면이 위로 올라가 버릴 때가 많다. 이용하고 있는 터미널 어플리케이션에 따라 스크롤바가 있을 수도 없을 수도 있는데, 그런 것엔 상관없이 긴 출력을 한 화면씩 잘라서 보기 위해 more 명령을 이용한다.

예를들어 ps aux |more 같은 형식으로 이용한다. 사실 이 more는 파일의 내용을 읽기 위한 명령인데 표준 입출력을 서로 연결시켜 주는 유닉스 쉘의 기능인 파이프('|' 문자)를 통해 다른 명령의 긴 결과를 자기가 받고, 그것을 원래의 기능대로 끊어서 보여주는 것이다.

Bash 쉘 자체가 지원하는 기능인 Shift-PgUp/Dn를 이용하는 것도 좋다. GNU/Linux에서는 어떤 터미널 프로그램에서도 이 기능을 이용할 수 있지만 다른 시스템에서는 이용하는 터미널 프로그램에 따라 다르다.

Auto completion? 자동완성 기능!

조금 긴 디렉토리 경로를 찾아가는 다음과 같은 명령을 생각해 보자.

$ cd /home/cpu/The_CPU/han/study/fundamental/lookie/html/stylesheet-images/

이렇게 깊은 디렉토리 구조를 단 몇 초 만에 찾아갈 수 있다. 일일이 수많은 철자를 타이핑하거나 그래픽 파일관리자에서 마우스 클릭을 해대는 것으로는 따라올 수 없는 속도감! Bash를 비롯한 몇몇 쉘은 어떤 명령어 또는 파일이름에 대한 자동완성 기능을 가지고 있다. [8]

자동완성 기능을 이용하는 것은 너무나 간단하다. 명령어는 명령어대로, 또한 파일/디렉토리의 이름도 그 이름대로, 첫 글자만 입력하고 왼쪽 새끼 손가락으로 Tab키를 누르자. 만약 이용할 수 있는 명령어나 파일이름 중에 그 문자로 시작하는 다른 것이 없다면 나머지 철자들이 저절로 입력된다. 그러다 어느 위치에선가 멈추었다면 또 Tab키를 누른다. 지금까지 입력된 부분은 똑같지만 이후부터 철자가 다른 모든 경우가 보여질 것이다. 이때 원하는 이름, 즉 바로 다음 한 글자를 입력하고 또 Tab, ... 이런 식으로 하면 GNU/Linux 시스템의 디렉토리 구조가 복잡하고 파일 이름도 상당히 길다는 사실이 큰 문제가 되지 않는다.

$ clear - 화면 깨끗히 하기

화면이 가득차서 집중이 안되면 clear 명령을 이용한다. 이것도 입력하기 귀찮아 할까봐 쉘 단축키도 준비되어 있다 - Ctrl-L.



[8] 자동완성 기능은 굳이 쉘에서 뿐만 아니라 GNU/Linux 시스템에서 이용되는 대부분 소프트웨어들(그래픽 환경에서 돌아가는 것들도 포함)에서도 이용할 수 있다. 예를들어 어떤 소프트웨어의 파일 열기 대화상자에서 마우스 클릭으로 디렉토리를 옮겨다니거나 파일이름을 완성할 수 있지만, 그 대화상자 내부에 있는 텍스트 입력 필드에서 자동완성 기능을 이용해 일을 더 빨리 할 수도 있다.