Computer >> 컴퓨터 >  >> 프로그래밍 >> Bash 프로그래밍

Bash 스크립트에 도움말(Help) 기능 추가하는 방법

이 시리즈의 첫 번째 글에서는 아주 간단한 한 줄짜리 Bash 스크립트를 만들어 보면서, 셸 스크립트를 작성하는 이유와 컴파일 방식의 프로그램보다 셸 스크립트가 시스템 관리자에게 가장 효율적인 선택인 이유를 살펴보았습니다. 두 번째 글에서는 다른 Bash 프로그램의 출발점으로 활용할 수 있는 비교적 간단한 템플릿을 만드는 작업을 시작하고, 이를 테스트하는 방법까지 알아보았습니다.

네 편으로 구성된 이 시리즈의 세 번째 글에서는 간단한 도움말(Help) 기능을 만들고 사용하는 방법을 설명합니다. 도움말 기능을 구현하는 과정에서 함수를 사용하는 방법과 -h 같은 명령줄 옵션을 처리하는 방법도 함께 배우게 됩니다.

왜 도움말 기능이 필요한가?

아무리 간단한 Bash 프로그램이라도 어느 정도의 도움말 기능은 갖추는 것이 좋습니다. 필자가 작성하는 많은 Bash 셸 프로그램은 사용 빈도가 낮아 필요할 때마다 명령어의 정확한 문법을 잊어버리곤 합니다. 반대로 너무 복잡해서 자주 사용하더라도 옵션과 인자를 매번 다시 확인해야 하는 경우도 있습니다.

내장 도움말 기능이 있으면 코드 자체를 들여다볼 필요 없이 이런 정보를 바로 확인할 수 있습니다. 잘 만들어진 완전한 도움말 기능은 프로그램 문서화의 일부이기도 합니다.

함수란 무엇인가?

셸 함수는 셸 환경에 저장된 Bash 프로그램 문장들의 목록으로, 다른 명령어처럼 명령줄에 이름을 입력하여 실행할 수 있습니다. 사용하는 다른 프로그래밍 언어에 따라 프로시저(procedure)나 서브루틴(subroutine)이라고 부르기도 합니다.

함수는 스크립트 내부나 명령줄 인터페이스(CLI)에서 이름을 호출하는 방식으로 사용됩니다. 함수가 호출되면 함수 내부의 명령문들이 실행되고, 그 후 프로그램 흐름은 호출한 쪽으로 돌아가 다음 프로그램 문장들이 실행됩니다.

함수의 기본 문법은 다음과 같습니다:

FunctionName(){program statements}

CLI에서 간단한 함수를 만들어 직접 확인해 보겠습니다. (함수는 생성된 셸 인스턴스의 환경에 저장됩니다.) "hello world"를 줄인 hw라는 함수를 만들어 보겠습니다. CLI에 다음 코드를 입력하고 Enter 키를 누른 후, 다른 셸 명령어처럼 hw를 입력해 보세요:

[student@testvm1 ~]$ hw(){ echo "Hi there kiddo"; }
[student@testvm1 ~]$ hw
Hi there kiddo
[student@testvm1 ~]$

표준적인 "Hello world" 시작 예제에는 조금 질렸으니, 이제 현재 정의되어 있는 모든 함수 목록을 확인해 보겠습니다. 함수가 많기 때문에 새로 만든 hw 함수만 보여드립니다. 명령줄이나 프로그램 내에서 호출되면 함수는 프로그래밍된 작업을 수행한 뒤 종료되며, 제어권을 호출한 엔티티(명령줄 또는 스크립트에서 호출문 다음의 Bash 프로그램 문장)로 반환합니다:

[student@testvm1 ~]$ declare -f | less
<snip>
hw ()
{
echo "Hi there kiddo"
}
<snip>

더 이상 필요하지 않으므로 이 함수를 제거하겠습니다. unset 명령어로 삭제할 수 있습니다:

[student@testvm1 ~]$ unset -f hw ; hw
bash: hw: command not found
[student@testvm1 ~]$

도움말 함수 만들기

편집기에서 hello 프로그램을 열고 저작권 문구 뒤, echo "Hello world!" 문장 앞에 아래의 도움말 함수를 추가합니다. 이 도움말 함수는 프로그램에 대한 간단한 설명, 문법 다이어그램, 사용 가능한 옵션에 대한 짧은 설명을 표시합니다. 함수를 테스트하기 위한 호출문과, 함수 부분과 프로그램 본문을 시각적으로 구분해 주는 주석 줄도 함께 추가합니다:

################################################################################
# Help                                                                         #
################################################################################
Help()
{
   # Display Help
   echo "Add description of the script functions here."
   echo
   echo "Syntax: scriptTemplate [-g|h|v|V]"
   echo "options:"
   echo "g     Print the GPL license notification."
   echo "h     Print this Help."
   echo "v     Verbose mode."
   echo "V     Print software version and exit."
   echo
}

################################################################################
################################################################################
# Main program                                                                 #
################################################################################
################################################################################

Help
echo "Hello world!"

이 도움말 함수에 설명된 옵션들은 필자가 작성하는 프로그램에서 일반적으로 사용하는 것들이지만, 아직 코드에는 구현되어 있지 않습니다. 프로그램을 실행하여 테스트해 보겠습니다:

[student@testvm1 ~]$ ./hello
Add description of the script functions here.

Syntax: scriptTemplate [-g|h|v|V]
options:
g     Print the GPL license notification.
h     Print this Help.
v     Verbose mode.
V     Print software version and exit.

Hello world!
[student@testvm1 ~]$

필요할 때만 도움말을 표시하는 로직을 아직 추가하지 않았기 때문에 프로그램은 항상 도움말을 표시합니다. 함수가 올바르게 작동하고 있으니, 이제 명령줄에서 프로그램을 실행할 때 -h 옵션을 사용한 경우에만 도움말을 표시하는 로직을 추가해 보겠습니다.

옵션 처리하기

-h 같은 명령줄 옵션을 처리할 수 있는 능력은 프로그램의 동작을 지정하고 수정할 수 있는 강력한 기능을 제공합니다. -h 옵션의 경우, 프로그램이 도움말 텍스트를 터미널 세션에 출력한 후 나머지 프로그램을 실행하지 않고 종료하기를 원합니다. 명령줄에서 입력된 옵션을 처리하는 기능은 while 명령(while에 대해 자세히 알아보려면 How to program with Bash: Loops 참조)을 getoptscase 명령과 함께 사용하여 Bash 스크립트에 추가할 수 있습니다.

getopts 명령은 명령줄에서 지정된 모든 옵션을 읽어 옵션 목록을 생성합니다. 아래 코드에서 while 명령은 각 옵션에 대해 $options 변수를 설정하며 옵션 목록을 순회합니다. case 문은 각 옵션을 차례로 평가하여 해당하는 블록의 문장들을 실행합니다. while 문은 모든 옵션이 처리되거나 프로그램을 종료하는 exit 문을 만날 때까지 옵션 목록 평가를 계속합니다.

echo "Hello world!" 문장 바로 앞의 도움말 함수 호출을 삭제했는지 확인하세요. 이제 프로그램 본문은 다음과 같습니다:

################################################################################
################################################################################
# Main program                                                                 #
################################################################################
################################################################################
################################################################################
# Process the input options. Add options as needed.                            #
################################################################################
# Get the options
while getopts ":h" option; do
   case $option in
      h) # display Help
         Help
         exit;;
   esac
done

echo "Hello world!"

-h 옵션에 대한 case 블록 끝의 exit 문 뒤에 있는 이중 세미콜론(;;)에 주목하세요. case 문에 추가되는 각 옵션의 끝을 구분하기 위해 각 옵션마다 반드시 필요합니다.

테스트

이제 테스트가 조금 더 복잡해졌습니다. 여러 가지 서로 다른 옵션과 옵션 없이 프로그램을 테스트하여 어떻게 반응하는지 확인해야 합니다. 먼저 옵션 없이 테스트하여 "Hello world!"가 정상적으로 출력되는지 확인합니다:

[student@testvm1 ~]$ ./hello
Hello world!

정상적으로 작동합니다. 이제 도움말 텍스트를 표시하는 로직을 테스트해 보겠습니다:

[student@testvm1 ~]$ ./hello -h
Add description of the script functions here.

Syntax: scriptTemplate [-g|h|t|v|V]
options:
g     Print the GPL license notification.
h     Print this Help.
v     Verbose mode.
V     Print software version and exit.

예상대로 작동합니다. 이제 예상치 못한 옵션을 입력했을 때 어떻게 되는지 테스트해 보겠습니다:

[student@testvm1 ~]$ ./hello -x
Hello world!
[student@testvm1 ~]$ ./hello -q
Hello world!
[student@testvm1 ~]$ ./hello -lkjsahdf
Add description of the script functions here.

Syntax: scriptTemplate [-g|h|t|v|V]
options:
g     Print the GPL license notification.
h     Print this Help.
v     Verbose mode.
V     Print software version and exit.

[student@testvm1 ~]$

프로그램은 특별한 응답이 정의되지 않은 옵션은 오류 없이 그냥 무시합니다. 하지만 마지막 입력(-lkjsahdf)에 주목하세요. 옵션 목록에 h가 포함되어 있어 프로그램이 이를 인식하고 도움말 텍스트를 출력했습니다. 이 테스트를 통해 프로그램에 잘못된 입력을 감지하고 종료하는 기능이 없다는 사실이 확인되었습니다.

case 문에 명시적인 일치 항목이 없는 모든 옵션과 일치하는 또 다른 case 블록을 추가할 수 있습니다. 이 일반 케이스는 특정 일치 항목을 제공하지 않은 모든 입력과 일치합니다. \?를 마지막 케이스로 하는 전체 일치(catch-all)가 추가된 case 문은 이제 다음과 같습니다. 추가되는 모든 개별 케이스는 반드시 이 마지막 케이스 앞에 위치해야 합니다:

while getopts ":h" option; do
   case $option in
      h) # display Help
         Help
         exit;;
     \?) # incorrect option
         echo "Error: Invalid option"
         exit;;
   esac
done

동일한 옵션으로 프로그램을 다시 테스트하여 어떻게 작동하는지 확인해 보세요.

지금까지의 진행 상황

이번 글에서 명령줄 옵션을 처리하는 기능과 도움말 절차를 추가하며 상당한 진전을 이루었습니다. 이제 Bash 스크립트 전체는 다음과 같습니다:

#!/usr/bin/bash
################################################################################
#                              scriptTemplate                                  #
#                                                                              #
# Use this template as the beginning of a new program. Place a short           #
# description of the script here.                                              #
#                                                                              #
# Change History                                                               #
# 11/11/2019  David Both    Original code. This is a template for creating     #
#                           new Bash shell scripts.                            #
#                           Add new history entries as needed.                 #
#                                                                              #
#                                                                              #
################################################################################
################################################################################
################################################################################
#                                                                              #
#  Copyright (C) 2007, 2019 David Both                                         #
#  LinuxGeek46@both.org                                                        #
#                                                                              #
#  This program is free software; you can redistribute it and/or modify        #
#  it under the terms of the GNU General Public License as published by        #
#  the Free Software Foundation; either version 2 of the License, or           #
#  (at your option) any later version.                                         #
#                                                                              #
#  This program is distributed in the hope that it will be useful,             #
#  but WITHOUT ANY WARRANTY; without even the implied warranty of              #
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
#  GNU General Public License for more details.                                #
#                                                                              #
#  You should have received a copy of the GNU General Public License           #
#  along with this program; if not, write to the Free Software                 #
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   #
#                                                                              #
################################################################################
################################################################################
################################################################################

################################################################################
# Help                                                                         #
################################################################################
Help()
{
   # Display Help
   echo "Add description of the script functions here."
   echo
   echo "Syntax: scriptTemplate [-g|h|t|v|V]"
   echo "options:"
   echo "g     Print the GPL license notification."
   echo "h     Print this Help."
   echo "v     Verbose mode."
   echo "V     Print software version and exit."
   echo
}

################################################################################
################################################################################
# Main program                                                                 #
################################################################################
################################################################################
################################################################################
# Process the input options. Add options as needed.                            #
################################################################################
# Get the options
while getopts ":h" option; do
   case $option in
      h) # display Help
         Help
         exit;;
     \?) # incorrect option
         echo "Error: Invalid option"
         exit;;
   esac
done

echo "Hello world!"

이 버전의 프로그램을 반드시 철저하게 테스트하세요. 무작위 입력을 사용하여 어떤 일이 발생하는지 확인해 보고, 대시(-) 없이 유효한 옵션과 유효하지 않은 옵션을 테스트해 보는 것도 좋습니다.

다음 편 예고

이번 글에서는 도움말 함수와, 선택적으로 도움말을 표시할 수 있는 명령줄 옵션 처리 기능을 추가했습니다. 프로그램이 조금씩 복잡해지고 있어서, 완전한 테스트를 위해서는 더 많은 테스트 경로가 필요해지고 있습니다.

다음 글에서는 변수 초기화와, 프로그램이 올바른 조건에서만 실행되도록 보장하는 몇 가지 무결성 검사(sanity check)를 살펴볼 예정입니다.

참고 자료

  • How to program with Bash: Syntax and tools
  • How to program with Bash: Logical operators and shell expansions
  • How to program with Bash: Loops

이 글 시리즈는 David Both의 3부작 Linux 자습 과정 'Using and Administering Linux—Zero to SysAdmin' 2권 10장을 일부 기반으로 작성되었습니다.