เราสามารถใช้โฟลเดอร์ชั่วคราวเช่นไฟล์ชั่วคราว
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
ซึ่งจะถูกทำลายโดยอัตโนมัติหลังจากที่เปลือกนี้จบการทำงาน
เราสามารถใช้โฟลเดอร์ชั่วคราวเช่นไฟล์ชั่วคราว
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
ซึ่งจะถูกทำลายโดยอัตโนมัติหลังจากที่เปลือกนี้จบการทำงาน
คำตอบ:
ในกรณีของไฟล์ชั่วคราวตัวอย่างของคุณในคำถามจะสร้างขึ้นจากนั้นยกเลิกการเชื่อมโยงจากไดเรกทอรี (ทำให้ "หายไป") และเมื่อสคริปต์ปิดตัวบันทึกไฟล์ (อาจเป็นการยกเลิก) พื้นที่ที่ไฟล์ใช้ จะเรียกคืนได้โดยระบบ นี่เป็นวิธีทั่วไปในการจัดการกับไฟล์ชั่วคราวในภาษาเช่น C
มันเท่าที่ฉันรู้ว่าเป็นไปไม่ได้ที่จะเปิดไดเรกทอรีในลักษณะเดียวกันอย่างน้อยก็ไม่ได้ในลักษณะที่จะทำให้ไดเรกทอรีใช้งานได้
วิธีทั่วไปในการลบไฟล์และไดเรกทอรีชั่วคราวเมื่อสิ้นสุดสคริปต์คือการติดตั้งEXIT
กับดักทำความสะอาด ตัวอย่างโค้ดที่ให้ไว้ด้านล่างหลีกเลี่ยงการเล่นปาหี่ยื่นคำอธิบายอย่างสมบูรณ์
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"; rm -rf "$tmpdir"' EXIT
# The rest of the script goes here.
หรือคุณอาจเรียกฟังก์ชันล้างข้อมูล
cleanup () {
rm -f "$tmpfile"
rm -rf "$tmpdir"
}
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap cleanup EXIT
# The rest of the script goes here.
EXIT
กับดักจะไม่ได้รับการดำเนินการเมื่อได้รับKILL
สัญญาณ (ซึ่งไม่สามารถถูกขังอยู่) ซึ่งหมายความว่าจะไม่มีการทำความสะอาดดำเนินการแล้ว อย่างไรก็ตามมันจะดำเนินการเมื่อสิ้นสุดเนื่องจากสัญญาณINT
หรือTERM
(หากใช้งานกับbash
หรือksh
ในเชลล์อื่น ๆ คุณอาจต้องการเพิ่มสัญญาณเหล่านี้หลังจากEXIT
ในtrap
บรรทัดคำสั่ง) หรือเมื่อออกจากปกติเนื่องจากมาถึงตอนท้ายของสคริปต์หรือดำเนินการexit
โทร.
.
และ..
รายการ (ทดสอบบนลินุกซ์ผมไม่ทราบว่าที่สอดคล้องกันข้ามแพลตฟอร์ม.)
exec another-command
อย่างชัดเจน
เขียนเชลล์ฟังก์ชั่นที่จะถูกดำเนินการเมื่อสคริปต์ของคุณถ้าเสร็จแล้ว ในตัวอย่างด้านล่างฉันเรียกมันว่า 'cleanup' และตั้งกับดักที่จะดำเนินการในระดับทางออกเช่น: 0 1 2 3 6
trap cleanup 0 1 2 3 6
cleanup()
{
[ -d $TMP ] && rm -rf $TMP
}
ดูโพสต์นี้สำหรับข้อมูลเพิ่มเติม
cleanup
ก่อนทางออกที่สะอาด (0) และเมื่อได้รับ SIGHUP (1), SIGINT (2), SIGQUIT (3) และ SIGABRT (6) มันจะไม่ทำงานcleanup
เมื่อสคริปต์ออกเนื่องจาก SIGTERM, SIGSEGV, SIGKILL, SIGPIPE และอื่น ๆ นี่เป็นข้อบกพร่องที่เห็นได้ชัด
คุณสามารถ chdir ลงไปแล้วลบออกโดยที่คุณไม่ต้องใช้เส้นทางภายใน:
#! /bin/sh
dir=`mktemp -d`
cd "$dir"
exec 4>file 3<file
rm -fr "$dir"
echo yes >&4 # OK
cat <&3 # OK
cat file # FAIL
echo yes > file # FAIL
ฉันไม่ได้ตรวจสอบ แต่อาจเป็นปัญหาเดียวกันที่สุดเมื่อใช้ openat (2) ใน C กับไดเรกทอรีที่ไม่มีอยู่ในระบบไฟล์อีกต่อไป
หากคุณรูทและบนลีนุกซ์คุณสามารถเล่นกับเนมสเปซที่แยกต่างหากและmount -t tmpfs tmpfs /dir
ข้างใน
คำตอบที่บัญญัติ (ตั้งกับดักที่ EXIT) จะไม่ทำงานหากสคริปต์ของคุณถูกบังคับให้ออกจากสิ่งไม่สะอาด (เช่นกับ SIGKILL) ที่อาจทำให้ข้อมูลที่สำคัญแขวนอยู่รอบ ๆ
ปรับปรุง:
นี่คือยูทิลิตี้ขนาดเล็กที่ใช้แนวทาง namespace มันควรจะรวบรวมด้วย
cc -Wall -Os -s chtmp.c -o chtmp
และCAP_SYS_ADMIN
ความสามารถของไฟล์ (ตามรูท) ด้วย
setcap CAP_SYS_ADMIN+ep chtmp
เมื่อเรียกใช้ (ตามปกติ) ผู้ใช้เป็น
./chtmp command args ...
มันจะยกเลิกการแชร์ namespace ของระบบไฟล์ติดตั้งระบบไฟล์ tmpfs /proc/sysvipc
, chdir ลงไปและเรียกใช้command
ด้วยอาร์กิวเมนต์ที่กำหนด command
จะไม่สืบทอดCAP_SYS_ADMIN
ความสามารถ
ระบบไฟล์นั้นจะไม่สามารถเข้าถึงได้จากกระบวนการอื่นที่ไม่ได้เริ่มต้นcommand
และมันจะหายไปอย่างน่าอัศจรรย์ (ด้วยไฟล์ทั้งหมดที่สร้างขึ้นภายใน) เมื่อcommand
และลูก ๆ ของมันตายไม่ว่าจะเกิดอะไรขึ้น โปรดสังเกตว่านี่เป็นเพียงการยกเลิกการแชร์เนมสเปซการเมาท์ - ไม่มีสิ่งกีดขวางที่ยากระหว่างcommand
และกระบวนการอื่น ๆ ที่ดำเนินการโดยผู้ใช้เดียวกัน พวกเขายังคงสามารถแอบภายใน namespace ของทั้งผ่านptrace(2)
, /proc/PID/cwd
หรือโดยวิธีการอื่น ๆ
การไฮแจ็กของ "ไร้ประโยชน์" /proc/sysvipc
นั้นเป็นเรื่องไร้สาระ แต่ทางเลือกอื่นก็คือการทำสแปม/tmp
ด้วยไดเรกทอรีว่างที่จะต้องถูกลบออกหรือทำให้โปรแกรมขนาดเล็กนี้ซับซ้อนมากด้วยส้อมและรอ หรือdir
สามารถเปลี่ยนเป็นเช่น /mnt/chtmp
และสร้างโดยรูทเมื่อการติดตั้ง; อย่าทำให้ผู้ใช้สามารถกำหนดค่าได้และอย่าตั้งค่าเป็นเส้นทางที่ผู้ใช้เป็นเจ้าของเพราะอาจทำให้คุณต้องเชื่อมโยงกับ symps traps และสิ่งของที่มีขนดกอื่น ๆ ซึ่งไม่คุ้มค่ากับการใช้เวลา
chtmp.c
#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mount.h>
int main(int argc, char **argv){
char *dir = "/proc/sysvipc"; /* LOL */
if(argc < 2 || !argv[1]) errx(1, "usage: %s prog args ...", *argv);
argv++;
if(unshare(CLONE_NEWNS)) err(1, "unshare(CLONE_NEWNS)");
/* "modern" systemd remounts all mount points MS_SHARED
see the NOTES in mount_namespaces(7); YUCK */
if(mount("none", "/", 0, MS_REC|MS_PRIVATE, 0))
err(1, "mount(/, MS_REC|MS_PRIVATE)");
if(mount("tmpfs", dir, "tmpfs", 0, 0)) err(1, "mount(tmpfs, %s)", dir);
if(chdir(dir)) err(1, "chdir %s", dir);
execvp(*argv, argv);
err(1, "execvp %s", *argv);
}
rm $PWD
งานเชลล์ยังคงอยู่ใน dir นั้น แต่ไม่สามารถใส่ไฟล์ใหม่ลงใน "โฟลเดอร์" นี้ สิ่งเดียวที่คุณทำได้คืออ่าน / เขียนด้วยไฟล์ & 3, & 4 ดังนั้นนี่คือ "ไฟล์ชั่วคราว" ไม่ใช่ "โฟลเดอร์ชั่วคราว"
คุณต้องการเชลล์ที่เฉพาะเจาะจงหรือไม่?
หาก zsh เป็นตัวเลือกโปรดอ่านzshexpn(1)
:
ถ้า = (... ) ใช้แทน <(... ) จากนั้นไฟล์ที่ส่งผ่านเป็นอาร์กิวเมนต์จะเป็นชื่อของไฟล์ชั่วคราวที่มีเอาต์พุตของกระบวนการรายการ สิ่งนี้อาจถูกใช้แทนรูปแบบ <สำหรับโปรแกรมที่คาดว่าจะ
lseek
(ดูlseek(2)
) ในไฟล์อินพุต[ ... ]
ปัญหาอื่นเกิดขึ้นเมื่อใดก็ตามที่งานที่มีการทดแทนที่ต้องใช้ไฟล์ชั่วคราวถูกปิดใช้งานโดยเชลล์รวมถึงกรณีที่
&!
หรือ&|
ปรากฏในตอนท้ายของคำสั่งที่มีการทดแทน ในกรณีนั้นไฟล์ชั่วคราวจะไม่ถูกล้างเนื่องจากเชลล์ไม่มีหน่วยความจำของงานอีกต่อไป วิธีแก้ปัญหาคือการใช้ subshell ตัวอย่างเช่น(mycmd =(myoutput)) &!
เนื่องจากเชลล์ย่อยที่ถูกแยกจะรอให้คำสั่งเสร็จสิ้นจากนั้นลบไฟล์ชั่วคราว
วิธีแก้ปัญหาทั่วไปเพื่อให้แน่ใจว่าการทดแทนกระบวนการคงอยู่เป็นระยะเวลาที่เหมาะสมคือส่งผ่านพารามิเตอร์ไปยังฟังก์ชันเชลล์แบบไม่ระบุชื่อ (ชิ้นส่วนของรหัสเชลล์ที่ทำงานทันทีที่มีขอบเขตฟังก์ชัน) ตัวอย่างเช่นรหัสนี้:
() { print File $1: cat $1 } =(print This be the verse)
เอาท์พุทบางอย่างที่คล้ายกับต่อไปนี้
File /tmp/zsh6nU0kS: This be the verse
ตัวอย่างเช่นฉันใช้สิ่งนี้ในไรเฟิล (ส่วนหนึ่งของตัวจัดการไฟล์เรนเจอร์) เพื่อถอดรหัสไฟล์จากนั้นเรียกใช้ปืนไรเฟิลในไฟล์ชั่วคราวซึ่งจะถูกลบเมื่อโปรแกรมย่อยสิ้นสุดลง (อย่าลืมตั้ง$TERMCMD
)
# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")