การลบไฟล์ temp ที่สร้างขึ้นใน bash exit ที่ไม่คาดคิด


90

ฉันกำลังสร้างไฟล์ชั่วคราวจากสคริปต์ทุบตี ฉันกำลังลบออกเมื่อสิ้นสุดการประมวลผล แต่เนื่องจากสคริปต์ทำงานเป็นเวลานานถ้าฉันฆ่ามันหรือเพียงแค่ CTRL-C ในระหว่างการรันไฟล์ชั่วคราวจะไม่ถูกลบ
มีวิธีใดบ้างที่ฉันสามารถจับเหตุการณ์เหล่านั้นและล้างไฟล์ก่อนที่การดำเนินการจะสิ้นสุดลง

นอกจากนี้ยังมีแนวทางปฏิบัติที่ดีที่สุดสำหรับการตั้งชื่อและตำแหน่งของไฟล์ชั่วคราวเหล่านั้นหรือไม่?
ตอนนี้ฉันไม่แน่ใจระหว่างใช้:

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

และ

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

หรืออาจจะมีทางออกที่ดีกว่านี้?


คำตอบ:


99

คุณสามารถตั้งค่า " กับดัก " เพื่อดำเนินการเมื่อออกหรือบน control-c เพื่อล้างข้อมูล

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

หรืออีกวิธีหนึ่งคือ unix-isms ที่ฉันชอบที่สุดคือการเปิดไฟล์แล้วลบออกในขณะที่คุณยังเปิดอยู่ ไฟล์จะอยู่ในระบบไฟล์และคุณสามารถอ่านและเขียนได้ แต่ทันทีที่โปรแกรมของคุณออกจากระบบไฟล์จะหายไป ไม่แน่ใจว่าคุณจะทำอย่างไรในการทุบตี

BTW: ข้อโต้แย้งหนึ่งที่ฉันจะให้ mktemp แทนการใช้โซลูชันของคุณเอง: หากผู้ใช้คาดว่าโปรแกรมของคุณกำลังจะสร้างไฟล์ชั่วคราวขนาดใหญ่เขาอาจต้องการตั้งค่าTMPDIRเป็นที่ที่ใหญ่กว่าเช่น / var / tmp mktemp ตระหนักดีว่าโซลูชันแบบรีดด้วยมือของคุณ (ตัวเลือกที่สอง) ไม่ทำ ฉันใช้บ่อยTMPDIR=/var/tmp gvim -d foo barเช่น


8
ด้วยการทุบตีexec 5<>$TMPFILEสัมพันธ์ไฟล์อธิบาย 5 ถึง $ tmpfile เป็นอ่านเขียนและคุณสามารถใช้<&5, >&5และ/proc/$$/fd/5(Linux) หลังจากนั้น ปัญหาเดียวคือ Bash ขาดseekฟังก์ชั่น ...
ephemient

ยอมรับว่าคุณตอบเนื่องจากลิงค์ที่คุณให้มาคือสิ่งที่อธิบายสิ่งที่ฉันต้องการได้ดีที่สุด ขอบคุณ
skinp

4
หมายเหตุสองสามข้อเกี่ยวกับtrap: ไม่มีทางที่จะดักจับได้SIGKILL(ตามการออกแบบเนื่องจากจะยุติการดำเนินการทันที) ดังนั้นหากอาจเกิดขึ้นให้มีแผนสำรอง (เช่นtmpreaper) ประการที่สองกับดักไม่ได้เป็นแบบสะสม - หากคุณมีการดำเนินการมากกว่าหนึ่งการดำเนินการทั้งหมดจะต้องอยู่ในtrapคำสั่ง วิธีการหนึ่งที่จะรับมือกับการกระทำการล้างหลาย ๆ คือการกำหนดฟังก์ชั่น (และคุณสามารถ redefine ว่ามันเป็นเงินที่โปรแกรมของคุณถ้าจำเป็น) trap cleanup_function EXITและระบุว่า:
Toby Speight

1
ฉันต้องใช้trap "rm -f $LOCKFILE" EXITไม่เช่นนั้นฉันจะได้รับข้อผิดพลาดในการสิ้นสุดไฟล์โดยไม่คาดคิด
Jaakko

3
Shellcheck เตือนให้ใช้อัญประกาศเดี่ยวว่านิพจน์จะถูกขยาย 'ตอนนี้' ด้วยเครื่องหมายคำพูดคู่แทนที่จะใช้ในภายหลังเมื่อมีการเรียกใช้กับดัก
LaFayette

110

ฉันมักจะสร้างไดเร็กทอรีเพื่อวางไฟล์ชั่วคราวทั้งหมดของฉันจากนั้นสร้างตัวจัดการ EXIT เพื่อล้างไดเร็กทอรีนี้เมื่อสคริปต์ออก

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

หากคุณใส่ไฟล์ชั่วคราวทั้งหมดไว้ข้างใต้ไฟล์$MYTMPDIRทั้งหมดจะถูกลบเมื่อสคริปต์ของคุณออกในสถานการณ์ส่วนใหญ่ การฆ่ากระบวนการด้วย SIGKILL (ฆ่า -9) จะฆ่ากระบวนการทันทีดังนั้นตัวจัดการ EXIT ของคุณจะไม่ทำงานในกรณีนั้น


27
+1 ใช้กับดักอย่างแน่นอนใน EXIT ไม่ใช่ TERM / INT / HUP / อะไรก็ตามที่คุณคิดได้ แต่อย่าลืมที่จะพูดขยายพารามิเตอร์ของคุณและฉันจะยังแนะนำให้คุณเดียวอ้างดักของคุณ: กับดัก 'RM -rf "$ TMPDIR' EXIT
lhunath

7
เครื่องหมายคำพูดเดี่ยวเนื่องจากกับดักของคุณจะยังคงใช้งานได้ในภายหลังในสคริปต์ของคุณคุณตัดสินใจที่จะล้างและเปลี่ยน TMPDIR เนื่องจากสถานการณ์ต่างๆ
lhunath

1
@AaronDigulla ทำไม $ () vs backticks จึงสำคัญ?
Ogre Psalm33


3
รหัส @AlexanderTorstling ควรเป็นเครื่องหมายอัญประกาศเดี่ยวเสมอเพื่อป้องกันการแทรกทำให้เกิดการใช้รหัสโดยอำเภอใจ หากคุณขยายข้อมูลเป็นรหัสทุบตี STRING ตอนนี้ข้อมูลนั้นสามารถทำโค้ดอะไรก็ได้ซึ่งส่งผลให้เกิดข้อบกพร่องที่ไร้เดียงสาเขียนพื้นที่สีขาว แต่ยังทำลายข้อบกพร่องเช่นการล้างบ้านของคุณด้วยเหตุผลที่แปลกประหลาดหรือการแนะนำช่องโหว่ด้านความปลอดภัย โปรดทราบว่า Trap ใช้สตริงของรหัสทุบตีซึ่งจะถูกประเมินตามที่เป็นอยู่ ดังนั้นในภายหลังเมื่อกับดักเริ่มขึ้นเครื่องหมายคำพูดเดี่ยวจะหายไปและจะมีเพียงเครื่องหมายอัญประกาศคู่
lhunath

26

คุณต้องการใช้คำสั่งtrapเพื่อจัดการกับการออกจากสคริปต์หรือสัญญาณเช่น CTRL-C ดูของเกร็กวิกิพีเดียเพื่อดูรายละเอียด

สำหรับ tempfiles ของคุณการใช้basename $0เป็นความคิดที่ดีเช่นเดียวกับการจัดเตรียมเทมเพลตที่มีพื้นที่เพียงพอสำหรับไฟล์ temp:

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

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

trap 'rm -f $TMP1 $TMP2' EXIT

1
อย่าติดกับ TERM / INT กับดัก EXIT การพยายามคาดเดาสภาพทางออกโดยอาศัยสัญญาณที่ได้รับนั้นไร้สาระและไม่สามารถจับได้
lhunath

3
จุดรอง: ใช้ $ () แทน backticks เดี่ยว และใส่เครื่องหมายคำพูดคู่ประมาณ $ 0 เพราะอาจมีช่องว่าง
Aaron Digulla

ดีดี backticks ทำงานในความคิดเห็นนี้ $()แต่ที่จุดยุติธรรมก็เป็นสิ่งที่ดีที่จะอยู่ในนิสัยของการใช้ เพิ่มเครื่องหมายคำพูดคู่เช่นกัน
Brian Campbell

1
คุณสามารถแทนที่รูทีนย่อยทั้งหมดของคุณด้วยเพียง TMP1 = $ (tempfile -s "XXXXXX")
Ruslan Kabalin

4
@RuslanKabalin ไม่ใช่ทุกระบบที่มีtempfileคำสั่งในขณะที่ระบบสมัยใหม่ที่สมเหตุสมผลทั้งหมดที่ฉันรู้ว่ามีmktempคำสั่ง
Brian Campbell

9

เพียงจำไว้ว่าคำตอบที่เลือกคือbashismซึ่งหมายถึงวิธีการแก้ปัญหา

trap "{ rm -f $LOCKFILE }" EXIT

จะใช้งานได้เฉพาะใน bash (จะไม่จับ Ctrl + c ถ้าเชลล์เป็นdashหรือคลาสสิก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

อีกทางเลือกหนึ่งของการใช้ชื่อไฟล์ที่คาดเดาได้กับ $$ คือช่องโหว่ด้านความปลอดภัยที่น่ากลัวและคุณไม่ควรคิดที่จะใช้มันเลย แม้ว่าจะเป็นเพียงสคริปต์ส่วนตัวธรรมดา ๆ บนพีซีผู้ใช้คนเดียวของคุณ เป็นนิสัยที่ไม่ดีอย่างยิ่งที่คุณไม่ควรได้รับ BugTraqเต็มไปด้วยเหตุการณ์ "ไฟล์ชั่วคราวที่ไม่ปลอดภัย" ดูที่นี่ , ที่นี่และที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวกับด้านความปลอดภัยของไฟล์ temp

ผมได้รับในตอนแรกคิดว่าการอ้างไม่ปลอดภัย tmp1 และ TMP2 ได้รับมอบหมาย แต่ในความคิดที่สองที่อาจจะไม่ได้เป็นความคิดที่ดี


ฉันจะให้ถ้าฉันทำได้: +1 สำหรับคำแนะนำด้านความปลอดภัยและอีก +1 สำหรับการไม่อ้างถึงความคิดที่ไม่ดีและการอ้างอิง
TMG

2

ฉันไม่อยากจะเชื่อเลยว่าหลายคนคิดว่าชื่อไฟล์จะไม่มีช่องว่าง โลกจะพังทลายหากมีการกำหนด $ TMPDIR ให้กับ "ไดเรกทอรีชั่วคราว"

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

ช่องว่างและอักขระพิเศษอื่น ๆ เช่นเครื่องหมายคำพูดเดี่ยวและการส่งคืนค่าขนส่งในชื่อไฟล์ควรได้รับการพิจารณาในรหัสเป็นข้อกำหนดของนิสัยการเขียนโปรแกรมที่เหมาะสม


ควรสังเกตว่ามี${parameter@operator}การเพิ่มส่วนขยายใน Bash 4.4 (เผยแพร่เมื่อเดือนกันยายน 2559)
Robin A.Meade

เทียบเท่ากับ Bash <v4.4:trap "rm -f $(printf %q "$zTemp")" EXIT
Robin A.Made

นี่เป็นคำตอบที่ดีที่สุดเพราะไม่ต้องกังวลว่าzTempจะมีการเปลี่ยนแปลงสคริปต์ในภายหลัง นอกจากนี้ยังzTempสามารถประกาศlocalเป็นฟังก์ชัน ไม่จำเป็นต้องเป็นตัวแปรสคริปต์ส่วนกลาง
Robin A.Meade

1

ฉันชอบใช้tempfileที่สร้างไฟล์ใน / tmp ในลักษณะที่ปลอดภัยและคุณไม่ต้องกังวลกับการตั้งชื่อ:

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

tempfile เป็นเรื่องที่ไม่สามารถพกพาได้อย่างน่าเศร้าแม้ว่าจะปลอดภัยกว่าก็ตามดังนั้นจึงควรหลีกเลี่ยงหรืออย่างน้อยก็เลียนแบบได้ดีกว่า
lericson

-4

คุณไม่ต้องกังวลกับการลบไฟล์ tmp ที่สร้างด้วย mktemp พวกเขาจะถูกลบในภายหลัง

ใช้ mktemp หากทำได้เนื่องจากสร้างไฟล์ที่ไม่ซ้ำใครมากขึ้นจากนั้นจึงใช้คำนำหน้า "$$" และดูเหมือนวิธีข้ามแพลตฟอร์มมากขึ้นในการสร้างไฟล์ temp จากนั้นใส่ลงใน / tmp อย่างชัดเจน


ลบโดยการดำเนินการ | ระบบไฟล์เองหลังจากช่วงเวลาหนึ่ง
Mykola Golubyev

4
มายากล? cronjob? หรือเครื่อง Solaris ที่รีบูท?
innaM

น่าจะเป็นหนึ่งในนั้น หากไฟล์ temp ไม่ได้ถูกลบออกโดยการหยุดชะงักบางอย่าง (จะไม่บ่อยเกินไป) สักวันหนึ่งไฟล์ tmp จะถูกลบ - นั่นคือสาเหตุที่พวกเขาเรียกว่า temp
Mykola Golubyev

22
คุณไม่สามารถไม่ควรต้องไม่คิดว่าสิ่งที่ใส่ / tmp จะยังคงอยู่ที่นั่นตลอดไป ในเวลาเดียวกันคุณไม่ควรคิดว่ามันจะหายไปอย่างน่าอัศจรรย์
innaM
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.