Skip to content
isdnetworks
Go back

실행 위치를 전제한 스크립트

로그를 정리하는 스크립트를 평소와 다른 디렉터리에서 돌렸다. 그랬더니 정리 대상이 아닌 파일이 지워졌고 스크립트는 아무 오류 없이 정상 종료했다.

Table of contents

Open Table of contents

원인 — 상대 경로와 실행 위치

스크립트를 열어 보니 대상 디렉터리를 상대 경로로 적고 있었다.

find ./logs -name '*.log' -mtime +30 -delete

./logs 는 스크립트가 놓인 자리가 아니라 명령을 친 시점의 pwd 를 기준으로 풀린다.

/app 에서 돌리면 /app/logs 를 지우고 홈 디렉터리에서 돌리면 거기 있는 logs 를 지운다. 그 자리에 마침 logs 가 있어서 find 가 아무 오류 없이 지우고 끝난 것이었다.

스크립트 위치 기준의 절대 경로

먼저 dirname "$0" 으로 스크립트가 자기 위치를 알아내고 그것을 기준으로 절대 경로를 만들게 고쳤다.

SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
APP_ROOT=${APP_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}

find "$APP_ROOT/logs" -name '*.log' -mtime +30 -delete

$0 에서 dirname 으로 스크립트가 놓인 자리를 얻고 cdpwd 로 절대 경로를 확정하므로 어디서 돌려도 $APP_ROOT/logs 한 곳을 가리킨다.

APP_ROOT 를 환경 변수로도 받게 둔 것은 시험 장비에서 다른 자리를 가리키게 하기 위해서였고 안 주면 SCRIPT_DIR 의 부모가 된다. 기준을 바꾼 뒤에는 확정된 $APP_ROOT 를 실행 전에 화면에 찍게 했다.

경로가 맞아도 자리는 확인한다

$APP_ROOT 가 맞게 만들어졌어도 그 디렉터리가 정말 우리 것인지는 별개 문제였다. 다른 프로젝트에도 logs 는 있기 때문이다.

그래서 그 디렉터리에 .app-root 표시 파일을 두고 그것이 있을 때만 진행하게 했다.

[ -f "$APP_ROOT/.app-root" ] || {
    echo "여기는 애플리케이션 루트가 아닙니다: $APP_ROOT"
    exit 1
}

.app-root 가 없으면 exit 1 로 멈춘다. 디렉터리 이름만 보고 지우는 것보다 표시 파일 하나를 test -f 로 확인하는 쪽이 안전했다.

삭제 전 출력과 건수 상한

지우기 전에 무엇을 지울지 목록으로 출력하고 DRY_RUN=1 이면 실제로는 안 지우게 했다.

targets=$(find "$APP_ROOT/logs" -name '*.log' -mtime +30)
count=$(echo "$targets" | grep -c . || true)

echo "$count 개를 지웁니다. ($APP_ROOT/logs)"
echo "$targets" | head -5

if [ "${DRY_RUN:-0}" = "1" ]; then exit 0; fi
echo "$targets" | xargs -r rm -f

find 결과를 targets 에 담아 두고 head -5 로 앞부분을 보인 뒤 DRY_RUN 이 1이면 xargs rm 까지 안 가고 exit 0 한다. 처음 돌릴 때와 조건을 바꾼 뒤에는 DRY_RUN=1 로 먼저 본다.

여기에 건수 상한을 하나 뒀는데 평소보다 훨씬 많이 잡히면 조건이 잘못된 것으로 보고 멈춘다.

if [ "$count" -gt 10000 ]; then
    echo "지울 것이 $count 개입니다. 예상보다 많아 멈춥니다."
    exit 1
fi

count 가 상한을 넘으면 exit 1 이다. 실제로 그 뒤에 한 번 걸렸고 확인해 보니 -mtime 값에 오타가 있어 find 가 전체를 잡은 것이었다.

전제의 문서화와 실행 기록

같은 모양의 스크립트가 더 있는지 grep 으로 찾아봤다.

$ grep -rn "^\s*\(find\|rm\|cp\|mv\)\s\+\./" scripts/
scripts/clean-cache.sh:8:   rm -rf ./cache/*
scripts/backup.sh:14:       cp -r ./config /backup/

find · rm · cp · mv 뒤에 ./ 가 오는 줄을 찾는 식이고 넷이 더 나왔다. 그중 rm -rf ./cache/* 가 가장 위험해서 먼저 고쳤다. 하나만 고치면 다음에 다른 것에서 같은 일이 난다.

마지막으로 각 스크립트 첫머리에 어디서 돌리는 것을 전제하는지를 적고 실행할 때마다 한 줄을 기록에 남겼다.

echo "$(date -Is) $(whoami)@$(hostname) pwd=$(pwd) root=$APP_ROOT count=$count" >> "$LOG"

date -Iswhoamihostnamepwd 를 한 줄에 남기므로 언제 누가 어느 자리에서 돌렸는지가 드러난다. 전제를 안 적으면 다음 사람이 다른 자리에서 돌리고 이 줄이 없으면 무엇이 지워졌는지 알 수 없다.

정리


Share this post on:

Previous Post
DB 수정이 닿지 않는 경계
Next Post
다른 컬럼에 있던 금액