본문 바로가기
목차
shell

Shell Expansion: Brace Expansion, Tilde Expansion, Variable Expansion, Command Substitution, Arithmetic Expansion, Filed Splitting, Globbing

by ds31x 2026. 1. 11.
728x90
반응형

시작하기

Shell(bash, zsh 등)은 사용자가 입력한 문자열을 바로 실행하지 않는다.

  • 먼저 언어 처리 파이프라인을 거쳐
  • 문자열을 실제 실행 인자 목록으로 변환한 뒤 실행함.

POSIX / bash에서 정의된 처리 순서는 다음과 같음:

0. Lexical analysis(어휘 분석) (quoting, backslash 처리)
1. Brace expansion(중괄호 확장)
2. Tilde expansion(틸드 확장)
3. Value expansion
	* Parameter expansion(매개변수 확장)
	* Command substitution(명령 치환)
	* Arithmetic expansion(산술 확장)
4. Field splitting(필드 분리) (word splitting)
5. Pathname expansion(경로명 확장) (globbing)
6. Quote removal(따옴표 제거)
7. 실행

 

이 과정을 shell expansion이라고도 부르면 이는 다음으로 구성됨.

  • brace expansion
  • tilde expansion
  • parameter expansion(=variable expansion)
  • command substitution
  • arithmetic expansion
  • field splitting (=word splitting)
  • pathname expansion(=globbing)

 

주의:

  • 위 순서는 “확장(expansion)과 단어 생성(word generation)” 관점에서 정리한 규칙임.
  • 실제 실행 전에는 위에서 언급한 처리 외에 pipe/redirection 같은 연산자 파싱이 함께 일어난다.

이 문서는 문자열에서 단어가 어떻게 바뀌는가 에만 집중하여
Shell Expansion에 대해 정확한 이해를 하도록 돕는게 목적임.


0. Lexical analysis — quoting과 backslash(escape)

Lexer로도 불리는 단계에서

  • shell은 입력 문자를 읽어 토큰(word)을 만들고 (tokenizer),
  • 각 문자가 literal인지 아니면 expansion(확장) 대상인지를 구분함 (lexer).
  • operator와 word를 구분하는 동작도 이루어지지만 이 문서는 expansion과 관련된 word에만 집중함
  • 원래 컴파일러에서의 Lexer는 다음과 같음: lexer = tokenizer + token 분류 + 상태 관리

 

Shell에도 lexical analysis 단계가 존재하지만,
컴파일러처럼 ‘lexer’나 ‘tokenizer’라는 이름의 구성요소로 따로 분리하진 않음.
bash의 문서등에서도 lexical analysis라고만 부름.

 

처리되는 문법:

문법 의미
'...' 완전 리터럴 구간 (... 을 모두 lieteral로)
"..." 부분 리터럴 구간 (일부 부분만 literal로)
\ 다음 문자 1개를 리터럴로(문자 수준 escape)
  • 주의할 점은 이 단계에서는 값이 바뀌지 않는다는 점임.
  • 이 단계에선 오직 이후 expansion 단계가 적용될 위치만 결정됨.

lexical의 어원은 그리스어 λέξις (léxis) 이며 의미는 말, 단어, 표현, 어휘 임.

 

token에 대한 개념이 헷갈리면 다음을 참고:

literal에 대한 개념이 헷갈리면 다음을 참고:


0-1. Single quote '...'

Single quote 안에서는 모든 expansion과 escape가 차단 됨.

 

\ 백슬래시도 특별한 의미(escape)를 잃고 그냥 문자 \가 된다.

요소 동작
$VAR 차단 (Variable Expansion)
$(cmd) 차단 (Command Substitution)
$(( )) 차단 (Arithmetic Expansion)
~ 차단 (Tilde Expansion)
{a,b} 차단 (Brace Expansion)
* ? [ ] 차단 (Globbing)
\ 리터럴 문자 취급
공백 분리 차단

 

다음의 예를 참고

echo '\$HOME \* \n $(date)'
# \$HOME \* \n $(date)

0-2. Double quote "..."

Double quote는 value expansion($ or `로 시작하는 expansion들)을 허용하고

단어 구조 변화(field spliting)globbing, tilde expansion, brace expansion 을 차단한다:
특수 문자들 중 $ (dollar sign), \ (backslash), `(backtick) 는 고유한 기능적 의미를 유지하나 나머진 의미를 잃음

$VAR 허용 (Variable Expansion)
$(cmd) 허용 (Command Substitution)
$(( )) 허용 (Arithmetic Expansion)
~ 차단 (Tilde Expansion)
{a,b} 차단 (Brace Expansion)
* ? [ ] 차단 (Globbing)
공백 분리 차단

0-2-1. Double quote 안에서의 escape 규칙(중요)

" 안에서 \는 다음 문자에 대해서만 escape로 작동.

\ 뒤 문자 결과
" "
\ \
$ $
` `
그 외 (n, t 등) \n, \t 그대로

 

예제로 확인할 것:

echo "\\ \$HOME \"test\""
# \ $HOME "test"

0-3. Escape \ (문자 수준 escape)

이 단계에서
\는 quoting과 독립적인 lexical escape로,
다음에 위치하는 문자 1개를 메타문자에서 제외 한다.

제어문자(newline, tab 등)를 표현하기 위한 escape seqeuence 처리는 여기서 이루어지지 않음.

 

다음 예에서 앞에 놓인 backslash로 인해 asterisk 와 환경변수가 원래의 기능이 아닌 literal 문자  처리됨.

echo \* \$HOME
# * $HOME

참고: Escape sequence(개행/벨/백스페이스)는 어디서 처리되나?

\n, \a, \b 같은 “제어문자 변환”은 \ 만을 붙인다고 자동으로 처리되진 않음.

 

그 변환은 다음과 같은 특정 문맥/도구에서 처리함.

 

1. printf (POSIX 표준, 권장) — 제어문자 변환은 printf가 처리

printf "A\nB\n"
printf "bell:\a done\n"
printf "ab\bX\n"

 

 

2. $'...'  (bash/zsh의 ANSI-C quoting) — 이 quoting이 \n 등을 실제 제어문자로 변환

printf "%s" $'A\nB\n'
printf "%s" $'\a'
printf "%s" $'ab\bX\n'

 

3. echo -e (비표준/셸마다 다름, 비권장) — 동작이 일관되지 않아 스크립트에선 피하는 편이 안전

  • bash에서 -e 옵션(enable)을 켜야 escape sequence를 처리하고, 기본으론 처리를 하지 않으나,
  • zsh에선 기본으로 escape sequence를 처리하고 -E 옵션(disable)을 주면 처리를 안 함.

스크립트 작성 시
가급적 echo -e 대신 printf를 사용할 것.


1. Brace expansion

문자열 패턴을 여러 개로 복제함.

 

다음의 예를 참고할 것:

echo file{1,2,3}.txt
# file1.txt file2.txt file3.txt
echo {a,b}{1,2}
# a1 a2 b1 b2

1-1. Range 형식의 brace expansion

숫자나 문자를 범위로도 생성할 수 있음: ASCII 에서의 순서 기반.

echo {1..7}
# 1 2 3 4 5 6 7
echo chap{1..5}
# chap1 chap2 chap3 chap4 chap5
echo {a..e}
# a b c d e

1-2. Brace expansion에서 주의할 점.

Brace expansion은 파일 시스템과 무관 한 단순한 문자열 처리임 ( globbing 과의 차이점),

 

그리고, globbing보다 먼저 수행된다.

brace expansion은 POSIX 필수 기능은 아니지만,
bash/zsh에서 일반적으로 제공됨.

 

실제 사용에선 표준처럼 널리 쓰이지만, Python의 glob 모듈에선 지원하지 않음.

 

그리고, Quote 안에서는 작동하지 않음.


2. Tilde expansion

Home 디렉토리 기호 ~를 실제 경로로 치환됨.

 

다음 예제를 참고:

echo ~
# /home/dsaint31

echo ~root
# /root

 

이는 Quote 안에서는 작동하지 않음.

echo "~"
# ~

3. Parameter / Command / Arithmetic expansion

이 단계에서 실제 값 계산이 일어난다.

3-1. Parameter expansion ($VAR, ${VAR})

Variable expansion 이라고도 불림.

 

다음의 사용예를 참고할 것.

name=kim
echo $name
# kim

echo ${HOME}/work
# /home/dsaint31/work

3-2. Command substitution ($(cmd))

예전 버전에선 backtick 으로 감싸는 방식으로 사용했으나 중첩 등이 안되므로 가급적 사용하지 말 것.

 

이전 방식

echo "Today is `date`"

 

권장 방식

echo "Today is $(date)"

 

$( )를 이용할 경우, 중첩도 가능:

echo "files: $(ls "$(pwd)")"

보다 자세한 건 다음을 참고:

2023.10.01 - [shell] - [Shell] command substitution

 

[Shell] command substitution

command substitution (명령어치환)우리나라말로 명령어 치환 이라고 불리며,특정 명령어의 수행결과를 문자열로 입력받는 형태로 셀프로그래밍 등에서 사용됨.command substitution 사용법아래 예제는 resu

ds31x.tistory.com


3-3. Arithmetic expansion ($((...)))

정수 연산만 된다는 점을 주의할 것.

echo $((3 + 4))
# 7

a=5; b=3
echo $((a*b + 2))
# 17

4. Field splitting

앞 단계에서 확장된 문자열을 공백(IFS) 기준으로 나눔(spliting).

구조적 변화를 일으킴.

이 단계 때문에
가급적 파일과 디렉토리 이름에
공백을 넣지 않는게 좋다는 애기가 나옴.

 

4-1. IFS란?

  • Internal Field Separator의 약자,
  • shell이 field splitting(단어 분리) 을 수행할 때 사용할 구분 문자 집합을 뜻함.
  • 기본값은 보통 space / tab / newline 임,
  • 환경 변수 IFS에 space, tab, newline 이 할당되어 있음: echo ${IFS} 

4-2. 동작

Field splittingunquoted 의 expansion(확장) 결과에 대해서만 IFS를 기준으로 인자가 나눔.

  • 이 단계 때문에 "${var}"가 권장됨
    (변수의 값에 공백문자가 있는 경우 하나로 처리하려면 quoting이 필요)
  • IFS를 바꾸면 분리 기준도 바뀐다

다음은 IFS를 :로 바꾼 경우임 (bash에서만 동작함).

IFS=:
set -- ${PATH}
printf "%s\n" "$1" "$2"
  • zsh에선 unquoted parameter expansion에서 field splitting을 수행하지 않음.
  • 때문에  $1 (1st positional parameter)에 $PATH의 전체 문자열이 들어감.
  • zsh에선 parameter expansion modifer를 사용해야 함.
더보기
set -- ${(s/:/)PATH}
printf "%s\n" "$1" "$2"

set은 positional arguments를 설정하는 명령어임.

더보기

set 은 shell의 option 을 바꾸거나 positional argument ($1, $2, ...)을 설정하는 명령임(cmd.exe와 다름)

-- 은 linux의 shell 명령어에 option이 끝났음을 알리는 옵션임. -- 이후는 모두 argument로 처리됨.

 

참고로 Linux 쉘에선 변수 할당은

a="bbb" # a 변수에 bbb 문자열 할당.

 

cmd.exe의 경우

set a="bbb" 

 

다음은 일반적인 경우의 예임

x="a b"
echo $x
# a b   (두 개의 인자)

echo "$x"
# a b   (하나의 인자)

 

또 다른 예:

files="$(printf "a b\nc d\n")"
echo $files      # 공백/줄바꿈으로 쪼개짐
echo "$files"    # 원형 유지(하나의 인자)

5. Pathname expansion (Globbing)

와일드카드를 현재 file system을 이용하여 실제 파일 이름 목록으로 바꾼다.

보다 자세한 내용은 다음을 참고:

2025.10.04 - [CE] - glob 이란?

 

glob 이란?

정의glob은 파일 이름과 경로를 간단한 와일드카드 패턴으로 매칭하는 방식을 가리킴. Bash, zsh 등의 shell 과 여러 프로그래밍 언어(Python의 glob 모듈 등)에서 폴더 내 파일을 찾거나 일괄 처리할 때

ds31x.tistory.com

 

주의할 점은 실제 파일을 찾아서 해당 이름 목록으로 바꾼다는 것임:

  • 패턴에 해당하는 파일이 없을 경우에 대한 처리가 shell마다 다르므로 주의해야 함.
  • bash 에선 매칭이 없을 때 패턴 문자열이 반환되는데,
  • shopt -s failglob 로 빈 매칭시 에러를 발생하도록 바꾸는 것이 권장됨.
  • zsh 에선 에러를 반환

다음은 간단한 예제임:

ls *.txt

 

a.txt b.txt가 있으면:

ls a.txt b.txt

 

Quote나 escape된 패턴은 globbing되지 않는다.

ls "*.txt"
ls \*.txt

참고: Globbing 사용할 때 “매칭이 없을 때”에 대한 처리 중요

globbing 매칭 실패 시 동작은 셸 옵션/셸 종류에 따라 달라질 수 있음.
(예: bash의 nullglob, failglob 등)

 

다시 한번 강조하지만 스크립트에서는 “매칭이 없을 수 있는 패턴”을 다룰 때 주의해야 함.

개인적으로는 shell scripting에선 find 등을 globbing보다 사용하는 것을 선호.

2024.05.11 - [Linux] - [linux] find 사용법.

 

[linux] find 사용법.

find 명령어 사용법find 명령어는 UNIX 및 Linux 기반 시스템에서파일이나 디렉토리를 검색할 때 사용하는 S/W.이 명령어는 지정된 "[검색 시작 경로]"에서부터 파일 시스템을 순회하며주어진 조건에

ds31x.tistory.com


6. Quote removal

이 단계에서 이제 역할이 끝난 ' " \ 가 실제 문자열에서 제거된다.

 

해당 제거는 모든 expansion과 globbing이 끝난 후에 처리된다.


예제 1 — double quote가 왜 최종 출력에 안 보이는가

echo "abc"

처리 흐름:

  1. Lexical analysis: "abc" 하나의 토큰
  2. Expansion 없음
  3. Quote removal -> abc
  4. echo abc 실행

출력:

abc

참고로 Windows의 cmd.exe에선 double quote가 보인다.


예제 2 — quote가 구조를 보호하고 나중에 사라짐

echo "$HOME *.txt"
  1. $HOME은 확장됨
  2. *.txt에 대한 globbing 은 double quoting에 의해 차단됨.
  3. Quote removalecho 명령어에 대한 실제 인자 는 다음과 같음:
/home/dsaint31 *.txt
  • *.txt가 그대로 출력되는 점에 유의할 것: globbing 차단.

예제 3 — escape도 최종 인자에서 사라짐

echo "\$HOME"
  1. Lexical 단계: $HOME이 literal로 마킹됨
  2. Expansion 안 됨
  3. Quote removal → $HOME

출력:

$HOME

종합 예

종합 예 1 — Double quote

echo "~/{a,b}/$(whoami)/*.txt"

 

Double quote 때문에:

단계 결과
brace double quote에 의해 차단 : {a, b}
tilde double quote에 의해 차단 : ~
command  whoami 명령어의 반환값인 dsaint31 로 expansion
field split double quote에 의해 차단 
IFS에 의해 분리되지 않음
(따옴표로 보호)
glob double quote에 의해 차단

 

출력:

~/{a,b}/dsaint31/*.txt

종합 예 2 — Single quote

echo '~/{a,b}/$(whoami)/*.txt'

 

Single quote 때문에:

항목 결과
brace / tilde / command / arithmetic 전부 차단
globbing 차단
backslash escape 차단(백슬래시는 그냥 문자)

 

출력:

~/{a,b}/$(whoami)/*.txt

요약

Shell은 단순한 명령 실행기가 아니라
문자열을 언어 규칙에 따라 계산하는 해석기다.

  • Brace → 문자열 생성
  • Tilde → 경로 계산
  • $, $(), $(( )) → 값 계산
  • Field splitting → 인자 분리
  • Globbing → 파일 이름 계산
  • Single quote → 모든 expansion 차단 (globbing도 넓은 의미에서 shell expansion임)
  • Double quote → 값 계산만 허용 + 제한적 escape(\\, \", \$, \`)
  • Escape(문자 수준) → 문자 하나 보호
  • Escape sequence(제어문자) → printf 또는 $'...'에서 변환
  • Quote removal → "'\ 제거
728x90