← Back to Blog

[Linux] Shell Scripting Basics

computer science > programming

2026-07-124 min read

#computer-science #programming #linux #shell #bash #shell-script #automation

공식 문서: GNU Bash Reference Manual, ShellCheck

Linux shell이란?

Shell은 사용자가 입력한 command를 해석하고 operating system에 실행을 요청하는 program이다. Linux에서 terminal을 열고 다음 command를 입력할 때 실제로 command를 해석하는 것이 shell이다.

pwd
ls -al
mkdir project
cd project

자주 사용하는 shell은 다음과 같다.

Shell특징
sh전통적인 Unix shell interface
bashLinux에서 널리 사용하는 shell
zshmacOS 기본 shell, plugin과 customization이 다양함
fish편리한 자동 완성과 interactive 기능 제공

현재 사용 중인 login shell은 다음 command로 확인할 수 있다.

echo "$SHELL"

Shell script는 terminal에서 실행하던 여러 command를 파일에 저장해 반복 작업을 자동화하는 program이다.


첫 shell script 작성

hello.sh 파일을 만든다.

#!/usr/bin/env bash

echo "Hello, Linux!"
pwd
date

첫 줄의 #!shebang이라고 한다. 이 파일을 어떤 interpreter로 실행할지 지정한다.

#!/usr/bin/env bash

Bash 기능을 사용하는 script라면 interpreter를 Bash로 명확하게 지정한다.

실행 권한을 추가한다.

chmod +x hello.sh

Script를 실행한다.

./hello.sh

실행 권한 없이 Bash에 파일을 전달해 실행할 수도 있다.

bash hello.sh

두 방식의 차이는 다음과 같다.

실행 방법Interpreter 선택
./hello.shShebang에 지정된 interpreter 사용
bash hello.sh현재 command에서 Bash를 직접 지정

Comment 작성

# 뒤의 내용은 comment로 처리된다.

#!/usr/bin/env bash

# 현재 시간을 출력한다.
date

Shebang도 #으로 시작하지만 첫 줄의 #!는 특별한 의미를 가진다.


변수

Shell 변수는 이름=값 형식으로 만든다.

name="br4c3"
project_dir="$HOME/projects/blog"

변수를 대입할 때 = 앞뒤에 공백을 넣지 않는다.

# 올바른 작성
name="br4c3"

# 잘못된 작성
name = "br4c3"

변수의 값은 $name 또는 ${name}으로 사용한다.

echo "$name"
echo "User: ${name}"
echo "Project: ${project_dir}"

문자열 안에서는 변수 범위를 명확하게 하기 위해 ${name} 형태가 편리하다.

filename="report"
echo "${filename}_2026.txt"

따옴표

Shell에서 따옴표는 변수 확장과 공백 처리에 영향을 준다.

name="Linux"

echo "$name"
echo '$name'

출력은 다음과 같다.

Linux
$name
문법동작
"$name"변수 값을 확장함
'$name'문자 그대로 사용함
$nameword splitting과 globbing이 발생할 수 있음

File path나 사용자 입력을 담은 변수는 특별한 이유가 없다면 "$variable"처럼 double quote로 감싼다.

file="my report.txt"
cat "$file"

Quote를 생략하면 myreport.txt를 서로 다른 argument로 처리할 수 있다.


Command 결과를 변수에 저장

$(command)를 사용하면 command의 출력을 변수에 저장할 수 있다.

current_date=$(date +%Y-%m-%d)
current_user=$(whoami)

echo "Date: $current_date"
echo "User: $current_user"

예전 backtick 문법보다 $(...)를 사용하는 것이 읽기 쉽고 중첩하기도 편하다.

kernel_version=$(uname -r)

사용자 입력

read command로 값을 입력받는다.

#!/usr/bin/env bash

read -r -p "Enter your name: " name
echo "Hello, $name"

-r option은 backslash를 특별하게 처리하지 않고 입력 그대로 읽도록 한다.

Password처럼 입력 내용을 화면에 표시하지 않으려면 -s를 사용한다.

read -r -s -p "Password: " password
echo

Script argument

Script를 실행할 때 전달한 값은 positional parameter로 사용할 수 있다.

./greet.sh Alice developer
#!/usr/bin/env bash

echo "Script: $0"
echo "First argument: $1"
echo "Second argument: $2"
echo "Argument count: $#"
echo "All arguments: $@"

주요 parameter는 다음과 같다.

Parameter의미
$0Script 이름
$1, $2첫 번째, 두 번째 argument
$#Argument 개수
"$@"모든 argument를 각각 유지
$?직전에 실행한 command의 exit status

모든 argument를 순서대로 처리할 때는 "$@"를 사용한다.

for arg in "$@"; do
    echo "$arg"
done

조건문

Bash에서는 if, elif, else, fi로 조건문을 작성한다.

#!/usr/bin/env bash

file_path="$1"

if [[ -f "$file_path" ]]; then
    echo "File exists: $file_path"
elif [[ -d "$file_path" ]]; then
    echo "Directory exists: $file_path"
else
    echo "Path does not exist: $file_path"
fi

자주 사용하는 file 조건은 다음과 같다.

조건의미
-e "$path"Path가 존재함
-f "$path"일반 file임
-d "$path"Directory임
-r "$path"읽을 수 있음
-w "$path"쓸 수 있음
-x "$path"실행할 수 있음

문자열을 비교한다.

if [[ "$name" == "admin" ]]; then
    echo "Administrator"
fi

숫자를 비교한다.

count=10

if (( count >= 5 )); then
    echo "count is at least 5"
fi

반복문

for

목록을 순서대로 처리한다.

for name in Alice Bob Charlie; do
    echo "Hello, $name"
done

Directory의 .log file을 처리한다.

for file in ./*.log; do
    [[ -e "$file" ]] || continue
    echo "Log file: $file"
done

while

조건이 참인 동안 반복한다.

count=1

while (( count <= 3 )); do
    echo "$count"
    ((count++))
done

File을 한 줄씩 읽는다.

while IFS= read -r line; do
    echo "$line"
done < input.txt

함수

반복해서 사용하는 command를 함수로 묶을 수 있다.

log() {
    local message="$1"
    echo "[$(date +%H:%M:%S)] $message"
}

log "Build started"
log "Build finished"

함수 안의 local 변수는 함수 내부에서만 사용된다.

greet() {
    local name="$1"
    echo "Hello, $name"
}

greet "Linux"

Exit status

Linux command는 종료할 때 숫자 status를 반환한다.

0: 성공
0이 아닌 값: 실패 또는 다른 상태

직전 command의 status는 $?로 확인한다.

ls /tmp
echo "$?"

if는 command의 출력 문자열이 아니라 exit status를 기준으로 판단한다.

if ping -c 1 example.com > /dev/null 2>&1; then
    echo "Network is reachable"
else
    echo "Network is unavailable"
fi

Script를 특정 status로 종료한다.

exit 0

Error 상황에서는 1 같은 non-zero 값을 반환할 수 있다.

if [[ $# -eq 0 ]]; then
    echo "Usage: $0 <file>" >&2
    exit 1
fi

Redirection과 pipe

출력 redirection

Command 출력을 file에 저장한다.

ls -al > files.txt

>는 file을 새로 쓰고, >>는 뒤에 추가한다.

echo "start" > app.log
echo "finish" >> app.log

Error 출력은 file descriptor 2를 사용한다.

command 2> error.log

Standard output과 error를 모두 저장한다.

command > output.log 2>&1

Pipe

앞 command의 출력을 뒤 command의 입력으로 전달한다.

ps aux | rg python
find . -name "*.log" | sort
du -sh ./* | sort -h

Pipe는 여러 작은 command를 연결해 하나의 처리 흐름을 만드는 Linux shell의 핵심 기능이다.


안전한 기본 option

Bash script 시작 부분에서 다음 option을 사용할 수 있다.

set -Eeuo pipefail
Option의미
-eCommand가 실패하면 script 종료
-u정의하지 않은 변수를 사용하면 오류
-o pipefailPipe 중간 command의 실패도 반영
-EERR trap을 함수 등에도 상속

작은 자동화 script에서는 실수를 빨리 발견하는 데 도움이 된다. 다만 실패를 직접 처리해야 하는 command에는 조건문을 사용한다.

if ! mkdir -p "$target_dir"; then
    echo "Failed to create directory: $target_dir" >&2
    exit 1
fi

간단한 backup script

다음은 지정한 directory를 날짜가 포함된 archive로 만드는 예제다.

#!/usr/bin/env bash

set -Eeuo pipefail

if [[ $# -ne 2 ]]; then
    echo "Usage: $0 <source-directory> <backup-directory>" >&2
    exit 1
fi

source_dir="$1"
backup_dir="$2"
timestamp=$(date +%Y%m%d-%H%M%S)

if [[ ! -d "$source_dir" ]]; then
    echo "Source directory does not exist: $source_dir" >&2
    exit 1
fi

mkdir -p "$backup_dir"

archive_name="backup-${timestamp}.tar.gz"
archive_path="${backup_dir}/${archive_name}"

tar -czf "$archive_path" -C "$source_dir" .

echo "Backup created: $archive_path"

실행 권한을 추가한다.

chmod +x backup.sh

실행한다.

./backup.sh ~/projects ~/backups

이 예제에는 다음 개념이 함께 사용된다.

Shebang
Strict mode
Script arguments
Condition
Variables and quoting
Command substitution
Exit status

문법 검사

실행하지 않고 Bash 문법만 확인한다.

bash -n backup.sh

ShellCheck를 설치하면 quoting, 변수 사용, portability와 관련된 문제를 정적으로 검사할 수 있다.

Ubuntu:

sudo apt install shellcheck

macOS:

brew install shellcheck

검사한다.

shellcheck backup.sh

자동 formatting이 필요하다면 shfmt도 사용할 수 있다.


정리

Linux shell script의 기본 흐름은 다음과 같다.

1. Shebang으로 interpreter 지정
2. Terminal command를 파일에 작성
3. chmod +x로 실행 권한 추가
4. 변수와 path는 double quote로 보호
5. if, for, while, function으로 흐름 구성
6. Exit status로 성공과 실패 판단
7. Redirection과 pipe로 command 연결
8. bash -n과 ShellCheck로 검사

처음에는 terminal에서 반복해서 입력하는 command를 .sh 파일로 옮기는 것부터 시작하면 된다. Script가 길어지면 함수로 역할을 나누고, argument 검사와 error 처리를 추가하면 재사용하기 쉬운 자동화 도구가 된다.