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

Bash 스크립트 실력을 한 단계 끌어올리는 5가지 필수 기법

시스템 관리자라면 다양한 작업을 자동화하기 위해 Bash 스크립트를 자주 작성하게 됩니다. 짧은 스크립트도 있고, 상당히 길고 복잡한 스크립트도 있죠.

소프트웨어 벤더가 제공하는 설치 스크립트를 들여다본 적이 있으신가요? 이런 스크립트에는 고객의 시스템에 손상을 주지 않고 설치가 제대로 완료되도록 수많은 함수와 검증 로직이 포함되어 있습니다. 저는 오랜 기간 Bash 스크립트의 품질과 안정성을 높이기 위한 다양한 기법들을 축적해 왔는데, 그중 유용한 몇 가지를 소개하려고 합니다. 여러분의 스크립트 작성에 도움이 되기를 바랍니다.

시작은 간단한 명령 나열부터

처음 Bash를 배울 무렵, 제 스크립트란 단순히 명령어를 순서대로 나열한 것에 불과했습니다. 웹 콘텐츠 배포처럼 반복적인 셸 작업의 시간을 아끼는 용도였죠. 예를 들어 정적 콘텐츠 압축 파일을 Apache 웹 서버 홈 디렉터리에 풀어내는 작업을 담당한 스크립트는 대략 다음과 같았습니다.

cp january_schedule.tar.gz /usr/apache/home/calendar/
cd /usr/apache/home/calendar/
tar zvxf january_schedule.tar.gz

물론 타이핑 시간을 줄여주긴 했지만, 장기적으로 봤을 때 큰 가치가 있는 스크립트는 아니었습니다. 시간이 지나면서 저는 소프트웨어 패키지 생성, 프로그램 설치, 파일 서버 백업 같은 더 복잡한 작업까지 처리할 수 있는 방법을 익혔습니다.

1. 조건문 활용하기

다른 프로그래밍 언어와 마찬가지로 조건문은 Bash에서도 가장 강력하고 널리 쓰이는 기능입니다. 조건문 덕분에 컴퓨터 프로그램이 논리적인 판단을 수행할 수 있으며, 이후에 소개할 예제들 대부분이 조건 로직을 기반으로 합니다.

조건문의 기본은 if 문입니다. 특정 조건을 검사한 뒤, 그 결과에 따라 스크립트의 동작 방식을 제어할 수 있습니다. 예를 들어 Java bin 디렉터리의 존재 여부를 확인해 Java가 설치되어 있는지 판단하고, 발견되면 실행 경로(PATH)를 업데이트해 Java 애플리케이션이 호출할 수 있게 만드는 식입니다.

if [ -d "$JAVA_HOME/bin" ] ; then
PATH="$JAVA_HOME/bin:$PATH"
fi

2. 실행 권한 제한하기

스크립트를 특정 사용자만 실행할 수 있도록 제한하고 싶을 때가 있습니다. Linux에는 사용자·그룹 권한 체계와 SELinux 같은 보호 메커니즘이 이미 마련되어 있지만, 스크립트 내부에 직접 로직을 넣는 선택지도 있습니다. 예컨대 특정 웹 애플리케이션의 시작 스크립트를 해당 애플리케이션의 소유자만 실행하도록 하거나, root 사용자만 실행 가능하도록 코드로 강제할 수 있습니다. 이때 활용할 수 있는 환경 변수가 두 가지 있습니다. 하나는 사용자 이름을 담고 있는 $USER, 다른 하나는 사용자 ID(UID)를 담고 있는 $UID입니다. 스크립트에서는 실행 중인 사용자의 UID가 반환됩니다.

특정 사용자로 제한

다음 예제는 여러 애플리케이션 서버 인스턴스가 공존하는 멀티 호스팅 환경에서, jboss1 사용자만 스크립트를 실행하도록 제한하는 방법입니다. if 문은 사실상 "실행 중인 사용자가 jboss1이 아니면?"이라고 묻는 셈입니다. 조건이 참이면 첫 번째 echo 문이 출력된 뒤 exit 1이 호출되어 스크립트가 종료됩니다.

if [ "$USER" != 'jboss1' ]; then
echo "Sorry, this script must be run as JBOSS1!"
exit 1
fi
echo "continue script"

root 사용자로 제한

다음 예제는 오직 root 사용자만 스크립트를 실행할 수 있도록 보장합니다. root의 UID는 0이므로, 조건문에서 -gt 옵션을 사용해 0보다 큰 모든 UID의 접근을 차단할 수 있습니다.

if [ "$UID" -gt 0 ]; then
echo "Sorry, this script must be run as ROOT!"
exit 1
fi
echo "continue script"

3. 인수(arguments) 받아들이기

실행 가능한 모든 프로그램과 마찬가지로 Bash 스크립트도 인수를 입력값으로 받을 수 있습니다. 예제를 살펴보기 전에 한 가지 원칙을 짚고 넘어가겠습니다. 좋은 프로그래밍이란 우리가 원하는 동작만 하는 애플리케이션을 만드는 것을 넘어, 우리가 원하지 않는 동작을 하지 못하도록 만드는 것이기도 합니다. 저는 인수가 전달되지 않은 상태에서 스크립트가 위험한 작업을 수행하지 않도록 항상 최우선으로 검증합니다. 아래 조건은 인수 개수($#)가 0인지 확인하고, 참이면 스크립트를 종료합니다.

if [ $# -eq 0 ]; then
echo "No arguments provided"
exit 1
fi
echo "arguments found: $#"

여러 개의 인수 처리

스크립트에는 두 개 이상의 인수를 전달할 수도 있습니다. 각 인수는 $1, $2, $3처럼 번호가 증가하는 내부 변수로 참조됩니다. 위 예제에 다음 한 줄을 추가하면 처음 세 개의 인수를 화면에 출력할 수 있습니다. 물론 실무에서는 전체 인수 개수에 맞춘 적절한 처리 로직이 추가로 필요하지만, 여기서는 개념 설명을 위해 단순화했습니다.

echo $1 $2 $3

인수 변수를 살펴보다가 "0번은 왜 빼먹었지?"라고 궁금해하셨을 수도 있습니다.

네, 의도적으로 건너뛰었습니다. 그런데 여기에는 훌륭한 이유가 있습니다. $0 변수는 분명히 존재하며 매우 유용합니다. 이 변수에는 실행 중인 스크립트의 이름이 담깁니다.

echo $0

실행 시점에 스크립트 이름을 참조해야 하는 중요한 이유 중 하나는, 로그 파일 이름에 스크립트 이름을 포함시키기 위해서입니다. 가장 단순한 형태는 다음과 같이 echo 문으로 남기는 것입니다.

echo test >> $0.log

실제 운영 환경에서는 로그가 저장될 위치와 파일명, 기록할 정보를 용도에 맞게 세밀하게 구성하는 추가 코드를 넣는 것이 좋습니다.

4. 사용자 입력 받기

스크립트가 실행 중에 입력을 받을 수 있다는 점도 매우 유용한 기능입니다. 가장 간단한 형태는 사용자에게 값을 입력받는 것입니다.

echo "enter a word please:"
read word
echo $word

선택지를 제공하는 방식으로 확장할 수도 있습니다. 아래 예제는 사용자에게 설치 여부를 묻고, 'n'을 입력하면 설치를 중단합니다.

read -p "Install Software ?? [Y/n]: " answ
if [ "$answ" == 'n' ]; then
exit 1
fi
echo "Installation starting..."

5. 실패 시 즉시 중단하기

몇 년 전, 저는 최신 버전의 JDK(Java Development Kit)를 컴퓨터에 설치하는 스크립트를 작성한 적이 있습니다. 이 스크립트는 JDK 아카이브를 특정 디렉터리에 추출하고, 심볼릭 링크를 업데이트한 뒤, alternatives 유틸리티를 통해 새 버전을 시스템에 등록합니다. 만약 아카이브 추출이 실패했는데도 스크립트가 계속 진행된다면, 시스템 전반의 Java 환경이 손상될 수 있습니다. 따라서 저는 이런 상황에서 스크립트가 즉시 중단되기를 원했습니다. 추출이 성공적으로 완료되기 전까지는 후속 시스템 변경 작업이 일어나지 않아야 하기 때문입니다. 해당 스크립트의 일부는 다음과 같습니다.

tar kxzmf jdk-8u221-linux-x64.tar.gz -C /jdk --checkpoint=.500; ec=$?
if [ $ec -ne 0 ]; then
echo "Installation failed - exiting."
exit 1
fi

$? 변수의 동작은 다음 한 줄짜리 명령으로도 손쉽게 확인할 수 있습니다.

ls T; ec=$?; echo $ec

먼저 touch T로 파일을 생성한 뒤 이 명령을 실행하면 ec 값은 0입니다. 이번엔 rm T로 파일을 삭제하고 명령을 다시 실행해 보세요. 이번에는 T를 찾지 못해 ls가 오류를 보고하기 때문에 ec 값이 2로 반환됩니다.

이렇게 명령의 성공·실패 여부를 알려주는 종료 코드를 활용하면, 위 예제처럼 스크립트의 흐름을 정교하게 제어하는 로직을 구성할 수 있습니다.

마치며

더 높은 수준의 기능을 구현하려면 Python, C, Java 같은 언어를 써야 한다고 생각하기 쉽지만, 반드시 그럴 필요는 없습니다. Bash 스크립팅 언어는 그 자체로 매우 강력하며, 잠재력을 최대한 끌어올리기 위해 배울 것이 많습니다. 이번에 소개한 몇 가지 예제가 Bash로 할 수 있는 일의 폭을 가늠하는 데 도움이 되기를 바랍니다.