docker exec 에 컨테이너 안의 /var/www/html 을 인자로 줬는데 엉뚱한 경로로 들어가는 바람에 명령이 실패했다. 오류 메시지에는 내가 준 적도 없는 C:/Program Files/Git/var/www/html 이 그대로 찍혀 있었다.
Table of contents
Open Table of contents
원인 — 인자가 바뀌어 들어간 경로
Git Bash 의 MSYS2 런타임이 슬래시로 시작하는 인자를 Windows 경로로 번역해 주는 기능을 갖고 있었다. MSYS2 공식 문서는 이 변환을 MSYS2_ARG_CONV_EXCL 로 접두 단위로 제외하는 방법을 적고 런타임 소스 msys2_path_conv.cc 는 MSYS_NO_PATHCONV 가 설정돼 있으면 변환을 통째로 건너뛴다. 컨테이너 안에서만 뜻이 있는 경로인데 호스트 경로로 바꿔서 넘긴 것이다.
두 가지 방법이 있었는데 MSYS_NO_PATHCONV=1 을 명령 앞에 붙이는 것과 //var/www/html 처럼 슬래시를 두 개 붙이는 것이다. 앞쪽을 골랐다. 이유는 뒤쪽은 왜 두 개인지가 코드에 안 드러나서다. 대신 그 변수를 모르는 다른 셸에서는 아무 뜻 없는 접두가 남는 것을 감수했다.
상황 — 한 환경에서만 나는 문제
같은 명령을 네 개 환경에서 돌려 보니 Git Bash 한 곳에서만 이 현상이 났다. macOS 의 zsh 와 Linux 의 bash 와 PowerShell 에서는 인자가 아무 변환 없이 그대로 전달되고 있었다.
한 환경에서만 나는 문제는 다른 사람이 재현하지 못하므로 내 자리에서는 된다는 말이 그대로 성립한다. 그래서 어느 환경에서 나는 문제인지를 먼저 확정해야 대화가 됐다.
해결 — 스크립트에서 흡수하기
명령을 쓰는 사람이 이 사정을 몰라도 되게 하려고 run.sh 에서 uname -o 가 Msys 일 때만 export MSYS_NO_PATHCONV=1 을 하게 했다. 변환이 없는 다른 환경에서는 그 분기가 아무 일도 하지 않는다.
이런 차이는 사용자에게 알리는 것보다 흡수하는 편이 낫다. 알려 줘도 매번 기억해야 하고 한 번 잊는 순간 같은 일이 다시 난다.
주의 — 호스트 경로와 컨테이너 경로
docker run -v 에서는 한 인자에 호스트 경로와 컨테이너 경로가 : 로 함께 들어간다. 앞의 것은 변환돼야 맞고 뒤의 것은 변환되면 안 되는 상황이었다.
그래서 HOST_DIR 을 pwd -W 로 미리 만들고 CTR_DIR 은 그대로 둔 뒤 -v "$HOST_DIR:$CTR_DIR" 로 조합했다. 한 문자열 안에서 서로 다른 규칙이 필요하면 그것을 분리하는 것이 유일한 방법이었다.
넘기는 것을 줄이기
근본적으로는 셸을 거쳐 넘기는 값 자체를 줄이는 쪽이 나았다. 실행할 내용을 .sh 파일에 넣고 그 경로만 넘기면 해석될 문자가 거의 남지 않는다.
같은 이유로 원격 실행에서는 셸이 자동으로 펼치거나 치환하는 것들도 함께 봐야 했다. 어느 계층에서 무엇이 해석되는지 헷갈릴 때는 printf '%s\n' "$@" 로 실제로 도착한 문자열을 먼저 출력해 보고 나서 진짜 명령을 넘겼다.
정리
- Git Bash 처럼 셸이 경로 인자를 자동으로 바꾸는 환경이 있다
- 변환을 끄는 방법을 알아 두고 이유가 드러나는 쪽을 쓴다
- 한 환경에서만 나면 다른 사람은 재현하지 못한다
- 어느 환경에서 나는지를 먼저 확정한다
- 차이는 알리는 것보다 스크립트에서 흡수하는 편이 낫다
- 한 문자열에 다른 규칙이 필요하면 변수로 분리한다
- 셸을 거쳐 넘기는 값을 줄이면 깨질 자리가 준다
- 어느 계층에서 해석되는지는 도착한 문자열을 찍어 확인한다