Bash 문자열 조작

Bash 파라미터 확장을 이용한 문자열 조작을 설명합니다. 길이, 부분 문자열, 대소문자 변환, 앞/뒤 패턴 제거(#/%), 치환(//), 기본값 설정, 파일 경로 분해 패턴을 다룹니다.

· 5 min read · PALDYN Team

지난 글에서 Bash 산술 연산의 다양한 방법을 살펴봤습니다. 이번엔 문자열을 자르고, 바꾸고, 합치는 파라미터 확장 기법을 정리합니다. 외부 명령 없이 ${} 문법만으로 대부분의 문자열 변환을 처리할 수 있습니다.

길이와 부분 문자열

STR="Hello, World!"

# 길이
echo "${#STR}"        # 13

# 부분 문자열: ${var:offset:length}
echo "${STR:7}"       # World!
echo "${STR:7:5}"     # World
echo "${STR: -6}"     # orld!  (음수 offset — 뒤에서)
echo "${STR: -6:4}"   # orld

음수 오프셋 앞에 공백을 넣어야 합니다(${var: -n}). 붙여 쓰면(${var:-n}) 기본값 문법으로 해석됩니다.

Bash 문자열 조작 문법

대소문자 변환 (Bash 4.0+)

STR="Hello World"

echo "${STR^^}"    # HELLO WORLD  (전체 대문자)
echo "${STR,,}"    # hello world  (전체 소문자)
echo "${STR^}"     # Hello World  (첫 글자 대문자)
echo "${STR,}"     # hello World  (첫 글자 소문자)

# 특정 문자만
echo "${STR^^[aeiou]}"   # HEllO WOrld  (모음 대문자)

패턴 제거 — # / ## / % / %%

#%는 각각 앞쪽(왼쪽)과 뒤쪽(오른쪽)에서 패턴을 제거합니다. 단일 기호는 최단 일치, 이중 기호는 최장 일치입니다.

FILE="/path/to/file.tar.gz"

# 앞에서 제거
echo "${FILE#*/}"     # path/to/file.tar.gz  (첫 / 까지)
echo "${FILE##*/}"    # file.tar.gz           (마지막 / 까지)

# 뒤에서 제거
echo "${FILE%.*}"     # /path/to/file.tar    (마지막 . 부터)
echo "${FILE%%.*}"    # /path/to/file         (첫 . 부터)

# 실용 패턴
filename="${FILE##*/}"     # 파일명 (basename)
dir="${FILE%/*}"           # 디렉터리 (dirname)
ext="${filename##*.}"      # 확장자
stem="${filename%%.*}"     # 확장자 제거한 이름

이 패턴을 외우면 basename, dirname 외부 명령 없이도 경로를 처리할 수 있습니다.

치환 — / 와 //

STR="foo bar foo baz"

echo "${STR/foo/FOO}"    # FOO bar foo baz  (첫 번째만)
echo "${STR//foo/FOO}"   # FOO bar FOO baz  (전체)
echo "${STR/#foo/FOO}"   # FOO bar foo baz  (앞에서만)
echo "${STR/%baz/BAZ}"   # foo bar foo BAZ  (뒤에서만)
echo "${STR// /}"        # foobarf oobaz    (공백 제거)

치환 문자열을 비우면 삭제와 같습니다.

# 줄바꿈 제거
clean="${STR//$'\n'/}"

# 경로에서 특수 문자 이스케이프
safe="${PATH//:/\\:}"

문자열 조작 실전 패턴

기본값과 존재 확인

# 변수가 미설정이거나 빈 문자열이면 기본값 반환
echo "${NAME:-Anonymous}"

# 미설정이면 값을 설정하고 반환
echo "${NAME:=DefaultName}"

# 설정되어 있을 때만 대체 값 반환
echo "${DEBUG:+verbose}"

# 미설정이면 오류 메시지와 함께 종료
echo "${REQUIRED:?'REQUIRED가 설정되어야 합니다'}"

# 설정 여부만 확인
[[ -v NAME ]] && echo "설정됨"

:--의 차이: :-은 빈 문자열도 미설정으로 간주하고, -는 실제로 unset인 경우만 처리합니다.

구분자로 분리하고 결합

# IFS + read로 분리
IFS=',' read -ra items <<< "apple,banana,cherry"
echo "${items[0]}"   # apple
echo "${#items[@]}"  # 3

# IFS + echo로 결합
arr=("a" "b" "c")
joined=$(IFS=','; echo "${arr[*]}")
echo "$joined"   # a,b,c

printf로 문자열 포매팅

# 폭과 정렬
printf "%-20s %s\n" "파일명" "크기"
printf "%-20s %10d\n" "report.pdf" 1234567

# 16진수로 인코딩
printf '%s' "hello" | xxd -p   # 68656c6c6f

# URL 인코딩 흉내 (간단 버전)
encode() {
    printf '%s' "$1" | od -An -tx1 | tr ' ' '%' | tr -d '\n' | tr -d ' '
}

실전 예제 — 설정 파일 파서

#!/usr/bin/env bash
parse_config() {
    local file=$1
    while IFS='=' read -r key value; do
        # 주석과 빈 줄 건너뜀
        [[ $key =~ ^[[:space:]]*# ]] && continue
        [[ -z "${key// /}" ]] && continue

        # 앞뒤 공백 제거
        key="${key#"${key%%[![:space:]]*}"}"
        key="${key%"${key##*[![:space:]]}"}"
        value="${value#"${value%%[![:space:]]*}"}"
        value="${value%"${value##*[![:space:]]}"}"

        # 동적 변수 설정
        declare -g "CFG_${key^^}=$value"
    done < "$file"
}

parse_config myapp.conf
echo "${CFG_HOST}"
echo "${CFG_PORT}"

지난 글: Bash 산술 연산

다음 글: Bash 파라미터 확장 심화


읽어주셔서 감사합니다. 😊