Skip to content
isdnetworks
Go back

범위를 넓히는 조용한 실패

작업 폴더 루트에 흩어진 산출물을 하위 폴더로 옮기는 스크립트를 실행했더니 이동 명령이 저장소 루트 자체를 옮기려 했다. 목적지가 자기 하위 경로여서 오류로 무산됐는데 목적지가 달랐으면 저장소가 통째로 옮겨졌을 것이다.

Table of contents

Open Table of contents

빈 배열이 만든 인자

스크립트는 이런 모양이었다.

mapfile -t files < <(ls "$R")
for f in "${files[@]}"; do
  mv "$R/$f" "$R/artifacts/"
done

mapfile은 bash 전용 내장인데 이 도구는 zsh로 실행되므로 명령을 못 찾는 오류가 나고 배열은 빈 채로 남았다. 그런데 zsh에서 빈 배열을 이 형태로 순회하면 루프가 0회 도는 것이 아니라 빈 문자열 한 번을 돈다. 그 상태에서 변수가 빈 문자열이면 이동 명령의 인자가 저장소 루트가 된다.

침묵의 구조

명령을 못 찾는다는 오류 자체는 났지만 그 뒤가 조용했다. 스크립트가 거기서 멈추지 않았고 배열이 비었다는 사실을 어디에서도 알려 주지 않았다.

실패가 오류로 드러나지 않고 다음 단계로 그냥 흘러간 것이 이 함정의 핵심이다. 오류가 났다는 사실과 그 오류가 뒤를 막았다는 사실은 별개이고, 막지 않으면 앞 단계의 실패가 뒤 단계의 입력이 된다.

같은 형태의 다른 사례

변수 이름이 겹친 경우가 있었는데 저장소 21개를 대조하는 스크립트에서 경로를 담은 변수를 루프 카운터가 덮어 첫 항목만 정상이고 나머지 20개가 전부 실패했다. 그것을 보고 디스크가 빠졌다고 판단해 하드웨어 장애를 보고했는데 디스크는 멀쩡했다.

여기서 진단 신호를 하나 얻었다. 첫 항목만 성공하고 나머지가 똑같이 실패하면 환경이 아니라 스크립트를 먼저 의심한다. 환경 장애면 첫 항목도 같이 실패했어야 한다.

엄격 모드가 스크립트를 죽인 경우도 있었다. 없는 컨테이너를 조회했더니 종료 코드가 0이 아니라서 스크립트 전체가 그 자리에서 끝났고 뒷단계가 실행되지 않았는데 출력만 봐서는 조용히 끝난 것처럼 보였다.

파괴적 명령의 가드

이동과 삭제와 복사에 변수를 넣을 때 빈 값 검사는 선택이 아니다.

[ -n "$f" ] || { echo "빈 값 - 중단"; exit 1; }
mv "$R/$f" "$R/artifacts/"

한 줄짜리 검사지만 이것이 없으면 앞에서 본 사고가 그대로 재현된다. 여기에 더해 대상을 명시 목록으로 만든다. 조회 결과를 그대로 흘려보내지 않고 목록을 파일에 적고 존재를 확인한 뒤 건별로 처리하면 목록이 비었을 때 그 자리에서 드러난다.

실행 셸 확인

이 스크립트를 돌리는 도구가 zsh로 동작한다는 것을 몰랐던 것이 시작이었다. mapfilereadarray와 연관 배열 선언 같은 것은 bash 전용이라 zsh에서는 없거나 다르게 동작한다. 쓰기 전에 실행 셸을 확인하거나 두 셸에서 모두 되는 문법만 쓴다.

정리


Share this post on:

Previous Post
Docker host 모드에서 VPN 트래픽이 전달되지 않는 문제 해결
Next Post
통과했지만 아무것도 검증 못 한 테스트