예기치 않은 bash 종료에서 생성 된 임시 파일 제거


89

bash 스크립트에서 임시 파일을 만들고 있습니다. 처리가 끝날 때 삭제하지만 스크립트가 꽤 오랫동안 실행되고 있기 때문에 실행 중에 종료하거나 CTRL-C를 실행하면 임시 파일이 삭제되지 않습니다.
실행이 끝나기 전에 이러한 이벤트를 포착하고 파일을 정리할 수있는 방법이 있습니까?

또한 해당 임시 파일의 이름과 위치에 대한 모범 사례가 있습니까?
현재 다음을 사용하는지 잘 모르겠습니다.

TMP1=`mktemp -p /tmp`
TMP2=`mktemp -p /tmp`
...

TMP1=/tmp/`basename $0`1.$$
TMP2=/tmp/`basename $0`2.$$
...

아니면 더 나은 해결책이 있습니까?


답변:


97

종료 할 때 실행하도록 " trap "을 설정 하거나 정리하기 위해 control-c에서 실행할 수 있습니다.

trap "{ rm -f $LOCKFILE; }" EXIT

또는 내가 가장 좋아하는 유닉스 리즘 중 하나는 파일을 연 다음 열려있는 동안 삭제하는 것입니다. 파일은 파일 시스템에 남아 있으며 읽고 쓸 수 있지만 프로그램이 종료되면 파일이 사라집니다. 그래도 bash에서 어떻게할지 모르겠습니다.

BTW : 자신의 솔루션을 사용하는 대신 mktemp를 선호하는 한 가지 주장 : 사용자가 프로그램이 거대한 임시 파일을 생성 할 것으로 예상하면 TMPDIR/ var / tmp와 같이 더 큰 어딘가로 설정하고 싶을 수 있습니다 . mktemp는 손으로 굴린 솔루션 (두 번째 옵션)은 인식하지 못합니다. TMPDIR=/var/tmp gvim -d foo bar예를 들어 나는 자주 사용 합니다.


8
강타와 함께, exec 5<>$TMPFILE넥타이 파일 기술자 (5)는 읽기 - 쓰기로 TMPFILE을 $, 그리고 당신이 사용할 수있는 <&5, >&5그리고 /proc/$$/fd/5(리눅스) 이후. 유일한 문제는 Bash가 seek기능 이 없다는 것입니다 ...
ephemient

제공 한 링크가 내가 필요한 것을 가장 잘 설명하기 때문에 답변을 수락했습니다. 감사합니다
skinp

4
몇 가지 참고 사항 trap: 트랩 할 방법이 없습니다 SIGKILL(설계 상 실행을 즉시 종료하므로). 따라서 이러한 상황이 발생할 수있는 경우 대체 계획 (예 :)을 마련하십시오 tmpreaper. 둘째, 트랩은 누적되지 않습니다. 수행 할 작업이 둘 이상이면 모두 trap명령 에 있어야합니다 . 여러 정리 작업에 대처하는 한 가지 방법은 함수를 정의하고 (필요한 경우 프로그램이 진행될 때이를 재정의 할 수 있음) 다음을 참조하는 것 trap cleanup_function EXIT입니다.
토비 Speight

1
사용하지 trap "rm -f $LOCKFILE" EXIT않으면 예기치 않은 파일 오류가 발생합니다.
Jaakko 2016

3
Shellcheck는 작은 따옴표를 사용하라는 경고를주었습니다. 표현식은 나중에 트랩이 호출 될 때가 아니라 큰 따옴표로 '지금'확장 될 것입니다.
LaFayette

110

일반적으로 모든 임시 파일을 저장할 디렉터리를 만든 다음 스크립트가 종료 될 때이 디렉터리를 정리하는 EXIT 처리기를 만듭니다.

MYTMPDIR=$(mktemp -d)
trap "rm -rf $MYTMPDIR" EXIT

모든 임시 파일을 아래에두면 $MYTMPDIR대부분의 상황에서 스크립트가 종료 될 때 모두 삭제됩니다. SIGKILL (kill -9)로 프로세스를 종료하면 프로세스가 즉시 종료되므로이 경우 EXIT 핸들러가 실행되지 않습니다.


27
+1 어리석은 TERM / INT / HUP / 당신이 생각할 수있는 모든 것이 아니라 EXIT에 트랩을 확실히 사용하십시오. 비록, 기억 인용 하여 매개 변수 확장을 나는 것 또한 당신에게 추천 견적을 당신의 함정 : 함정 'RF RM은 "$ TMPDIR"'EXIT
lhunath

7
작은 따옴표. 나중에 스크립트에서 상황으로 인해 TMPDIR을 정리하고 변경하기로 결정하면 트랩이 계속 작동하기 때문입니다.
lhunath

1
@AaronDigulla $ () 대 백틱이 중요한 이유는 무엇입니까?
오우거 시편


3
@AlexanderTorstling 코드는 삽입으로 인한 임의 코드 실행을 방지하기 위해 항상 작은 따옴표로 묶어야합니다. 데이터를 bash 코드 STRING으로 확장하면 해당 데이터는 이제 코드가 수행하는 모든 작업을 수행하여 공백을 포함하여 무고한 버그가 발생하지만 기괴한 이유로 homedir을 지우거나 보안 허점을 도입하는 것과 같은 파괴적인 버그를 유발할 수 있습니다. 트랩은 나중에있는 그대로 평가 될 bash 코드 문자열을 사용합니다. 따라서 나중에 트랩이 실행되면 작은 따옴표가 사라지고 구문 상 큰 따옴표 만 남게됩니다.
lhunath

25

trap 명령 을 사용하여 스크립트 종료 또는 CTRL-C와 같은 신호를 처리 하려고합니다 . 자세한 내용은 Greg의 Wiki 를 참조하십시오.

basename $0임시 파일의 경우 충분한 임시 파일을위한 공간을 제공하는 템플릿을 제공 할뿐만 아니라 사용 하는 것이 좋습니다.

tempfile() {
    tempprefix=$(basename "$0")
    mktemp /tmp/${tempprefix}.XXXXXX
}

TMP1=$(tempfile)
TMP2=$(tempfile)

trap 'rm -f $TMP1 $TMP2' EXIT

1
TERM / INT에 트랩하지 마십시오. EXIT에 트랩. 수신 된 신호를 기반으로 종료 조건을 예측하려는 시도는 어리 석고 확실히 캐치 올이 아닙니다.
lhunath

3
사소한 점 : 단일 백틱 대신 $ ()를 사용하십시오. 공백을 포함 할 수 있으므로 $ 0 주위에 큰 따옴표를 넣으십시오.
Aaron Digulla

글쎄, 백틱은이 주석에서 잘 작동하지만 그것은 공정한 요점이며 사용하는 습관을 갖는 것이 좋습니다 $(). 큰 따옴표도 추가되었습니다.
Brian Campbell

1
당신은 (임시 파일 -s "XXXXXX") 단지 TMP1 = $로 전체 서브 루틴을 대체 할 수
루슬란 Kabalin

4
@RuslanKabalin 모든 시스템에 tempfile명령 이있는 것은 아니지만 내가 아는 모든 합리적인 최신 시스템에는 mktemp명령이 있습니다.
Brian Campbell

9

선택한 답변은 bashism이며 이는 다음과 같은 솔루션을 의미합니다.

trap "{ rm -f $LOCKFILE }" EXIT

bash에서만 작동합니다 (shell이 dash또는 classic 인 경우 Ctrl + c를 잡지 못함 sh). 호환성을 원한다면 트랩하려는 모든 신호를 열거해야합니다.

또한 스크립트가 신호 "0"(일명 EXIT)에 대한 트랩을 종료 할 때 항상 수행되어 trap명령이 두 번 실행된다는 점에 유의하십시오 .

EXIT 신호가 있으면 모든 신호를 한 줄에 쌓지 않는 이유입니다.

더 잘 이해하려면 변경없이 다른 시스템에서 작동하는 다음 스크립트를 살펴보십시오.

#!/bin/sh

on_exit() {
  echo 'Cleaning up...(remove tmp files, etc)'
}

on_preExit() {
  echo
  echo 'Exiting...' # Runs just before actual exit,
                    # shell will execute EXIT(0) after finishing this function
                    # that we hook also in on_exit function
  exit 2
}


trap on_exit EXIT                           # EXIT = 0
trap on_preExit HUP INT QUIT TERM STOP PWR  # 1 2 3 15 30


sleep 3 # some actual code...

exit 

이 솔루션은 최종 종료 ( preExit함수) 직전에 실제 신호 발생시 일부 코드를 실행할 수 있고 필요한 경우 실제 EXIT 신호 (종료 최종 단계)에서 일부 코드를 실행할 수 있으므로 더 많은 제어를 제공합니다.


4

예측 가능한 파일 이름을 $$와 함께 사용하는 대안은 틈이있는 보안 허점이며 절대로 사용에 대해 생각해서는 안됩니다. 단일 사용자 PC의 단순한 개인 스크립트 일지라도. 당신이 얻지 말아야 할 아주 나쁜 습관입니다. BugTraq 은 "안전하지 않은 임시 파일"사고로 가득 차 있습니다. 임시 파일의 보안 측면에 대한 자세한 내용 은 여기 , 여기여기 를 참조 하십시오 .

처음에는 안전하지 않은 TMP1 및 TMP2 할당을 인용 할 생각 이었지만 다시 생각 하면 좋은 생각 이 아닐 것입니다 .


내가 할 수 있다면 줄 것입니다 : 보안 조언에 +1하고 나쁜 아이디어와 참조를 인용하지 않음에 대해 또 다른 +1
TMG

1

tempfile안전한 방식으로 / tmp에 파일을 생성하는 것을 선호 하며 이름 지정에 대해 걱정할 필요가 없습니다.

tmp=$(tempfile -s "your_sufix")
trap "rm -f '$tmp'" exit

tempfile은 더 안전하지만 안타깝게도 매우 이식 할 수 없으므로 종종 피하거나 적어도 에뮬레이트하는 것이 좋습니다.
lericson 2013 년

1

나는 너무 많은 사람들이 파일 이름에 공백이 없을 것이라고 생각한다고 믿을 수 없습니다. $ TMPDIR이 "임시 디렉토리"에 할당되면 세계가 충돌합니다.

zTemp=$(mktemp --tmpdir "$(basename "$0")-XXX.ps")
trap "rm -f ${zTemp@Q}" EXIT

파일 이름의 작은 따옴표 및 캐리지 리턴과 같은 공백 및 기타 특수 문자는 적절한 프로그래밍 습관의 요구 사항으로 코드에서 고려해야합니다.


그것은 주목해야한다 ${parameter@operator}확장 배쉬 4.4에 추가되었다 (9 월 2016 발표).
Robin A. Meade

Bash <v4.4에 해당 :trap "rm -f $(printf %q "$zTemp")" EXIT
Robin A. Meade

zTemp나중에 스크립트에서 변경되는 값에 대해 걱정할 필요가 없기 때문에 이것이 최선의 대답 입니다. 또한 함수에 zTemp선언 할 수 있습니다 local. 전역 스크립트 변수 일 필요는 없습니다.
Robin A. Meade

-4

mktemp로 생성 된 tmp 파일을 제거하지 않아도됩니다. 나중에 삭제됩니다.

가능한 경우 mktemp를 사용하면 '$$'접두사보다 더 고유 한 파일을 생성 할 수 있습니다. 그리고 임시 파일을 만든 다음 명시 적으로 / tmp에 넣는 크로스 플랫폼 방법처럼 보입니다.


4
누구 또는 무엇에 의해 삭제 되었습니까?
innaM

일정 시간이 지난 후 운영 | 파일 시스템 자체에 의해 삭제됨
Mykola Golubyev

4
마법? 크론 잡? 아니면 재부팅 된 Solaris 시스템입니까?
innaM

아마 그들 중 하나 일 것입니다. 임시 파일이 중단에 의해 제거되지 않으면 (너무 자주 발생하지는 않을 것입니다) 언젠가 tmp 파일이 제거 될 것입니다. 이것이 바로 temp를 호출 한 이유입니다.
Mykola Golubyev

22
/ tmp에 넣은 무언가가 영원히 거기에 남아있을 것이라고 가정해서는 안되며 그렇게해서는 안됩니다. 동시에 마술처럼 사라질 것이라고 가정해서는 안됩니다.
innaM
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.