- Unix 계열에는 이름이 한 글자 기호인
/bin/[실행 파일이 있을 수 있으며, 셸 조건식처럼 보이는 문법도 실제로는 명령 실행과 종료 코드 위에 놓여 있음 test는 표현식을 평가해 참이면 0, 거짓이면 1을 반환하고,[로 호출되면 마지막 인자가]인지까지 확인함- 많은 셸은
test와[를 내장 명령으로도 제공하므로, 외부/bin/test와 셸 내장 구현의 오류 메시지나 동작이 달라질 수 있음 - Bash 확장인
[[는 외부 명령이 아니라 내장 구문이라, 예시처럼long*를 글롭 확장하지 않고 리터럴 문자열로 비교하는 등[와 다른 규칙을 적용함 - 이식 가능한 스크립트에는
[가 맞고, Bash 전용이라면[[를 일관되게 쓰는 편이 낫지만 두 방식의 확장 규칙 차이를 알고 선택해야 함
/bin/[와 /bin/test의 정체
- Unix 시스템에는 이름이 한 글자 기호인 실행 파일
/bin/[가 있을 수 있음- 예시 명령
ls /bin/?는/bin/[를 보여줌
- 예시 명령
/bin/[와/bin/test는 같은 바이너리를 가리킬 수 있음- 예시에서는 두 경로가 같은 inode와 크기를 가진 파일로 나타남
- 다만 모든 시스템에서 하드 링크일 필요는 없음
test는 셸에서 표현식을 평가하는 프로그램임- 문자열 비교
- 숫자 비교
- 파일 조건 확인
- 평가 결과가 참이면 종료 코드 0, 거짓이면 1을 반환함
왜 [가 명령처럼 동작하는가
test a = b는 조건식처럼 보이기 어렵지만, 같은 로직을[ a = b ]처럼 쓰면 더 익숙한 형태가 됨[는 별도 문법처럼 보이지만 실제로는 명령 호출임if [ a = b ]; then ... fi는[명령을 실행하고 종료 코드를 확인함[로 호출되면 프로그램은 마지막 인자가 닫는 대괄호]인지 검사함
if문은 조건식을 직접 해석하는 대신, 전달된 명령의 종료 코드를 기준으로 분기함test a = a; echo $?는0test a = b; echo $?는1[ a = a ]; echo $?는0[ a = b ]; echo $?는1
- 같은 맥락에서
true와false도 종료 코드를 반환하는 보조 바이너리로 볼 수 있음
외부 바이너리와 셸 내장 명령의 차이
test와[는 셸 스크립트에서 자주 쓰이기 때문에, 대부분의 셸은 이를 내장 명령으로도 구현함- 같은 입력이라도 외부 바이너리와 셸 내장 명령의 출력이 다를 수 있음
/bin/test a b는test: a: unexpected operatortest a b는dash: 2: test: a: unexpected operator
- 이런 차이는
test와[뿐 아니라echo처럼 단순해 보이는 명령에서도 발생할 수 있음 - 셸마다 내장 구현이 달라, 스크립트 동작이 실행 셸에 따라 달라질 여지가 있음
Bash 확장 [[가 적용하는 별도 규칙
[[는 Bash 확장이며[사용을 대체할 수 있음- 가장 큰 차이는
[[가 항상 내장 구문이라는 점임- 외부 바이너리로 실행될 수 있는
[와 달리,[[에서는 Bash가 표현식 내부의 언어 규칙을 바꿀 수 있음
- 외부 바이너리로 실행될 수 있는
- 글롭 예시에서
[와[[는 서로 다르게 동작함touch long-name후[ long* = long-name ] && echo match는match를 출력함[명령의 인자에는 일반 셸 확장 규칙이 적용되어long*가 디렉터리의long-name으로 확장됨[[ long* = long-name ]] && echo match는 출력하지 않음[[는long*를 리터럴 문자열로 다뤄long-name과 그대로 비교하므로 실패함
- Bash 전용 스크립트라면
[[로 정규식 매칭=~같은 기능도 사용할 수 있음
스크립트에서 무엇을 선택할지
- 이식 가능한 셸 스크립트에는
[를 사용하는 편이 맞음 test도 사용할 수 있지만 일반적인 선택은 아님- 스크립트가 Bash 전용이라면
[[를 일관되게 쓰는 편이 나음 - 셸 자체에도
!,&&,||같은 표현식 연산자가 있음- 이 연산자들은 명령의 종료 상태를 기준으로 동작함
grep ^hello$ ... && grep ^bye$ ...는 두 명령이 모두 성공하면 전체 종료 코드가0이 됨- 첫 번째
grep이 실패하면&&뒤 명령까지 성공하지 않아 전체 종료 코드가1이 됨
- 따라서
test/[표현식과 셸의 논리 연산자를 한 조건문 안에서 조합할 수 있음- 예:
[ a = b ] || grep -q ^hello$ /usr/share/dict/words
- 예:
/bin/[와/bin/test는 POSIX에서 하드 링크일 것을 요구하지 않음- NetBSD에서는 하드 링크였음
- macOS Catalina는 같은 바이너리의 별도 복사본을 제공함
- Debian testing은 서로 다른 바이너리를 제공함
- POSIX 명세는 두 파일이 링크일 것을 요구하지 않음