Bash 이식성
이식성 있는 POSIX 호환 스크립트를 작성하고, 필요에 따라 bash 전용 기능을 활용하는 방법에 대한 지침입니다.
Shebang 선택
Bash 스크립트: #!/usr/bin/env bash 사용
#!/usr/bin/env bash
이유: PATH에서 bash를 검색하므로 bash의 위치가 다른 시스템에서도 작동합니다.
POSIX 스크립트: #!/bin/sh 사용
#!/bin/sh
이유: bash 기능이 필요하지 않을 때 최대 이식성을 제공합니다. 많은 시스템에서 /bin/sh는 dash나 다른 POSIX 셸입니다.
직접 경로가 필요할 때
#!/bin/bash
다음 경우에만 사용: 시스템 요구 사항이 bash 위치를 보장하거나, 보안 정책에서 절대 경로를 요구하는 경우.
POSIX vs Bash 기능 매트릭스
| 기능 | POSIX | Bash | 권장 사항 |
|---|---|---|---|
[[ ]] | 아니오 | 예 | POSIX에서는 [ ] 사용 |
(( )) | 아니오 | 예 | [ ]와 -eq 등을 사용 |
| 배열 | 아니오 | 예 | 위치 매개변수 또는 파일 사용 |
local | 부분 | 예 | 일반적으로 안전 |
${var,,} | 아니오 | 4+ | POSIX에서는 tr 사용 |
<<< | 아니오 | 예 | echo | cmd 사용 |
=~ 정규식 | 아니오 | 예 | grep 또는 expr 사용 |
source | 아니오 | 예 | . (dot) 명령어 사용 |
function f() | 아니오 | 예 | f()만 사용 |
$'...' | 아니오 | 예 | printf 사용 |
{1..10} | 아니오 | 예 | seq 또는 while 루프 사용 |
POSIX 호환 패턴
조건문
# POSIX - 적절한 인용과 함께 [ ] 사용
if [ -f "$file" ]; then
echo "파일이 존재합니다"
fi
# 문자열 비교
if [ "$var" = "value" ]; then
echo "일치"
fi
# 숫자 비교
if [ "$num" -gt 10 ]; then
echo "큼"
fi
# 복합 조건
if [ -f "$file" ] && [ -r "$file" ]; then
echo "읽기 가능한 파일"
fi
대소문자 변환 (POSIX)
# 소문자로
lower=$(echo "$string" | tr '[:upper:]' '[:lower:]')
# 대문자로
upper=$(echo "$string" | tr '[:lower:]' '[:upper:]')
부분 문자열 연산 (POSIX)
# 부분 문자열 가져오기 - expr 또는 cut 사용
substr=$(expr "$string" : '.\{3\}\(.\{5\}\)') # 4-8번째 문자
substr=$(echo "$string" | cut -c4-8)
# 문자열 길이
length=$(expr length "$string")
length=${#string} # 이는 실제로 POSIX입니다
파일 읽기 (POSIX)
# 한 줄씩 읽기
while IFS= read -r line; do
echo "$line"
done < "$file"
# 전체 파일 읽기 (cat 없이)
content=$(cat "$file") # cat은 POSIX입니다
명령어 치환
# 최신 문법 (POSIX에서도 선호)
result=$(command)
# 레거시 문법 (피하세요)
result=`command`
# 중첩 (최신 문법이 더 나은 이유)
result=$(echo $(date)) # 명확함
result=`echo \`date\`` # 이스케이프 지옥
사용할 가치가 있는 Bash 전용 기능
이식성이 필요하지 않을 때, 다음 bash 기능은 코드 품질을 향상시킵니다:
확장 테스트 [[ ]]
# 패턴 매칭
[[ "$file" == *.txt ]]
# 정규식 매칭
[[ "$input" =~ ^[0-9]+$ ]]
# 단어 분할 걱정 없음
[[ -f $file ]] # 인용은 선택 사항 (그러나 여전히 권장)
# 내부에서 논리 연산자
[[ -f "$file" && -r "$file" ]]
배열
# 인덱스 배열
declare -a files=()
files+=("one.txt")
files+=("two.txt")
for f in "${files[@]}"; do
process "$f"
done
# 연관 배열 (Bash 4+)
declare -A config
config[host]="localhost"
config[port]="8080"
매개변수 확장
# 기본값
"${var:-default}"
# 대소문자 변환 (Bash 4+)
"${var,,}" # 소문자
"${var^^}" # 대문자
# 부분 문자열
"${var:0:10}" # 처음 10자
"${var: -5}" # 마지막 5자
# 검색/바꾸기
"${var//old/new}"
Here 문자열
# Bash
read -r var <<< "input string"
# POSIX 동등
var=$(echo "input string")
프로세스 치환
# Bash - 두 명령어 출력 비교
diff <(sort file1) <(sort file2)
# POSIX 동등 (임시 파일 사용)
sort file1 > /tmp/sorted1
sort file2 > /tmp/sorted2
diff /tmp/sorted1 /tmp/sorted2
셸 유형 감지
# Bash에서 실행 중인지 확인
if [ -n "${BASH_VERSION:-}" ]; then
echo "Bash에서 실행 중"
fi
# Bash 버전 확인하여 기능 확인
if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
echo "Bash 4+ 사용 가능"
fi
# 일반적인 셸 감지
case "${SHELL##*/}" in
bash) echo "bash" ;;
zsh) echo "zsh" ;;
*) echo "기타" ;;
esac
이식성 유틸리티 함수
이식성 결정 가이드
POSIX를 사용할 때:
- 스크립트가 최소 시스템(컨테이너, 임베디드)에서 실행됨
- 대상에 dash, ash 또는 busybox sh가 포함됨
- 최대 호환성이 요구됨
- 스크립트가 시스템 초기화의 일부임
Bash를 사용할 때:
- 대상 시스템에 bash가 있음이 보장됨
- 배열, 연관 배열 또는 정규식이 필요함
- 복잡한 문자열 조작이 필요함
- 코드 명확성이 크게 향상됨
- 대화형 기능이 필요함
일반적인 이식성 함정
echo vs printf
# 문제가 있음 - 동작이 다양함
echo -n "개행 없음"
echo -e "탭\t포함"
# 이식성 있음
printf '%s' "개행 없음"
printf '탭\t포함\n'
변수 할당
# 모든 곳에서 작동
var="value"
# 일부 셸에서 실패할 수 있음
var = "value" # = 주변 공백은 잘못됨
할당과 함께 export 사용
# POSIX - 별도 명령어
var="value"
export var
# Bash/최신 - 결합 (대부분의 환경에서 작동)
export var="value"
배열이 없는 배열형 연산
# 위치 매개변수 사용
set -- "item1" "item2" "item3"
for item in "$@"; do
echo "$item"
done
# 또는 IFS 기반 분할
items="item1:item2:item3"
IFS=':' read -r item1 item2 item3 <<EOF
$items
EOF