쉘 콜론(:)은 아무것도 하지 않습니다. 그래도 사용하세요

쉘 콜론(:)은 아무것도 하지 않습니다. 그래도 사용하세요

소개

쉘 콜론(:)은 인수를 평가하고 그 결과를 폐기하는 내장 null-command입니다. 그 자체로는 아무 일도 하지 않지만, 매개변수 확장(parameter expansion) 앞에 배치하면 쉘이 결과 문자열을 명령어로 실행하려고 시도하지 않고도 확장이 일어나도록 허용합니다.

필수 인수 유효성 검사

${VAR:?diagnostic} 확장에 :를 사용하면 변수가 설정되어 있고 비어 있지 않은지 확인하는 한 줄짜리 방법을 제공합니다.

: "${1:?missing argument, aborting.}"
echo "Hello $1!"

만약 $1이 설정되지 않았거나 비어 있으면, 쉘은 진단 메시지를 stderr에 출력하고 non-zero 상태로 종료됩니다. 그렇지 않으면 확장은 $1의 값을 반환하고 스크립트는 계속됩니다. 동일한 기술이 이름이 있는 변수에도 적용됩니다. 예를 들어 : "${GREET_NAME:?missing argument, aborting.}"는 오류 메시지에 변수 이름을 포함합니다.

:=를 사용한 기본값 설정

${VAR:=default} 확장은 VAR가 설정되지 않았거나 비어 있을 때 기본값을 할당합니다. :를 접두사로 사용하면 할당을 수행하면서도 할당된 값을 폐기합니다.

: "${DATA_DIR:=/var/data}"
: "${RETRIES:=3}"

이 줄들이 실행된 후, DATA_DIR와 RETRIES가 이미 설정되어 있지 않았다면 기본값을 갖게 됩니다.

파일 자르기 및 테스트

: 뒤에 오는 리다이렉션은 null-command가 출력을 생성하지 않기 때문에 파일의 길이를 0으로 만듭니다(truncate).

: > error.log # error.log를 자름
: > error.log > access.log # 두 파일 모두를 자름

마찬가지로, 리다이렉션과 :만 포함된 서브쉘을 사용하여 읽기 또는 쓰기 권한을 테스트할 수 있습니다.

( : < dataset.json ) && echo YES # dataset.json을 읽을 수 있는가?
( : >> result.json ) && echo YES # result.json에 쓸 수 있는가?

콜론이 필요한 이유는 서브쉘에 명령어가 필요하기 때문일 뿐이며, 리다이렉션 자체가 작업을 수행합니다.

trap 및 조건문에서의 콜론 사용

trap 내장 명령은 명령어를 기대합니다. :는 아무것도 하지 않으면서 그 요구 사항을 충족합니다.

trap : INT
sleep 60 # sleep이 인터럽트 가능하게 유지됨

then 분기에 명령어가 포함되어야 하는 if-else 문에서 :는 플레이스홀더 역할을 할 수 있습니다.

if some-command; then : # 명령어가 필요함 else echo "command failed" fi

자주 묻는 질문 (기사 내용 중)

왜 null-command가 필요한가요? 콜론 없이도 확장이 일어나지 않나요?

매개변수 확장은 상관없이 발생하지만, null-command나 유사한 구조가 없으면 쉘은 결과 문자열을 실행할 명령어로 취급합니다. 예를 들어:

% ${HELLO:=123} zsh: command not found: 123

:를 접두사로 사용하면 확장을 수행하면서도 결과를 폐기합니다:

% : ${HELLO:=123} % echo $HELLO 123

VAR=${VAR:-default-value}를 사용할 수 있는데 왜 null-command를 사용하나요?

둘 다 동일한 효과를 내지만, 콜론 형태는 변수를 한 번만 언급하므로 오타가 발생할 확률을 줄여줍니다:

: "${DATA_DIR:=/var/data}" # DATA_DIR가 한 번만 나타남 DATA_DIR="${DATA_DRI:-/var/data}" # 오타 가능성 있음 (DATA_DRI)

커뮤니티 반응 (선택된 HN 댓글)

  • @gpvos가 Larry Wall의 언어 재설계 법칙을 회상했습니다: "모두가 콜론을 원한다. Larry가 콜론을 얻었다."

  • @amiga386가 if some-command; then : else echo "command failed" fi 관용구를 언급하며, 그와 동일한 if! some-command; then echo "command failed" fi 또한 POSIX 표준임을 지적했습니다.

  • @rgrau는 :? 테스트를 명령어 인수에 직접 인라인화할 것(예: echo "${1:? first param required}")을 제안했고, 루프 본문에서 콜론을 사용하는 while 루프 패턴을 설명했습니다.

  • @fphilipe는 Git에서 에디터를 열지 않고 auto-squash를 사용하여 대화형 rebase를 수행하기 위해 콜론을 EDITOR로 사용합니다.

  • @archargelod는 읽기 쉬운 if 문을 : "${1:?...}"로 교체하는 것이 가독성을 해친다고 주장하며, 대안으로 [ -z "$1" ] && { echo "missing argument, aborting." 1>&2; exit 1 }를 제시했습니다.

  • @kevincox는 :? 형식이 필수 환경 변수를 확인하는 데 유용하다고 생각하지만, 다른 많은 예시들은 전용 명령어를 사용하는 것이 더 명확하다고 보았습니다.

  • @kazinator는 서브쉘 테스트 ( : < file ) && echo YES가 불필요하다고 비판하며, 리다이렉션만으로도 읽기/쓰기 권한을 테스트할 수 있고 : >> file로 0 길이 파일을 만드는 것이 바람직하지 않을 수 있다고 언급했습니다.

  • @olexsmir는 개인적인 사용 사례를 공유했습니다: dotfiles 부트스트랩 스크립트에서 : "${DOTFILES_PATH:=$HOME/.dotfiles}"를 사용하여 구성 환경 변수의 기본값을 설정합니다.

  • @luciana1u는 콜론은 아무것도 하지 않기 때문에 "LLM이 과하게 설계(over-engineer)할 수 없는 유일한 bash 명령어"라고 말했습니다.

  • @m2f2는 getopts 스타일의 루프에서 콜론을 사용하는 방식을 설명했습니다: while : ; do case "$1" in... esac done.

  • @jeffrallen은 저자에게 감사 인사를 전했지만, 그는 청중에게 스스로를 설명하는 코드를 선호하기 때문에 이 관용구를 사용하지 않을 것이라고 말했습니다.

  • @lucideer는 예시들이 단순히 읽기 쉬운 다중 행 코드를 한 줄짜리 코드로 바꾼 것에 불과하며, 이는 스크립트에서 설 자리가 없다고 느꼈습니다.

  • @normie3000은 if! x; then something; fi보다 if x then :; else something; fi를 선호했습니다.

  • @PunchyHamster는 이러한 한 줄짜리 코드를 도입하는 pull request는 거절할 것이라고 말하며, 긴 스크립트는 Python이나 Perl로 다시 작성할 것을 옹호했습니다.

  • @garethrowlands는 이러한 트릭의 필요성이 쉘 구문의 오래된 문자열 치환 특성에서 기인한다고 주장했습니다.

  • @ocd는 잊어버린 유틸리티를 빠르게 grep하기 위해 : alias f3probe와 같이 아무것도 하지 않는 alias를 만드는 데 콜론을 사용합니다.

  • @dominiwe는 쉘 스크립트를 동시에 유효한 YAML 파일로 만들기 위해 콜론이 필요했습니다.

  • @__del__는 콜론이 초기 Unix 쉘에서 몇 안 되는 특수 처리된 코드 경로 중 하나였는지 궁금해했습니다.

  • @greatgib은 이 구조를 Claude가 생성한 코드에서 처음 보고 나중에 이 기사에서 보았다고 말했습니다.

  • @search_facility는 교차 플랫폼 안정성을 위해 bash에서 일반 Python 스크립트로 전환하며 "bash zoo"를 포기했습니다.

Sources