Làm thế nào để tạo một tập tin tạm thời trong shell script?


155

Trong khi chạy tập lệnh, tôi muốn tạo một tập tin tạm thời trong /tmpthư mục.

Sau khi thực hiện tập lệnh đó, tập lệnh đó sẽ được làm sạch.

Làm thế nào để làm điều đó trong shell script?

Câu trả lời:


198
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
: ...
rm "$tmpfile"

Bạn có thể đảm bảo rằng một tệp bị xóa khi các tập lệnh thoát (bao gồm cả giết chết và sự cố) bằng cách mở một mô tả tệp vào tệp và xóa nó. Tệp luôn có sẵn (đối với tập lệnh; không thực sự cho các quy trình khác nhưng /proc/$PID/fd/$FDlà một công việc xung quanh) miễn là bộ mô tả tệp được mở. Khi nó được đóng (mà kernel tự động thực hiện khi quá trình thoát), hệ thống tập tin sẽ xóa tập tin.

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
exec 3>"$tmpfile"
rm "$tmpfile"
: ...
echo foo >&3

4
Câu trả lời hay, giải pháp tao nhã với bộ mô tả tệp trong trường hợp bị sập +1
hỗn loạn

2
/proc- ngoại trừ các hệ thống không có nó.
Dennis Williamson

4
những gì hiện exec 3> "$tmpfile"làm gì? Điều đó không hữu ích nếu tmpfile là một tập lệnh độc lập?
Alexej Magura

5
Làm thế nào để bạn đọc từ FD được tạo?
eckes

3
"Bạn có thể sử dụng con mèo <3 hoặc một cái gì đó tương tự." thực tế là đọc từ một tập tin có tên 3 @ dragon788. Ngoài ra, cat <&3sẽ cho Bad file descriptor. Tôi sẽ đánh giá cao nếu bạn sửa nó hoặc gỡ bỏ nó; thông tin sai lệch không giúp được gì nhiều.
Daniel Farrell

65

Sử dụng mktempđể tạo một tập tin hoặc thư mục tạm thời:

temp_file=$(mktemp)

Hoặc cho một direcotry:

temp_dir=$(mktemp -d)

Khi kết thúc tập lệnh, bạn phải xóa tập tin / thư mục tạm thời:

rm ${temp_file}
rm -R ${temp_dir}

mktemp tạo tập tin trong /tmpthư mục hoặc trong phần bổ sung được cung cấp với --tmpdirđối số.


20
Bạn có thể sử dụng trap "rm -f $temp_file" 0 2 3 15ngay sau khi tạo tệp để khi tập lệnh thoát hoặc bị dừng với ctrl-Ctệp vẫn bị xóa.
wurtel

1
@wurtel Điều gì xảy ra nếu EXITlà cái móc duy nhất trap?
Hauke ​​Laging 30/1/2015

4
@HaukeLaging Sau đó, bẫy không kích hoạt nếu tập lệnh bị dừng bằng Ctrl + C. Một điều cần lưu ý là TRAP không giúp đỡ nếu bạn kill -9 $somepid. Tín hiệu tiêu diệt đặc biệt đó là insta-death không có gì khác xảy ra.
dragon788

5
@ dragon788 Bạn đã thử chưa? Bạn nên. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke ​​Laging

Bẫy EXITlà đủ.
Kusalananda

15

Nếu bạn đang sử dụng hệ thống có mktemp , bạn nên sử dụng nó làm câu trả lời khác.

Với công cụ POSIX:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"

Điều gì xảy ra nếu EXITlà cái móc duy nhất cho trap?
Hauke ​​Laging 30/1/2015

@HaukeLaging: tmpfilevẫn bị xóa trước khi thoát tập lệnh, nhưng không phải khi tập lệnh nhận được tín hiệu khác.
cuonglm

Đó không phải là những gì xảy ra ở đây (GNU bash, Phiên bản 4.2.53).
Hauke ​​Laging 30/1/2015

@HaukeLaging: Ý bạn là That's not what happensgì?
cuonglm

3
mktempcó nguồn gốc từ HP / UX với một cú pháp khác nhau. Todd C. Miller đã tạo một cái khác cho OpenBSD vào giữa những năm 90 (được sao chép bởi FreeBSD và NetBSD) và sau đó làm cho nó cũng có sẵn như là một tiện ích độc lập (www.mktemp.org). Đó là mktemptiện ích thường được sử dụng trên Linux cho đến khi tiện ích (hầu hết tương thích) được thêm vào lõi GNU trong năm 2007. Chỉ cần nói rằng người ta không thể thực sự nói mktemplà tiện ích GNU.
Stéphane Chazelas

14

Một số vỏ có tính năng tích hợp.

zsh

zsh's =(...)hình thức thay thế tiến trình sử dụng một tập tin tạm thời. Ví dụ, =(echo test)mở rộng tới đường dẫn của tệp tạm thời chứa test\n.

$ {cat $file; ls -l /dev/fd/3; echo test2 >&3; cat $file} 3<> ${file::==(echo test)}
test
lrwx------ 1 stephane stephane 64 Jan 30 11:19 /dev/fd/3 -> /tmp/zshMLbER0
test2

Tập tin đó được tự động loại bỏ, khi lệnh đã kết thúc.

bash / zsh trên Linux.

Các tệp ở đây hoặc các chuỗi ở đây bashzshđược triển khai dưới dạng các tệp tạm thời bị xóa.

Vì vậy, nếu bạn làm:

exec 3<<< test

Bộ mô tả tệp 3 được kết nối với một tệp tạm thời bị xóa có chứa test\n.

Bạn có thể lấy nội dung của nó với:

cat <&3

Nếu trên Linux, bạn cũng có thể đọc hoặc ghi vào tệp đó thông qua /dev/fd/3

$ exec 3<<< test
$ cat <&3
test
$ echo foo > /dev/fd/3
$ cat /dev/fd/3
foo

(một số vỏ khác sử dụng đường ống hoặc có thể sử dụng /dev/nullnếu tài liệu ở đây trống).

MỘT BỘ MÔ TẢ CHÍNH THỨC CUNG CẤP MỘT TIÊU CHUẨN CHO THIẾT KẾ HỆ ĐIỀU HÀNH, ĐẶC BIỆT LÀ CÁC MÔ TẢ TƯƠNG THÍCH VỚI UNIX

Không có mktemptiện ích POSIX. Tuy nhiên, POSIX chỉ định mkstemp(template)API Cm4tiện ích tiêu chuẩn hiển thị API đó với mkstemp()chức năng m4 cùng tên.

mkstemp()cung cấp cho bạn một tên tệp với một phần ngẫu nhiên được đảm bảo không tồn tại tại thời điểm hàm được gọi. Nó tạo tập tin với quyền 0600 theo cách không có chủng tộc.

Vì vậy, bạn có thể làm:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

Tuy nhiên, xin lưu ý rằng bạn cần xử lý dọn dẹp khi thoát, mặc dù nếu bạn chỉ cần viết và đọc tệp một số lần cố định, bạn có thể mở nó và xóa nó ngay sau khi tạo như thế cho tài liệu này ở đây-doc / here- cách tiếp cận chuỗi trên:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

# open once for writing, twice for reading:
exec 3> "$tempfile" 4< "$tempfile" 5< "$tempfile"

rm -f -- "$tmpfile"

cmd >&3   # store something in the temp file
exec 3>&- # fd no longer needed

# read the content twice:
cat <&4
cat <&5

Bạn có thể mở tệp để đọc một lần và tua lại giữa hai lần đọc, tuy nhiên không có tiện ích POSIX nào có thể thực hiện việc tua lại đó ( lseek()), vì vậy bạn không thể thực hiện được điều đó trong tập lệnh POSIX ( zsh( sysseekdựng sẵn) và ksh93( <#((...))toán tử) làm điều đó mặc dù).


1
Bash cũng có quy trình thay thế bằng cách sử dụng<()
WinnieNicklaus

3
@WinnieNicklaus, vâng, nhưng điều đó không sử dụng các tệp tạm thời nên không liên quan ở đây. Quá trình thay thế được giới thiệu bởi ksh, được sao chép bởi bash và zsh và zsh đã mở rộng nó với dạng thứ 3 : =(...).
Stéphane Chazelas

7

Dưới đây là một câu trả lời được cải thiện một chút trong dòng của Hauke ​​Laging's:

#!/bin/bash

tmpfile=$(mktemp)  # Create a temporal file in the default temporal folder of the system

# Lets do some magic for the tmpfile to be removed when this script ends, even if it crashes
exec {FD_W}>"$tmpfile"  # Create file descriptor for writing, using first number available
exec {FD_R}<"$tmpfile"  # Create file descriptor for reading, using first number available
rm "$tmpfile"  # Delete the file, but file descriptors keep available for this script

# Now it is possible to work with the temporal file
echo foo >&$FD_W
echo bar >&$FD_W  # Note that file descriptor always concatenates, not overwrites

cat <&$FD_R

2
Cần lưu ý rằng nội dung chỉ có sẵn một lần. Tức là nếu tôi làm mèo <& $ FD_R lần thứ hai, không có đầu ra nào được tạo ra. Xem unix.stackexchange.com/questions/166482/ . Có cách nào để tệp tự động bị xóa nếu chương trình gặp sự cố, nhưng làm cho nó có thể truy cập nhiều lần?
smihael

0

Quy trình làm việc của tôi thường với các tệp tạm thời là do một số tập lệnh bash tôi đang thử nghiệm. Tôi muốn teenó lên để tôi có thể thấy rằng nó hoạt động và lưu kết quả đầu ra cho lần lặp tiếp theo của quy trình của tôi. Tôi đã tạo một tệp có têntmp

#!/bin/bash
echo $(mktemp /tmp/$(date +"%Y-%m-%d_%T_XXXXXX"))

để tôi có thể sử dụng nó như

$ some_command --with --lots --of --stuff | tee $(tmp)

Lý do tôi thích datetime được định dạng trước các giá trị ngẫu nhiên là vì nó cho phép tôi tìm tệp tmp mà tôi vừa thực hiện dễ dàng và tôi không phải suy nghĩ về việc đặt tên cho lần sau (và tập trung vào việc lấy tập lệnh dang của tôi làm việc).

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.