리눅스 mktemp 명령어 사용법 - 안전한 임시 파일·디렉터리 생성과 자동 정리
이 명령어를 언제 쓰는가
스크립트가 중간 결과를 파일로 떨궈야 할 때, /tmp/work.txt 같은 고정 이름을 쓰면 안 되기 때문에 쓴다. 같은 스크립트가 두 개 동시에 돌면 서로의 파일을 덮어쓰고, 더 나쁘게는 다른 사용자가 그 이름으로 미리 심볼릭 링크를 걸어 두면 스크립트가 남의 파일(때로는 /etc 아래)에 쓰게 된다. 오래된 취약점 보고서에 단골로 나오는 유형이다.
mktemp는 이름을 무작위로 만들고 만드는 동작 자체를 원자적으로 수행한 뒤 그 경로를 출력한다. 파일은 0600, 디렉터리는 0700 권한으로 생성되므로 다른 사용자가 들여다볼 수도 없다.
기본 형식
mktemp [옵션] [템플릿]
템플릿은 끝에 X가 세 개 이상 연속으로 있어야 하며, 그 자리가 무작위 문자로 채워진다. 템플릿을 생략하면 $TMPDIR(없으면 /tmp) 아래에 tmp.XXXXXXXXXX 형태로 만든다. 출력된 경로를 변수에 받아 쓰는 것이 사용법의 전부다.
자주 쓰는 옵션
| 옵션 | 의미 | 예시 |
|---|---|---|
| -d | 파일이 아니라 디렉터리를 만든다(권한 0700) | mktemp -d |
| -t | 템플릿을 임시 디렉터리 기준으로 해석. GNU에서는 deprecated이며 -p를 권한다 | mktemp -t build.XXXXXX |
| -p 디렉터리 | 생성 위치를 명시. TMPDIR보다 우선 | mktemp -p /data/tmp -d |
| -u | 이름만 만들고 실제로 만들지는 않는다(권장하지 않음) | mktemp -u |
| -q | 실패해도 메시지를 내지 않는다. 종료 코드로 판단 | mktemp -q |
| --suffix=.json | 확장자를 붙인다(GNU 전용) | mktemp --suffix=.json |
실전 예제
1) 임시 파일을 만들고 스크립트가 어떻게 끝나든 지운다. 이 세 줄이 mktemp 사용의 표준형이다. trap이 없으면 /tmp에 쓰레기가 쌓인다.
#!/bin/bash
set -euo pipefail
TMP="$(mktemp)"
trap 'rm -f "$TMP"' EXIT
curl -fsS https://example.com/api/list > "$TMP"
wc -l < "$TMP"
2) 작업 디렉터리째로 만들고 통째로 정리한다. 파일이 여러 개 나올 때는 디렉터리가 관리하기 쉽다.
WORK="$(mktemp -d "${TMPDIR:-/tmp}/deploy.XXXXXX")"
trap 'rm -rf "$WORK"' EXIT
tar -xzf /backup/release.tar.gz -C "$WORK"
3) 큰 파일을 다루므로 위치를 데이터 볼륨으로 지정한다. /tmp가 tmpfs(메모리)인 서버에서 수십 GB를 쓰면 OOM이 난다.
WORK="$(mktemp -d -p /data/tmp restore.XXXXXX)"
4) 원자적 교체를 위해 같은 파일시스템에 임시 파일을 만든다. 설정 파일을 반쯤 쓴 상태로 서비스가 읽는 사고를 막는다.
TARGET=/etc/app/routes.json
TMP="$(mktemp "$(dirname "$TARGET")/.routes.XXXXXX")"
trap 'rm -f "$TMP"' EXIT
generate_routes > "$TMP"
chmod 644 "$TMP"
mv -f "$TMP" "$TARGET"
5) macOS·BSD에서의 차이를 확인한다. BSD의 -t는 템플릿이 아니라 접두사를 받는다.
mktemp -t build
/var/folders/sn/4mg1qk012d54m49jmnfj4djc0000gn/T/build.9lCkq0uzNr
함정과 주의점
trap을 EXIT에만 걸어도 대부분 충분하지만, 만드는 순서를 틀리면 소용없다. TMP=$(mktemp)보다 trap을 먼저 걸면 $TMP가 비어 있는 상태로 rm -rf ""가 실행될 수 있다. 반대로 mktemp 직후에 걸지 않으면 그 사이에 스크립트가 죽었을 때 파일이 남는다. 생성 바로 다음 줄에 trap이 정답이다. 또한 rm -rf "$WORK"처럼 변수 하나만 지우게 하고, 뒤에 하위 경로를 덧붙이지 마라.
-u는 쓰지 마라. 이름만 받아 두고 나중에 리다이렉션으로 만드는 방식은 mktemp를 쓰는 이유였던 경쟁 조건을 그대로 되살린다. 이름과 생성 사이의 틈이 곧 취약점이다. 파일 경로가 꼭 필요하면 만들어 놓고 내용을 덮어쓰면 된다.
systemd 서비스 안에서는 /tmp가 서비스마다 다를 수 있다. 유닛에 PrivateTmp=yes가 걸려 있으면 그 서비스의 /tmp는 다른 프로세스에서 보이지 않는 별도 네임스페이스다. "스크립트가 만든 임시 파일이 안 보인다"는 문의는 대개 이것이다. 서비스 간에 파일을 주고받아야 하면 -p로 명시적인 공유 디렉터리를 지정하라.
/tmp가 noexec으로 마운트된 서버가 있다. 임시 디렉터리에 스크립트를 풀어 실행하려는 설치 스크립트가 여기서 Permission denied로 죽는다. 이때는 TMPDIR을 실행 가능한 다른 경로로 바꿔 주면 된다.
X 뒤에 확장자를 붙이면 플랫폼마다 결과가 갈린다. BSD 쪽이 특히 위험하다. mktemp /tmp/XXXX.log를 치면 GNU는 --suffix=.log가 걸린 것으로 보고 /tmp/FBhV.log처럼 정상 동작하지만, macOS·BSD의 mktemp는 끝에 붙은 X만 치환하므로 오류도 내지 않고 /tmp/XXXX.log라는 이름 그대로 파일을 만든다. 무작위성이 사라져 mktemp를 쓰는 의미 자체가 없어지는데, 실패가 아니라 성공(종료 코드 0)으로 지나가기 때문에 알아채기 어렵다. 이식성이 필요하면 X를 반드시 맨 끝에 두고, 확장자가 필요하면 mktemp -d로 디렉터리를 만들어 그 안에 원하는 이름으로 파일을 두어라.
함께 보면 좋은 명령어
- mv — 같은 파일시스템 안에서의
mv는 원자적이라 임시 파일 교체 패턴의 핵심이다. - dirname — 대상과 같은 디렉터리에 임시 파일을 만들 때 경로 계산에 쓴다.
- find —
/tmp에 쌓인 오래된 임시 파일을-mtime으로 걸러 정리할 때 쓴다.