Xóa tệp tạm thời đã tạo trong lần thoát bash không mong muốn


89

Tôi đang tạo các tệp tạm thời từ một tập lệnh bash. Tôi đang xóa chúng khi kết thúc quá trình xử lý, nhưng vì tập lệnh đang chạy trong một thời gian dài, nếu tôi giết nó hoặc đơn giản là CTRL-C trong quá trình chạy, các tệp tạm thời sẽ không bị xóa.
Có cách nào để tôi có thể nắm bắt các sự kiện đó và xóa các tệp trước khi quá trình thực thi kết thúc không?

Ngoài ra, có một số loại phương pháp hay nhất để đặt tên và vị trí của các tệp tạm thời đó không?
Tôi hiện không chắc chắn giữa việc sử dụng:

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

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

Hoặc có thể có một số giải pháp tốt hơn?


Câu trả lời:


97

Bạn có thể đặt " bẫy " để thực hiện khi thoát hoặc trên điều khiển-c để dọn dẹp.

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

Ngoài ra, một trong những unix-isms yêu thích của tôi là mở một tệp, sau đó xóa nó trong khi bạn vẫn mở nó. Tệp vẫn nằm trên hệ thống tệp và bạn có thể đọc và ghi nó, nhưng ngay sau khi chương trình của bạn thoát ra, tệp sẽ biến mất. Tuy nhiên, không chắc bạn sẽ làm điều đó như thế nào.

BTW: Một lập luận mà tôi sẽ ủng hộ mktemp thay vì sử dụng giải pháp của riêng bạn: nếu người dùng dự đoán chương trình của bạn sẽ tạo ra các tệp tạm thời khổng lồ, họ có thể muốn đặt TMPDIRthành một nơi nào đó lớn hơn, như / var / tmp. mktemp nhận ra rằng, giải pháp cuộn tay của bạn (tùy chọn thứ hai) thì không. Tôi thường xuyên sử dụng TMPDIR=/var/tmp gvim -d foo bar, chẳng hạn.


8
Với Bash, exec 5<>$TMPFILEquan hệ tập tin mô tả từ 5 đến $ tmpfile như đọc-ghi, và bạn có thể sử dụng <&5, >&5/proc/$$/fd/5(Linux) sau đó. Vấn đề duy nhất là Bash thiếu seekchức năng ...
ephemient

Đã chấp nhận câu trả lời của bạn vì liên kết bạn cung cấp là thứ giải thích tốt nhất những gì tôi cần. Cảm ơn
skinp

4
Một vài lưu ý về trap: không có cách nào để mắc bẫy SIGKILL(theo thiết kế, vì nó ngay lập tức chấm dứt thực thi). Vì vậy, nếu điều đó có thể xảy ra, hãy có một kế hoạch dự phòng (chẳng hạn như tmpreaper). Thứ hai, bẫy không phải là tích lũy - nếu bạn có nhiều hơn một hành động để thực hiện, tất cả chúng phải nằm trong traplệnh. Một cách để đối phó với nhiều hành động dọn dẹp là xác định một chức năng (và bạn có thể định nghĩa lại nó như là tiền thu được chương trình của bạn, nếu cần) và tài liệu tham khảo mà: trap cleanup_function EXIT.
Toby Speight

1
Tôi phải sử dụng trap "rm -f $LOCKFILE" EXIT, nếu không tôi sẽ gặp lỗi cuối tệp không mong muốn.
Jaakko

3
Shellcheck đã đưa ra cảnh báo sử dụng dấu ngoặc kép, rằng biểu thức sẽ được mở rộng 'ngay bây giờ' với dấu ngoặc kép thay vì sau đó khi bẫy được gọi.
LaFayette

110

Tôi thường tạo một thư mục để đặt tất cả các tệp tạm thời của mình và ngay sau đó, tạo một trình xử lý EXIT để dọn dẹp thư mục này khi tập lệnh thoát.

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

Nếu bạn đặt tất cả các tệp tạm thời của mình $MYTMPDIR, thì tất cả chúng sẽ bị xóa khi tập lệnh của bạn thoát trong hầu hết các trường hợp. Tuy nhiên, việc giết một quá trình bằng SIGKILL (kill -9) sẽ giết quá trình đó ngay lập tức, vì vậy trình xử lý EXIT của bạn sẽ không chạy trong trường hợp đó.


27
+1 Chắc chắn sử dụng một cái bẫy trên EXIT, không phải là TERM / INT / HUP ngớ ngẩn / bất cứ điều gì khác mà bạn có thể nghĩ ra. Mặc dù vậy, hãy nhớ trích dẫn mở rộng tham số của bạn và tôi cũng khuyên bạn nên trích dẫn duy nhất cái bẫy của bạn: trap 'rm -rf "$ TMPDIR"' EXIT
lhunath

7
Các dấu ngoặc kép, vì sau đó bẫy của bạn sẽ vẫn hoạt động nếu sau này trong tập lệnh của bạn, bạn quyết định xóa và thay đổi TMPDIR vì các bất thường.
lhunath

1
@AaronDigulla Tại sao $ () so với backticks lại quan trọng?
Ogre Psalm33


3
Mã @AlexanderTorstling phải luôn được trích dẫn đơn để ngăn chặn việc đưa vào dẫn đến việc thực thi mã tùy ý. Nếu bạn mở rộng dữ liệu thành mã bash STRING thì dữ liệu đó bây giờ có thể thực hiện bất kỳ điều gì mà mã thực hiện, dẫn đến lỗi vô tội với khoảng trắng nhưng cũng có lỗi phá hoại chẳng hạn như xóa homedir của bạn vì lý do kỳ lạ hoặc tạo ra các lỗ hổng bảo mật. Lưu ý rằng bẫy lấy một chuỗi mã bash sẽ được đánh giá là sau này. Vì vậy, sau này khi cái bẫy hoạt động, các dấu nháy đơn sẽ biến mất và chỉ còn lại dấu nháy kép theo cú pháp.
lhunath

25

Bạn muốn sử dụng lệnh bẫy để xử lý việc thoát tập lệnh hoặc các tín hiệu như CTRL-C. Xem Greg's Wiki để biết thêm chi tiết.

Đối với basename $0các tệp tạm thời của bạn, sử dụng là một ý tưởng hay, cũng như cung cấp một mẫu cung cấp đủ chỗ cho các tệp tạm thời:

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

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

trap 'rm -f $TMP1 $TMP2' EXIT

1
Không mắc bẫy trên TERM / INT. Bẫy trên EXIT. Cố gắng dự đoán điều kiện thoát dựa trên các tín hiệu nhận được là ngớ ngẩn và chắc chắn không phải là một catchall.
lhunath

3
Điểm nhỏ: Sử dụng $ () thay vì đơn lẻ. Và đặt dấu ngoặc kép xung quanh $ 0 vì nó có thể chứa khoảng trắng.
Aaron Digulla

Chà, trong nhận xét này, các thanh backticks hoạt động tốt, nhưng đó là một điểm công bằng, bạn nên có thói quen sử dụng $(). Đã thêm dấu ngoặc kép.
Brian Campbell

1
Bạn có thể thay thế toàn bộ chương trình con của bạn chỉ với TMP1 = $ (tempfile -s "XXXXXX")
Ruslan Kabalin

4
@RuslanKabalin Không phải tất cả các hệ thống đều có tempfilelệnh, trong khi tất cả các hệ thống hiện đại hợp lý mà tôi biết đều có mktemplệnh.
Brian Campbell

9

Chỉ cần lưu ý rằng câu trả lời chọn là bashism, có nghĩa là giải pháp như

trap "{ rm -f $LOCKFILE }" EXIT

sẽ chỉ hoạt động trong bash (nó sẽ không bắt Ctrl + c nếu shell là dashhoặc classic sh), nhưng nếu bạn muốn tương thích thì bạn vẫn cần phải liệt kê tất cả các tín hiệu mà bạn muốn bẫy.

Cũng nên nhớ rằng khi script thoát khỏi bẫy tín hiệu "0" (hay còn gọi là EXIT) luôn được thực hiện dẫn đến việc thực thi traplệnh kép .

Đó là lý do để không xếp tất cả các tín hiệu vào một dòng nếu có tín hiệu EXIT.

Để hiểu rõ hơn, hãy xem tập lệnh sau sẽ hoạt động trên các hệ thống khác nhau mà không có thay đổi:

#!/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 

Giải pháp này sẽ cung cấp cho bạn nhiều quyền kiểm soát hơn vì bạn có thể chạy một số mã của mình khi xuất hiện tín hiệu thực tế ngay trước lần thoát cuối cùng ( preExitchức năng) và nếu cần, bạn có thể chạy một số mã ở tín hiệu EXIT thực tế (giai đoạn cuối cùng của lần thoát)


4

Việc thay thế sử dụng tên tệp có thể dự đoán được với $$ là một lỗ hổng bảo mật và bạn không bao giờ nên nghĩ đến việc sử dụng nó. Ngay cả khi nó chỉ là một script cá nhân đơn giản trên PC người dùng duy nhất của bạn. Đó là một thói quen rất xấu mà bạn không nên mắc phải. BugTraq chứa đầy các sự cố "tệp tạm thời không an toàn". Xem tại đây , đâyđây để biết thêm thông tin về khía cạnh bảo mật của tệp tạm thời.

Ban đầu tôi đã nghĩ đến việc trích dẫn các bài tập TMP1 và TMP2 không an toàn, nhưng tôi nghĩ rằng điều đó có lẽ không phải là một ý kiến ​​hay .


Tôi sẽ đưa ra nếu có thể: +1 cho lời khuyên bảo mật và +1 khác để không trích dẫn ý tưởng xấu và tài liệu tham khảo
TMG

1

Tôi thích sử dụng cách tempfiletạo tệp trong / tmp theo cách an toàn và bạn không phải lo lắng về cách đặt tên của nó:

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

Thật đáng buồn là tempfile rất không thể di chuyển mặc dù an toàn hơn, vì vậy tốt hơn là bạn nên tránh nó hoặc ít nhất là giả lập nó.
lericson

1

Tôi không thể tin rằng rất nhiều người cho rằng tên tệp sẽ không chứa khoảng trắng. Thế giới sẽ sụp đổ nếu $ TMPDIR được gán cho "thư mục tạm thời".

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

Dấu cách và các ký tự đặc biệt khác như dấu ngoặc kép và dấu xuống dòng trong tên tệp nên được xem xét trong mã như một yêu cầu của thói quen lập trình tốt.


+1 Trong khi trap 'rm -f "${zTemp}"' EXITdấu ngoặc kép xử lý chính xác khoảng trắng và các ký tự đặc biệt khác, giải pháp của câu trả lời này không trì hoãn việc đánh giá zTemp. Do đó, không cần phải lo lắng về việc giá trị zTempbị thay đổi sau này trong tập lệnh. Ngoài ra, zTempcó thể được khai báo cục bộ cho một hàm; nó không cần phải là một biến tập lệnh toàn cục.
Robin A. Meade,

Các dấu ngoặc kép xung quanh RHS của bài tập là không cần thiết.
Robin A. Meade,

Cần lưu ý rằng các bản ${parameter@operator}mở rộng đã được thêm vào trong Bash 4.4 (phát hành tháng 9 năm 2016).
Robin A. Meade,

-4

Bạn không cần phải xóa những tệp tmp được tạo bằng mktemp. Chúng sẽ bị xóa sau đó.

Sử dụng mktemp nếu bạn có thể vì nó tạo ra nhiều tệp duy nhất sau đó là tiền tố '$$'. Và có vẻ như cách nhiều nền tảng hơn để tạo các tệp tạm thời sau đó đưa chúng vào / tmp một cách rõ ràng.


4
Xóa bởi ai hoặc cái gì?
innaM

Xóa bởi hoạt động | hệ thống tập tin riêng của mình sau một khoảng thời gian
Mykola Golubyev

4
Ma thuật? Một cronjob? Hay máy Solaris đã khởi động lại?
innaM

Có lẽ là một trong số họ. Nếu tệp tạm thời không bị xóa bởi một số gián đoạn (nó sẽ không quá thường xuyên) một ngày nào đó tệp tmp sẽ bị xóa - đó là lý do tại sao chúng được gọi là tạm thời.
Mykola Golubyev

21
Bạn không thể, không nên, không được cho rằng thứ gì đó được đưa vào / tmp sẽ ở đó mãi mãi; đồng thời, bạn không nên cho rằng nó sẽ biến mất một cách kỳ diệu.
innaM
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.