리눅스 명령어(Linux Command)



▶ pwd

- 현재 경로 보기

- usage : pwd

 

[root@localhost root]# pwd

/root

 

[root@localhost bin]# pwd

/bin

 

 

 

▶ cd

- 디렉토리 이동       //cd = change directory

- usage : cd [인자값]

   .   -> 현재 디렉토리

   ..  -> 상위 디렉토리

 

[root@localhost root]# cd /var

[root@localhost var]#

 

[root@localhost root]# cd ..

[root@localhost /]#

 

 

 

▶ ls

- 파일 내역  출력       //"ls -al"을 가장 많이 쓴다. 모든 파일을 자세히 출력 가능

- usage : ls [ option ] [ directory / file ]

 

 

 

▶ cp    //copy

- 파일, 디렉토리 복사     //상위 폴더로 복사하기 - # cp sample.txt ../

- usage : cp [ option ] [ source ] [ target ]

 

 

 

▶ mv    //move

- 파일, 디렉토리 이동

- usage : mv [ option ] [ source ] [ target ]

 

 

▶ mkdir    //make directory

- 디렉토리 생성

- usage : mkdir [ option ] [ directory name ]

 

 

 

▶ rmdir    //remove directory

- 디렉토리 삭제

- usage : rmdir [ option ] [ directory name ]

 

    추가적인 옵션 없이 그냥 # mkdir kkw  입력으로 폴더를 생성할 수 있다. 그리고 왠만하면 습관적으로 # echo $?를 써주는 것이 좋다고 한다. # echo $?이라는 것은 앞의 작업이 잘 수행되었는가를 확인하는 명령어다. 0이 출력되면 정상적으로 잘 수행 되었다는 말이고, 만약 0 이외의 수가 출력 된다면 무언가 문제가 생겼다는 것을 의미한다. 참고로 Linux에서는 숫자 2는 오류를 의미한다. # ls를 입력해서 파일 내역을 출력해 볼 수있다. 그 다음 kkw 폴더를 삭제하기 위해서는 위에서 설명한 rmdir이 있다. 하지만 실무에서는 삭제하려는 것이 있다면 거의 rm -rf * 를 많이 쓴다.  습관화 되면 오히려 편하게 쓰일 것이다.

 

 

 

▶ rm     //remove

- 파일, 디렉토리 삭제

- usage : rm [ option ] [ directory / file ]    //그냥 지우는 건 무조건 rm -rf로 삭제!

 

 

 

▶ cat    //remove

- 텍스트 파일 내용 출력

- usage : cat [ file name ]

- 인자값 > : 파일 내용 덮어 씌우기  

- 인자값 >> : 기존 파일 내용 추가

 

   cat > kevin redirection  명령으로 "i2sec" 3개를 입력하고 ctrl+D(저장 후 바꾸어 나오기) 입력한다. 다시 cat kevin으로 파일 내용을 출력 해보면 저장한 대로 "i2sec"이 저장되어 있다.

 

 

 

   > redirection 같은 경우에는 그냥 보내는거다. 초기화를 하면서 쓰는 것이 > redirection이고, 반면에 >> redirection은 기존에 것은 그대로 두고 append 한다는 의미를 가지고 있다. 기존의 "i2sec" 아래에 just testing, redirection이 추가된 것을 볼 수 있다.

 

 

 

   이번에는 redirection을 사용하지 않는 상태에서 특정 파일의 내용을 싹 지워보겠다. cp /dev/null을 파일명 앞에 넣어주면 된다. /dev/null에서 null은 한마디로 값이 없다는 뜻이다. kevin 파일에 그것을 cp == 복사한다는 것이다. 결국 내용이 텅 빈 kevin파일을 cat명령으로 출력해 보면 아무 것도 없다

리눅스 명령어(Linux Command)(2)

 

▶ touch             //보안에서의 touch명령은 '시간 조작'으로 활용

- 파일 생성 및 시간 정보 변경       

- usage : touch [ file name ]

 

   touch명령을 통해서 i2sec.txt 파일을 만들어 봤다. tmp directory을 출력해 보면 i2sec.txt파일이 생성된 것을 볼 수 있다. 참고로 몇 분 뒤에 똑같이 touch i2sec.txt를 해주면 접근 시간이 바뀌어 있을 것이다. touch명령은 말 그대로 어떠한 파일을 만지는 것이다. 옆에 보이면 시간은 가장 최근에, 최종적으로 접근한 시간을 의미한다.

 

 

 

▶ head

- 파일 내용 중 처음부터 10줄 출력

- usage : head [ file name ]

 

 

 

▶ tail

- 파일 내용 중 마지막부터 10 줄 출력

- usage : tail [ file name ]

 

   /etc/passwd에는 사용자 인증이 필요한 계정 정보들이 담겨 있다. 그대로 명령을 치면 꽤 길게 나오고 page가 넘어갈 것이다. 이럴 때 head나 tail이 잘 활용되고, 특히 실무에서는 head보다는 tail을 자주 쓴다. 가장 최근에 무슨 일이 있었는지 빠르게 볼 수 있기 때문이다. 일단 head를 /etc/passwd 앞에 붙이면 가장 top부터 10줄을 출력해준다. 당연히 root가 최고 권한의 계정이기 때문에 top에 위치해 있을 것이다. 옆에 'x'는 password를 가리키는 것인데, /etc/passwd는 아무나 열람이 가능하기 때문에 여기에 이렇게

많은 계정들의 password가 있을 수 가 없다. password는 따로 /etc/shadow에 저장되어 있다. 그 외 옆에 나열된 번호들은 뒤에서 다시 살펴볼 것이다. tail을 보면 맨 아래에 kevin이라는 계정이 있다. 가장 최근에 생성한 계정이 되겠다.

 

 

 


   tail은 이런 면에서도 활용 될 수 있다. tail -f /var/log/messages명령으로 1번 콘솔(현재 root계정 login)에서 root의 접근 log를 실시간으로 monitoring 할 수 있다. 명령을 실행 시키면 아래로 로그가 뜰 것이다.

 

 

 

   콘솔1에서 실시간 monitoring을 켜 놓고 콘솔 2에서 root로 login을 해보자. 다시 콘솔1로 돌아와 보면 접근 log가 뜰 것이다. 

 



   16:36:50에 root가 tty2에 login 했다.

 

 

 

▶ more

- 파일 내용 화면단위로 출력     //spacebar == 한 page 씩 출력, enter == 한 line 씩 출력

- usage : more [ file name ]

 

 

 

리눅스 명령어(Linux Command)(3)



▶ rdate

- 타임서버 시간 조회 및 시스템 시간 변경

- usage : rdate [ option ] [ time server ]

- 주요 타임서버(time.bora.net, ntp.ewha.net)

//rdate -p time.bora.net : 타임서버 시간 조회(정확한 시간)

//rdate -s time.bora.net : 타임서버 시간 시스템 동기화

//server를 운영할 때는 시간이 가장 중요하다

 

   타임서버 시간을 조회하려면 일단 dhclient로 인터넷을 켜줘야 한다. dhclient 는 DHCP 를 이용해서 network설정을 자동으로 하는 프로그램이다. 이 후 정상적으로 작동되는지 ping test를 해본다. ping 8.8.8.8 명령을 했는데, 여기서 8.8.8.8은 google에서 운영하는 dns server이다. 이런 식으로 잘 돌아간 후에 타임서버 시간을 조회한다.

 

 

 

▶ file

- 파일 종류 확인

- usage : file [ option ] [ file name ]

 

 

 

▶ find

- 파일 찾기

- usage : find < 경로 > -name < file name >

 

   예를 들어 root directory 아래에 passwd라는 이름으로 된 파일을 찾고 싶을 때 이런식으로 사용하면 가능하다.

 

 

 

▶ ifconfig

- network IP 설정

- usage : ifconfig [ network interface ] [ IP ] [ netmask ] [ up / down ]

//ex) network interface == eth0

 

    자신의 IP주소와 netmask를 수동으로 설정 해줄 수 있다. ifconfig [ network interface ] [ IP ] [ netmask ] [ up / down ] 이러한 양식으로 손쉽게 변경 가능하다. netmask 뒤에 붙는 up과 down의 차이는 살리고 죽인다는 명령이다.

 

 

 

▶ route

- gateway 설정

- usage : route [ add / del ] [ IP / NET ]

//static IP : 정적 할당(수동)

//DHCP IP : 자동 할당(자동)

 

   route로는 gateway를 del과 add를 통해서 죽이고 다시 살릴 수 있다. route del default gw 192.168.0.1 명령으로 default를 지워준다. 다시 똑같이 del 부분을 add로 바꿔서 실행시켜주면 다시 살아난다. echo $?를 습관적으로 실행 시켜 주면서 오류가 발생하지는 않았는지, 이전 작업이 잘 진행 되었는지를 꼭 확인한다.

 

 

 

▶ shutdown

- 시스템 종료

- usage : shutdown [ option ] [ time ] "messages"

//ex) shutdown -h now == 지금 당장 종료 시키기

 

 



▶ pipe( | )

- 앞 프로그램의 결과를 뒤 프로그램의 입력 값으로 전달해주는 역할

- 다른 명령 조합

//ex) ls -al /usr/bin | more

//ex) ls /usr/bin | less

 

 

 

▶ filter(grep)

- 표준 입력으로부터 자료를 읽어 간단한 처리 후 표준출력으로 보내는 프로그램

//ex) ps -ef | grep dhclient

 

   ps -ef명령은 process를 열람 해라는 명령이다. 간단하게 말해서 ps -ef명령은 windows에서 ctrl+alt+del를 눌리면 나오면 작업관리자와 동일하다고 생각하면 된다. 아무튼 여기에 grep dhclient를 pipe로 묶은 상태에서 enter를 치는 순간 memory에 적재시킨다. dhclient는 당연히 출력되고 grep dhclient역시 출력된다. grep dhclient역시 process 되기 때문이다.

 

 

 

▶ 표준 입출력과 리다이렉션

- 표준출력 redirection

   표준출력장치(모니터)의 방향을 파일로 전환

   사용법 : >, >>

 

- 표준오류 redirection

   사용법 : 2>, 2>>           //리눅스에서 숫자 '2'는 error을 가리킨다.

//ex) asdfjkl 2> error.txt -> 오류 미 발생시 파일 생성 안됨

 

   'asdfjkl'라는 오류를 error.txt로 redirection 시키는 것이다. cat명령으로 error.txt 파일의 내용을 출력시켜 보면 asdfjkl라는 command를 찾을 수 없다고 나오게 된다.

 

 

 



'리눅스 > Tip' 카테고리의 다른 글

GDB 사용 디버깅 작업  (0) 2010.11.25

디버깅 작업 또는 프로그램의 안전성을 검사할 때 디버거를 잘 쓰면 꽤 많은 시간을 절약할 수 있습니다. 대부분 개발자들이 GDB를 써서 디버깅을 하고 있지만, GDB가 가지고 있는 강력한 기능들을 거의 쓰지 못하고 있기 때문에, 이 글에서는 자주 쓰이지는 않을 지언정, 알면 매우 도움이 되는 기능들을 위주로 살펴보겠습니다.

먼저, 이 글을 읽는 분들이 GDB의 기본적인 사용 방법 (특히 break, run, continue, file, backtrace, print 등)을 알고 있다고 가정하겠습니다. 기본적인 사용 방법을 모르신다면Emacs/GDB/etags/cscope나 기타 GDB manual을 참고하기 바랍니다.

Breakpoints

break 명령은 대개 다음과 같이 쓸 수 있다는 것은 이미 알고 계실 것입니다:

(gdb) break                # 현재 줄에 breakpoint 설정
(gdb) break 31             # 현재 파일 31번째 줄에 breakpoint 설정
(gdb) break foo            # 함수 foo에 breakpoint 설정
(gdb) break list::next     # list 클래스 next 멤버 함수에 설정
(gdb) break hello.c:main   # hello.c 파일의 main 함수에 설정
(gdb) break util.c:300     # util.c 파일의 300번째 줄에 설정

특히 C++의 경우, 한 클래스의 모든 멤버 함수에 breakpoint를 설정하고 검사할 필요가 있는데, 이 경우, 정규 표현식(regular expression)으로 breakpoint를 설정하는 rbreak 명령을 쓰면 편리합니다. 예를 들어 보면:

(gdb) rbreak f*o           # "f*o"를 만족하는 심볼 전체에 대해 설정
(gdb) rbreak list::        # "list::.*"를 만족하는 심볼 전체에 대해 설정

특히 위 두번째 예제를 보시면 ".*"이 항상 default로 따라 온다는 것을 알 수 있습니다. 사실 rbreak 명령에 "foo"를 준 경우 사용되는 정규 표현식은, 정확히 말하면 ".*foo.*"가 됩니다. 따라서 "foo"로 시작하는 함수 전체에 대해 breakpoint를 설정하고 싶다면, 다음처럼 쓰면 됩니다:

(gdb) rbreak ^foo

breakpoint를 설정하면, 해당 breakpoint마다 번호(BNUM)가 주어지고, 이 번호를 써서 다양한 작업을 수행할 수 있습니다. 예를 들어, 전체 breakpoint 목록을 보고 싶다면:

(gdb) info b
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x08066b44 in eventShow() at menubar.cpp:1017
        breakpoint already hit 3 time
2   breakpoint     keep y   0x080b06f4 in Play() at thumbview.cpp:416
3   breakpoint     keep y   0x08066e7e in ActPlay() at menubar.cpp:1085
4   breakpoint     keep y   0x08059cd3 in Play_SS(int, int) at widgets.cpp:2183
(gdb)

첫번째 컬럼(Num)은 각 breakpoint에 대한 고유번호(BNUM)를 나타냅니다. 그리고 두번째 컬럼(Type)은 breakpoint인지 watchpoint인지 catchpoint인지를 나타냅니다. (watchpoint와 catchpoint는 다음에 설명..) 그리고 세번째 컬럼(Disp)은 이 breakpoint의 특징을 나타냅니다. (다음에 설명). 네번째 컬럼(Enb)는 현재 이 breakpoint가 활성화되어 있는지를 나타냅니다. 비활성화(n)로 표시된 breakpoint는 동작하지 않습니다. 활성화/비활성화는 'enable br [BNUM]' 또는 'disable br [BNUM]'으로 변경할 수 있습니다. 예를 들어 1번 breakpoint를 비활성화하고 싶다면:

(gdb) disable br 1

전체 breakpoint를 활성화하고 싶다면:

(gdb) enable br

2번, 4번 breakpoint를 비활성화하고 싶다면:

(gdb) disable br 2 4

2번부터 5번까지 breakpoint를 활성화 하고 싶다면:

(gdb) enable br 2-5

등으로 할 수 있습니다.

때때로, 딱 한 번만 쓸 breakpoint가 필요한 경우가 있습니다. 이 경우 쓸 수 있는 명령은 enable br once [BNUM] 또는 enable br delete [BNUM]을 쓸 수 있습니다. 예를 들어 아래 명령은 1번, 3번 breakpoint를 활성화하고, 사용된 경우 바로 비활성화시킵니다:

(gdb) enable br once 1 3

아래 명령은 4번 breakpoint를 활성화하고, 사용된 경우, 이 breakpoint를 삭제합니다:

(gdb) enable br delete 4

쓸모있는 기능 중 하나가 바로 breakpoint에 조건을 지정하고, 해당 조건을 만족할 경우에 멈추도록 하는 것입니다. 예를 들어 다음과 같은 코드가 있다고 가정해 봅시다:

int i = 0;

/* do something #1 */

for (i = 0; i < 1000; i++) {
  /* do something #2 */
  /* do something #3 */
}

이상하게도 i가 456일때 반복문 안에서 프로그램이 이상하게 동작한다고 가정해 봅시다. 이 때 "do something #2" 부분에 breakpoint를 걸었다면 (이 breakpoint의 번호는 8번이라고 가정합시다), 반복할 때마다 계속 프로그램 실행이 멈출 겁니다. 정확히 1000번 멈추겠죠. 456번까지 진행한다는 것은 매우 귀찮은 일입니다. 이 경우, 다음과 같이 조건을 지정할 수 있습니다:

(gdb) cond 8 i == 456

즉, 8번 breakpoint는 i == 456을 만족할 때에만 멈추도록 지정합니다. 조건식에는 단순한 상수 비교 이외에, 복잡한 함수 호출도 가능합니다. 예를 들면 다음과 같습니다:

(gdb) cond 8 foo(i) > bar(rand())

앞에서 예로 든 코드는 단순 반복문이기 때문에, 처음 456 - 1번에 발생하는 breakpoint는 무시하라고 지정할 수도 있습니다. 처음 N번 발생하는 breakpoint를 무시하라는 명령은 다음과 같습니다:

(gdb) ignore 8 455

즉, 8번 breakpoint는 455번 동안 무시됩니다.

또, 다음과 같은 코드를 가정해 봅시다:

int i = 0;
int j, k;
long l;

while (1) {
  j = rand();
  k = some_funtion(j, time());

  /* do something #1 */
  l = j & 0xFF00 + (int)(log(k) * 3.2108) - ...;

  if (some_condition)
    break;
}

위 코드는 j와 k가 실행할 때마다 값이 변합니다. 그리고 이상하게도 j < k 일때 변수 l이 이상한 결과를 가지는 것 같지만, 확실하지는 않습니다. 우리가 확신할 수 있는 것은 j < k일 경우, l은 항상 양수이어야 한다는 것입니다. 그래서 l의 값이 전체 반복을 끝낼 동안 어떤 값을 가지고 있는지 검사해보고 싶습니다. 이 경우 해당 breakpoint에서 멈출 때, 특정 명령을 수행하도록 하는 GDB 명령인 commands를 쓰면 됩니다.

일단 "l = j & 0xFF00..." 부분에 breakpoint를 걸고 (9번 breakpoint라고 가정), 다음 명령을 내립니다:

(gdb) commands 9
Type commands for when breakpoint 9 is hit, one per line.
End with a line saying just "end".
>silent
>if j < k
 >printf "l is %d\n", l
 >end
>cont
>end

대충 눈치가 빠른 분은 아시겠지만 'commands [BNUM] ... end'는, BNUM breakpoint에서 멈췄을 때, "..."에 지정한 GDB 명령들을 수행합니다. 일단 silent 명령으로 명령 자체가 출력되지 않도록 한 다음, GDB printf 명령으로 변수 l 값을 출력합니다. 그리고 continue 명령으로 계속 프로그램을 진행하도록 합니다. 그 결과, 프로그램을 실행할 경우, breakpoint에서 멈추고 l 값을 출력한 다음 프로그램을 자동으로 진행합니다. 이 과정은 반복문이 끝날 때까지 계속되기 때문에, 다음과 같은 비슷한 출력을 얻을 수 있습니다.

(gdb) continue
l is 3
l is -2
l is 2
l is 1
l is -3

앞에서 j < k일 때, l은 항상 양수여야 한다고 말했습니다. 위 결과를 보고 우리는 l 값이 때때로 잘못된다는 것을 쉽게 알 수 있습니다.

commands에 쓸 수 있는 GDB 명령어 형태는 다음 기회에...

가끔 next나 step으로 실행 과정을 따라 가다가 반복문을 만날 경우, 반복문 끝난 부분으로 바로 건너뛰거나, 현재 함수의 실행을 정상적으로 끝내고 상위 함수로 돌아가야할 경우가 있습니다. 예를 들어:

for (i = 0; i < 1000; i++) {
  /* do something #1 */
  /* do something #2 */
}
/* do something #3 */

현재 "/* do something #2 */" 부분까지 실행했고, 이 반복문에 이상이 없다고 판단되면, 반복문 다음까지 빠르게 진행하고 싶을 겁니다. 이 경우, until 명령이나 advance 명령을 쓰면 편리합니다.

until 명령을 쓰면, 반복문이 아닌 경우에는 next 명령과 똑같이 동작합니다.

(gdb) until

반복문일 경우, 현재 스택 프레임 (즉, 현재 함수) 안에서, 현재 줄 다음 줄에 올 때까지 프로그램을 실행합니다. 쉬운 말로, 루프를 진행하고 빠져 나오는 순간까지 실행한 다음 "(gdb)" 프롬프트를 보여줍니다.

advance 명령은 continue 명령과 마찬가지로 프로그램을 주욱 실행시키는 대신, 지정한 곳에 코드 흐름이 오면 바로 멈춥니다. 예를 들어 위 코드의 "/* do something #3 */" 부분의 줄 번호가 34였다면, until 명령 대신 다음과 같이 실행할 수도 있습니다:

(gdb) advance 34

advance 명령은 스택 프레임에 대한 제한이 없기 때문에, 현재 함수가 아닌, 아무 곳이나 설정할 수 있으며, 위치 지정은 줄 번호 뿐만 아니라, break 명령에 쓰는 모든 형식을 다 지원합니다.


네트워크로 서비스 요청 데이터를 전송받아 분석하고, 적절한 기능을 수행하고, 그 결과를 돌려주는 서버 프로그램을 생각해 봅시다. 그리고 다음과 같은 꼴로 되어 있다고 가정해 봅시다:

#define PACKET_MAX      10

int
fetch(void)
{
  int packet_received = 0;
  int received[PACKET_MAX];

  while (1) {
    if (!packet_received) {
      if (recv_data(received, PACKET_MAX) == 0)
        packet_received = 1;
    }

    /* do work here */

    process_packet(received, PACKET_MAX);
  }
  return 0;
}

이 프로그램은 평소에는 정상적으로 잘 동작하지만, 특정 패킷을 받으면 이상하게 동작한다고 가정합시다. 그리고 이 패킷은 아주 가끔 들어온다고 가정해 봅시다. 원하는 대로 패킷을 보내주는 프로그램을 따로 작성해 두지 않았다면, 이 프로그램을 디버깅하기 위해서, 문제를 일으키는 패킷이 올 때까지 하염없이 기다려야할 지도 모릅니다. 실제 코드는 다음과 같습니다:

만약 원하는 패킷이 recv_data()를 통해 들어왔다고 가정합시다. 이 때 packet_received는 1이 되고, 그에 따라 처리 작업이 이상하게 동작할 것입니다. 이 때, received의 내용을 저장하기 위해, 다음 명령을 쓸 수 있습니다:

(gdb) dump binary value buggy.dat received

위 명령을 수행하면 배열 received의 내용을 파일 buggy.dat에 저장합니다. 만약 시작 주소와 끝 주소를 알고 있다면 다음 명령을 쓸 수 있습니다:

dump binary data buggy.dat START-ADDR END-ADDR

이 때, START-ADDR는 시작 주소를, END-ADDR는 끝 주소를 나타냅니다. 즉, 앞 received 배열의 경우, 다음과 같이 쓸 수 있습니다.

(gdb) dump binary memory buggy.dat received received+10

어느 방법을 썼든지, 현재 디렉토리에는 buggy.dat이라는 파일로, 배열 received의 내용이 저장될 것입니다. 이는 메모리 내용을 그대로 dump시킨 것이므로 od(1)와 같은 툴을 써서 그 내용을 직접 볼 수 있습니다. received 배열은 int 배열이므로 다음과 같이 확인 가능합니다:

$ od -td buggy.dat 
0000000         163         151         162          85
0000020          83         190         241         252
0000040         249         121
0000050
$ _

만약, 바로 디버깅을 성공적으로 끝냈다면, 사실 위와 같은 기능은 큰 역할을 발휘하지 못합니다. 하지만, 계속해서 디버거를 실행해서 여러번 디버깅을 해야 한다면 꽤 쓸모있다는 것을 알 수 있습니다.

일단, 새로 GDB를 띄워 디버깅을 시작했다고 합시다.

    if (!packet_received) {

위 코드를 실행할 때, 강제로 packet_received를 1로 만들어, 패킷을 받는 부분을 건너뜁니다. 변수의 값 변경은 print 명령으로 쉽게 할 수 있습니다:

(gdb) p packet_received = 1

그리고 나서, received 배열을 아까 저장해 두었던 buggy.dat에서 다음과 같이
불러올 수 있습니다:

(gdb) restore buggy.dat binary received
Restoring binary file buggy.dat into memory (0xbfeda890 to 0xbfeda8b8)

이 외에도, GDB는 타 디버거에 비해 강력한 기능들을 많이 제공합니다.

'리눅스 > Tip' 카테고리의 다른 글

리눅스 기본 명령어  (0) 2011.10.08

+ Recent posts