정리가 끝났는지 확인하는 스크립트를 짜면서 특정 접두어로 시작하는 폴더가 남아 있는지를 검사했다. 남아 있으면 잔존으로 없으면 통과로 찍히게 했는데 폴더가 하나도 없는데도 항상 잔존이 나왔다.
Table of contents
Open Table of contents
원인 — 검색 명령의 종료 코드 규칙
원인은 find가 매치가 0건이어도 0을 반환한다는 데 있었다. find(1) 매뉴얼도 모든 파일을 오류 없이 처리했으면 0이라고 적고 있다. 파일을 못 찾은 것은 검색의 실패가 아니라는 관점이다.
그래서 성공 분기가 항상 실행되고 실패 분기는 절대 실행되지 않았다. 없는 이름으로 find를 돌리고 echo $?를 찍어 보면 출력은 없는데 코드가 0으로 나오므로 5초면 확인된다.
검증 — 명령마다 다른 규칙
이것을 알고 나서 자주 쓰는 것들을 확인했더니 규칙이 제각각이었다. grep은 매뉴얼대로 선택된 줄이 없으면 1을 반환해서 같은 형태의 분기가 의도대로 동작한다.
diff는 반대로 차이가 있을 때 1을 반환하는데 같으면 성공이라는 관점이라 직관과는 맞지만 다른 명령과 섞어 쓰면 헷갈린다. 같은 형태의 분기를 썼는데 명령만 바뀌어도 판정이 뒤집히므로 종료 코드를 쓰기 전에 그 명령의 규칙을 확인해야 한다.
조치 — 출력 유무로 하는 판정
두 가지 방법이 있었는데 find에 grep -q를 붙여 종료 코드를 만들어 내는 것과 출력을 변수에 담아 -z로 판정하는 것이다. 뒤쪽을 골랐다. 이유는 종료 코드가 아니라 실제 출력을 보면 명령을 바꿔도 판정이 흔들리지 않아서다. 대신 출력이 큰 명령에서는 변수에 전부 담는 비용을 감수했다.
found=$(find "$DIR" -type d -name "_*" 2>/dev/null)
if [ -z "$found" ]; then echo "✓ 0개"; else echo "$found"; fi
남아 있으면 무엇이 남았는지도 함께 찍게 했다. 개수만 세면 무엇이 남았는지 모르고 0건이라는 출력만 보면 안 찾은 것인지 없는 것인지도 모른다.
주의 — 같은 유형의 다른 사례
같은 성격을 몇 개 더 만났다. 조건이 정확히 일치할 때만 지우는 삭제 API가 조건이 안 맞아도 오류 대신 202를 돌려줘서 지운 줄 알고 넘어간 적이 있었다.
str_replace가 대상을 못 찾아도 예외 없이 원본을 그대로 돌려주는 것도 같은 유형이고 그 결과 부분만 반영된 파일이 저장됐다. 컨테이너에 필요한 클라이언트가 없어서 아무것도 안 한 적도 있다. 빈 결과를 wc -l로 세니 1로 잡혀 대상 한 건으로 진행됐다. 전부 실패가 아니라는 것과 성공했다는 것을 같게 취급한 경우였다.
판단 기준 — 대조군을 만드는 확인
그래서 확인 방식에 한 단계를 더 뒀다. 결과가 예상과 같게 나오면 반대 입력으로 한 번 더 돌린다.
통과가 나왔으면 실패해야 하는 입력을 넣어 본다. 그때도 통과가 나오면 그 검사는 아무것도 가르지 못한 것이다. 명령이 성공했다는 것과 의도한 상태가 됐다는 것은 다르므로 마지막에는 다시 조회해서 결과를 확인한다.
정리
find는 못 찾아도 성공을 반환할 수 있다- 그러면 성공과 실패 분기가 항상 한쪽만 탄다
grep과diff는 규칙이 서로 다르다$?가 아니라 출력 유무로 판정한다- 무엇이 남았는지를 함께 찍는다
- 아무것도 안 지우고 성공하는 API도 같은 유형이다
- 실패가 아니라는 것과 성공했다는 것은 다르다
- 반대 입력으로 한 번 더 돌려 검사가 실제로 가르는지 확인한다