Bash 프로세스 치환

Bash 프로세스 치환 <(cmd)와 >(cmd)의 동작 원리와 파이프와의 차이를 설명합니다. while 루프에서 변수 범위를 보존하는 패턴, diff·comm·tee와의 결합 활용을 다룹니다.

· 4 min read · PALDYN Team

지난 글에서 Here Document와 Here String으로 텍스트를 stdin에 주입하는 방법을 배웠습니다. 이번엔 명령 출력 자체를 파일처럼 사용할 수 있게 해 주는 프로세스 치환을 다룹니다.

프로세스 치환이란

<(cmd) 형태를 프로세스 치환(process substitution)이라고 합니다. Bash는 이것을 /dev/fd/63과 같은 특수 파일 경로로 교체하고, 백그라운드에서 cmd를 실행해 그 stdout을 해당 경로로 연결합니다.

# 이렇게 쓰면
diff <(sort file1.txt) <(sort file2.txt)

# Bash 내부적으로 이런 일이 일어남
sort file1.txt > /dev/fd/63 &   # 백그라운드
sort file2.txt > /dev/fd/62 &   # 백그라운드
diff /dev/fd/63 /dev/fd/62

결과적으로 명령이 파일 경로를 기대하는 자리에 명령 출력을 바로 넣을 수 있습니다.

프로세스 치환 개념

파이프와의 결정적 차이 — 변수 범위

파이프라인에서 오른쪽 명령은 서브셸에서 실행됩니다. 그래서 while 루프 안에서 변수를 바꿔도 루프 바깥에서는 보이지 않습니다.

# ❌ 파이프 방식 — count가 서브셸에서만 증가
count=0
find . -name "*.log" | while IFS= read -r f; do
    (( count++ ))
done
echo "$count"   # 0  ← 항상 0

# ✅ 프로세스 치환 — while이 현재 셸에서 실행
count=0
while IFS= read -r f; do
    (( count++ ))
done < <(find . -name "*.log")
echo "$count"   # 실제 파일 수

done < <(cmd) 형태에서 첫 <는 리다이렉션, 두 번째 <(는 프로세스 치환입니다.

<(cmd) — 읽기 치환

# 두 명령 출력을 파일 없이 diff
diff <(ls /etc | sort) <(ls /usr/etc | sort)

# 원격 파일과 로컬 파일 비교
diff <(ssh server "cat /etc/nginx/nginx.conf") /etc/nginx/nginx.conf

# comm으로 공통 줄 찾기 (정렬된 입력 필요)
comm -12 <(sort file1.txt) <(sort file2.txt)

# paste로 두 명령 출력을 열 방향 병합
paste <(cut -d: -f1 /etc/passwd) <(cut -d: -f6 /etc/passwd)

프로세스 치환 실전 패턴

>(cmd) — 쓰기 치환

>(cmd) 형태를 쓰면 파일에 쓰듯 명령에 데이터를 보낼 수 있습니다.

# tee로 동시에 여러 명령에 보내기
generate_data | tee >(gzip > output.gz) >(wc -l > count.txt) > /dev/null

# 로그를 동시에 화면과 파일로
make 2>&1 | tee >(grep "error" > errors.log)

프로세스 치환 + mapfile

# 명령 출력을 배열로 (서브셸 없이)
mapfile -t files < <(find . -name "*.sh" | sort)
echo "발견된 스크립트: ${#files[@]}개"

for f in "${files[@]}"; do
    echo "처리: $f"
done

find ... | mapfile이 아니라 mapfile < <(find ...)를 쓰면 mapfile이 현재 셸에서 실행되어 배열이 바깥에서 보입니다.

주의사항

# ksh/sh에서는 지원하지 않음 — Bash, Zsh 전용
# /proc/self/fd 경로 기반 — WSL 등에서는 다를 수 있음

# 오류 처리가 어려움
# cmd가 실패해도 메인 명령에서 오류로 잡기 어려움
diff <(cmd_that_fails) file.txt   # diff는 성공 가능

프로세스 치환 안의 명령 오류는 pipefail 옵션을 켜도 잡히지 않을 수 있습니다. 신뢰성이 중요한 경우 임시 파일을 명시적으로 만들어 사용하는 것이 더 안전합니다.


지난 글: Bash Here Document와 Here String

다음 글: Bash 산술 연산


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