5. 명령행 옵션과 인자 :: YJcode

프로그램의 상당수는 명령행 옵션과 인자에 따라 다르게 동작한다.

전통적인 유닉스 명령행 옵션은 하이픈( - ) 으로 시작해서, 옵션을 나타내는 한 글자와, 경우에 따라 추가 인자가 뒤따르기도 한다. 여기에 GNU 유틸리티는 하이픈 2개( -- ) 로 시작해서, 옵션을 타나내는 문자열과, 경우에 따라 추가 인자가 뒤따르는 확장 옵션 문법을 제공한다. 이 옵션을 파싱할때는 getopt() 라이브러리 함수를 쓰면 편리하다.

명령행 문법이 복잡한 프로그램에는 사용자를 위한 간단한 도움말 기능이 있다.

--help 옵션으로 실행하면, 프로그램이 명령행 옵션과 인자의 문법에 대한 사용법을 보여준다.

 

대부분의 프로그램에서, 공통적으로 필요한 정의가 담겨있는 헤더 파일과 공통 함수를 사용한다.

 

  • 공통 헤더파일

아래 코드표의 헤더파일들은 거의 모든 프로그램이 매우 높은 빈도로 사용하는 헤더파일이다. 이 헤더 파일에는 여러 프로그램에서 쓰는 다른 많은 헤더 파일이 포함되어 있고, boolean 데이터형과 두 숫자중 최소값과 최대값을 구하는 매크로가 정의되어 있다.

							lib/tlpi_hdr.h

#ifdef TLPI_HDR_H
#define TLPI_HDR_H			//실수로 두번 포함하는 것을 방지한다.

#include <sys/types.h>			//여러 프로그램에 쓰이는 형 정의
#include <stdio.h>			//표준 I/O함수
#include <stdlib.h>			//표준 라이브러리 함수

#include <unistd.h>			//여러 시스템 호출 프로토타입
#include <errno.h>			//errno와 에러상수 정의
#include <string.h>			//흔히 쓰이는 문자열 처리 함수

#include "get_num.h"			//수 인자 처리함수 (getInt(), getLong()) 선언

#include "drror_functions.h"		//에러처리함수 선언

typedef enum { FALSE, TRUE } Boolean;

#define min(m,n) ((m) < (n) ? (m) : (n))
#define max(m,n) ((m) > (n) ? (m) : (n))

#endif

 

  • 에러 진단 함수

앞으로 예제 프로그램에서 에러 처리를 쉽게 하기 위해, 아래에 선언되어 있는 에러 진단 함수를 사용한다.

 

							lib/error_functions.h
#ifdef ERROR_FUNCTIONS_H
#define ERROR_FUNCTIONS_H

void errMsg(const char *format, ...);

#ifdef __GNUC__

#define NORETURN __attribute__ ((__noreturn__))
#else
#define NORETURN
#endif

void errExit(const char *format, ...) NORETURN ;

void err_exit(const char *format, ...) NORETURN ;

void errExitEN(int errnum, const char *format, ...) NORETURN ;

void fatal(const char *format, ...) NORETURN ;

void usageErr(const char *format, ...) NORETURN ;

void cmdLineErr(const char *format, ...) NORETURN ;

#endif

 

시스템 호출과 라이브러리 함수의 에러를 진단할 때 errMsg(), errExit(), err_exit(), errExitEN()을 쓴다.

 

#include "tlpi_hdr.h"

void errMsg(const char *format, ...);
void errExit(const char *format, ...);
void err_exit(const char *format, ...);
void errExitEN(int errnum, const char *format, ...);

 

errMsg() 함수는 표준 에러로 메시지를 출력한다. 인자 목록은 printf()와 동일하지만, 출력 문자열 끝에 줄마꿈 문자가 자동으로 추가되는 것이 다르다. errMsg() 함수는 errno의 값에 따라, EPERM 같은 에러이름, strerror()가 리턴하는 에러 설명으로 이뤄진 텍스트, 그리고 인자 목록에 따라 포맷된 내용을 출력한다.

errExit() 함수는 errMsg()와 비슷하지만, 에러 메시지를 출력한 뒤 exit()를 호출해 프로그램을 종료시키거나, 환경 변수 EF_DUMPCORE가 비어 있지 않은 문자열로 정의되어 있으면 abort()를 호출해 디버그용 코어 덤프 파일을 만든다.

err_exit() 함수는 errExit()와 비슷하지만 다음과 같은 차이점이 있다.

  • 에러 메싲지를 출력하기 전에 표준 출력 버퍼 내에 남아있던 내용을 모두 출력하지 않는다.
  • exit()대신 _exit()를 호출해 프로세스를 종료시킨다. 이로 인해 stdio 버퍼내에 남아있던 내용을 출력하거나 종료 핸들러를 부르지 않고 프로세스를 종료한다.

err_exit()의 이런 차이점에 대해서는 차후 자세히 살펴몰 것이다. 우선은 err_exit()는 에러 발생시 종료해야 하는 자기 프로세스를 만드는 라이브러리 함수를 작성할 때 특히 유용하다고만 알고 있으면 된다. 이렇게 종료할 때는 부포 프로세스의 stdio 버퍼를 복사해서 만든 자식 프로세스의 stdio 버퍼에 남아 있던 내용을 모두 출력하거나 부모 프로세스가 설정한 종료 핸들러를 부르지 않고 종료해야 한다.

errExitEN() 함수는 errExit()와 비슷하지만, errno 값에 따라 에러 텍스트를 출력하는 대신, 인자 errnum으로 넘겨준 에러 번호에 따른 텍스트를 출력한다.

errExitEN()은 주로 POSIX 스레드 API를 채택한 프로그램에서 사용한다. 에러 발생시 -1을 리턴하는 전통적인 유닉스 시스템 호출과 달리, POSIX 스레드 함수는 에러번호를 리턴한다.

POSIX 스레드 함수의 에러는 다음과 같은 코드로 확인할 수 있다.

errno = pthread_create(&thread, NULL, func, &arg);
if (errno != 0) {
	errExit("pthread_create");
}

하지만 이 방법은 비효율적이다. 스레드를 쓰는 프로그램에서 errno는 바뀔 수 있는 lvalue를 리턴하는 함수 호출로 확장되는 매크로로 정의되어 있기 때문이다. 따라서 errno를 쓸 때마다 함수를 호출하게 된다. errExitEN()함수를 쓰면 다음과 같이 좀 더 효율적인 코드를 작성할 수 있다.

int s;

s = pthread_create(&thread, NULL, func, &arg);
if (s != 0) {
	errExitEN(s, "pthread_create");
}

 

또 다른 종류의 에러를 조사할 때는 fatal(), usageErr(), cmdLineErr() 를 쓸 수 있다.

 

#include "tlpi_hdr.h"

void fatal(const char *format, ...);
void usageErr(const char *format, ...);
void cmdLineErr(const char *format, ...);

 

  • fatal() 함수는 errno를 설정하지 않는 라이브러리 함수를 포함하는 일반적인 에러를 조사할 때 쓴다. 인자 목록은 printf()와 같지만, 출력 문자열 끝에 줄바꿈 문자가 자동으로 추가되는 것이 다르다. 포맷된 메시지를 표준 에러로 출력하고는 errExit()를 호출해 프로그램을 종료시킨다.
  • usageErr() 함수는 명령행 인자의 용법이 잘못됐을때 쓴다. printf() 스타일의 인자 목록을 받아서 포맷된 메시지 앞에 Usage:를 붙여서 표준 에러로 출력한 뒤 exit()를 호출해 프로그램을 종료시킨다.
  • cmdLineErr()함수는 usageErr()와 비슷하지만 프로그램에 넘긴 명령행 인자의 에러를 알려줄 때 사용한다.

+ Recent posts