명령 리터럴

명령 리터럴 (Command literal)

명령 리터럴은 백틱(`)으로 감싼 문자열 또는 %x 퍼센트 리터럴이에요. 런타임에 이 문자열을 서브셸에서 실행해 캡처한 출력으로 치환돼요.

일반 문자열과 같은 이스케이프·보간 규칙이 적용돼요.

퍼센트 문자열 리터럴과 비슷하게, %x의 유효한 구분자는 괄호 (), 대괄호 [], 중괄호 {}, 꺾쇠 <>, 파이프 ||예요. 파이프를 제외한 모든 구분자는 중첩될 수 있어요. 문자열 안의 시작 구분자는 다음 끝 구분자를 이스케이프한다는 뜻이에요.

특수 변수 $?는 명령의 종료 상태를 Process::Status로 담아요. 명령 리터럴과 같은 스코프에서만 사용할 수 있어요.

`echo foo`  # => "foo"
$?.success? # => true

내부적으로 컴파일러는 명령 리터럴을 명령을 인자로 담은 문자열 리터럴과 함께 최상위 메서드 `() 호출로 다시 써요. `echo #{argument}`%x(echo #{argument}) `("echo #{argument}")로 다시 써져요.

보안 우려 (Security concerns)

명령 리터럴은 단순한 스크립트성 도구에는 유용할 수 있지만, 사용자 입력을 보간할 때는 특히 주의가 필요해요. 명령 삽입(command injection)으로 쉽게 이어질 수 있거든요.

user_input = "hello; rm -rf *"
`echo #{user_input}`

이 명령은 hello를 출력한 다음 현재 작업 디렉토리의 모든 파일과 폴더를 삭제해요.

이를 피하려면 명령 리터럴은 일반적으로 보간된 사용자 입력과 함께 쓰지 않는 게 좋아요. 표준 라이브러리의 Process는 사용자 입력을 명령 인자로 안전하게 전달하는 방법을 제공해요:

user_input = "hello; rm -rf *"
process = Process.new("echo", [user_input], output: Process::Redirect::Pipe)
process.output.gets_to_end # => "hello; rm -rf *"
process.wait.success?      # => true

출처: Crystal 공식 문서

더 알아보기 (Learn more)

  • 명령 실행을 안전하게 다루는 방법은 표준 라이브러리의 Process 문서를 참고하세요.