1P by GN⁺ | ★ favorite | 댓글 1개
  • 셸의 :는 아무 동작 없이 인수만 평가해 버리는 널 명령(null command) 으로, 매개변수 확장과 결합하면 필수 인수 검사와 기본값 설정을 간결하게 처리할 수 있음
  • : "${1:?missing argument, aborting!}"$1이 없거나 비어 있으면 진단 메시지를 stderr에 출력하고 0이 아닌 상태로 종료함
  • ${VAR:=default}:의 인수로 전달하면 확장은 실행하되 결과 문자열은 버리므로, 확장 결과를 명령으로 실행하지 않고 기본값만 설정할 수 있음
  • 리디렉션을 통한 파일 비우기와 접근 가능 여부 검사, trap의 빈 명령, set -u 환경의 변수 검사, 비어 있는 조건 분기에도 :를 활용할 수 있음
  • : "${VAR:=default}"는 변수명을 한 번만 써서 오타 가능성을 줄이고, while:에서는 항상 성공하는 조건이 되어 무한 루프를 표현함

널 명령과 매개변수 확장

  • :는 인수를 평가한 뒤 결과를 버리는 널 명령 내장 명령임
    • 1971년 Thompson shell까지 거슬러 올라가며, 당시에는 레이블이자 Unix 최초의 주석 표식으로도 쓰였음
  • 네 줄짜리 조건문 대신 다음 한 줄로 필수 인수를 검사할 수 있음
: "${1:?missing argument, aborting!}"
echo "Hello $1!"
  • 매개변수 확장 구문 ${name:?diagnostic}$name이 설정되지 않았거나 비어 있는지 검사함
    • 값이 없으면 diagnosticstderr에 출력하고 셸을 0이 아닌 상태로 종료함
    • 값이 있으면 $name과 같은 결과를 냄
  • 널 명령 없이 ${HELLO:=123}만 실행하면 확장 결과인 123을 명령으로 취급해 command not found가 발생함
    • : ${HELLO:=123}는 확장을 평가해 HELLO=123을 설정하면서 그 결과는 버림
  • : "${DATA_DIR:=/var/data}"DATA_DIR="${DATA_DIR:-/var/data}"와 달리 변수명을 한 번만 적으므로 DATA_DRI 같은 오타 가능성을 줄일 수 있음

:를 활용하는 방법

  • 기본값 설정

    • 확장 결과를 실행하지 않고 버리면서 변수의 기본값을 설정함
: "${DATA_DIR:=/var/data}"
: "${RETRIES:=3}"
  • 파일 비우기와 접근 검사

    • 리디렉션과 결합하면 하나 이상의 파일을 비울 수 있음
: > error.log
: > error.log > access.log
  • 서브셸과 리디렉션을 이용해 파일의 읽기·쓰기 가능 여부도 검사할 수 있음
( : < dataset.json ) && echo YES
( : >> result.json ) && echo YES
  • 빈 명령이 필요한 자리

    • trap처럼 문법상 명령이 필요하지만 실제 작업은 하지 않아야 할 때 사용할 수 있음
trap : INT
sleep 60
  • 조건문에서 비워 둘 분기에도 배치 가능함
if some-command; then
    :
else
    echo "command failed"
fi
  • 여러 변수 검사

    • set -u가 활성화된 상태에서 여러 변수가 설정됐는지 한 번에 검사함
set -u
: "$DEPLOY_ENV" "$HOST"
  • 무한 루프

    • :는 항상 성공하는 조건이므로 무한 루프를 간결하게 작성할 수 있음
while :; do
  echo "I love colons"
  sleep 1
done

댓글과 토론

Lobste.rs 의견들
  • 프롬프트를 다음처럼 구성하면 한 줄 전체를 복사해 붙여넣어도 프롬프트 부분은 무시되고 명령만 실행됨
    : hayalci@tassadar ~/src/dool ; jj rebase -s dxw -d mrn
    • Plan 9에서 착안해 셸의 autocd 옵션과 다음 형식도 사용함
      ~/directoryname; <command>
      셸의 명령 줄을 복사할 때마다 명령을 원래 실행했던 디렉터리로 계속 cd하게 됨
    • zsh에서는 보이지 않는 줄 바꿈 없는 공백을 붙여넣으면 왼쪽 내용을 지우도록 설정함
      nbsp=$'\u00A0'  
      PS1="...$nbsp"  
      bindkey -s $nbsp '^u'  
      
  • Makefile에서 보기 좋게 출력할 때 콜론을 자주 사용함
    out/%.o: src/%.c  
        @echo CC $@  
        @$(CC) -c -o $@ $<  
    
    이 방식은 make가 여러 echo 프로세스를 동시에 실행하면서 같은 스트림에 출력해 서로 간섭할 수 있음. 다음처럼 바꾸면 자식 프로세스가 아니라 make 프로세스 자체가 출력
    out/%.o: src/%.c  
        : CC $@  
        @$(CC) -c -o $@ $<  
    
  • 셸 스크립트에서 이 기법을 봤지만 man :이나 whatis :으로는 유용한 문서를 찾기 어려웠음
    true 명령과 비슷하지만 --help, --version 등에 특별히 반응하지 않고 글자 수도 더 적다는 차이가 있음
    • Bash 내장 명령은 man 대신 help로 찾아야 함
      $ help :  
      :: :  
          Null command.
      
          No effect; the command does nothing.
      
          Exit Status:  
          Always succeeds.  
      
    • Fish에서 :는 말 그대로 true의 별칭
  • 콜론 없이도 매개변수 확장은 일어나는데, 굳이 널 명령이 필요한 이유가 궁금함
    • 매개변수 확장은 그대로 일어나지만, 널 명령처럼 결과를 소비하는 구문이 없으면 셸이 확장된 문자열을 실행할 명령으로 취급함. 이 내용을 FAQ에도 추가함
      % ${HELLO:=123}  
      zsh: command not found: 123  
      
      % : ${HELLO:=123}  
      % echo $HELLO  
      123  
      
    • 보간된 문자열의 결과가 실행할 명령으로 해석됨
      #!/bin/sh  
      "${1:?missing argument, aborting!}"  
      
      $ sh foo.sh sh  
      sh-5.3$  
      
  • 명령을 입력하다가 나중에 다시 실행하고 싶을 때 줄 맨 앞에 콜론을 붙여 실행 기록에 저장함. 이후 기록에서 해당 줄을 찾아 첫 글자만 지우면 실제로 실행할 수 있음
    다만 인수를 평가하면서 생기는 부수 효과에 활용할 생각은 못 했음
    • : 대신 #을 붙여 주석으로 저장할 수도 있음. 나중에 실행할 내용이라면 그 안의 표현식을 미리 평가하지 않는다는 장점이 있음
    • zsh에서는 Meta-q 또는 Alt-q로 현재 줄을 잠시 치우고 다른 명령을 실행한 뒤, 끝나면 원래 줄을 명령줄 버퍼로 되돌릴 수 있음
      https://nathangrigg.com/2014/04/zsh-push-line-or-edit/
  • macOS에서 아직 Bash 3을 사용하고 있으며 다음 오류가 발생함
    -bash: !}": event not found
    • "!" 사용을 제거하면 동작하는지 궁금함
  • Con-colon-sion의 다음 코드는 while true; do...보다 사람을 혼란스럽게 만들 가능성이 큼
    while : colons are more than punctuation; do  
      echo "I love colons"  
      sleep 1  
    done  
    
    while true; dowhile :; do보다 세 글자 길 뿐이라 충분히 짧으며, 저 코드는 저자가 전달하려는 요점에 적합한 예제로 보이지 않음
    • 그대로 따르라는 예제가 아니라 말장난으로 넣은 코드임. 이 코드를 이해할 수 있다면 널 명령을 아는 것이고, 콜론은 실제로 문장부호 이상의 의미가 있음
    • 원래 보여주려던 것은 다음과 같은 무한 반복문이지만, 멋있게 표현하려다 요점을 놓친 듯함
      while :; do  
          something  
      done