การแก้ไข inplace ของไฟล์ที่ทำนั้นเป็นอย่างไร?


10

อะไร "inplace" การเปลี่ยนแปลงของไฟล์เช่นผ่านsed -iหรือperl -iหมายถึง?
คำถามของฉันเกี่ยวกับการปรับเปลี่ยน inplace นี้แล้ว ไฟล์ถูกคัดลอกการแก้ไขเสร็จสิ้นในการคัดลอกแล้วแทนที่ต้นฉบับหรือไม่? หรือไฟล์ต้นฉบับถูกแก้ไขอย่างใด


ดูbackreference.org/2011/01/29/in-place-editing-of-filesสำหรับคำอธิบายโดยละเอียดของหัวข้อนี้
scy

สำหรับเรื่องนั้นทำอย่างไรกับexหรือvi?
Wildcard

@ Wildcard - แต่ละระบบมีทั้งระบบ exรักษา mailfile (ชอบdead.mailหรือสิ่งที่อยู่ใน ~ คุณและอยู่ที่ไหนสักแห่งอื่นที่อยู่ใกล้ที่จะจัดคิวจดหมายของคุณปกติ) ตรวจสอบรายละเอียด - แต่ละของพวกเขารัฐได้กำหนดช่วงที่ดี ... exมีรูปแบบไบนารีของตัวเองในกรณีส่วนใหญ่(ดูที่ของคุณ-rescueไฟล์)และนี้จะใช้เพื่อแยกต่างหาก prezero ไฟล์บัฟเฟอร์ชั่วคราว(อาจจะมากที่สุดเท่าที่หก) ดังนั้นบล็อกคัดลอกอินพุตเหล่านี้เพื่อแก้ไขบัฟเฟอร์และซิงค์ที่เขียนลงในออฟเซ็ตต่อการเปลี่ยนแปลง:!writtenหรือไม่
mikeserv

คำตอบ:


18

sed สร้างไฟล์ชั่วคราวเขียนเอาต์พุตลงในไฟล์นั้นจากนั้นเปลี่ยนชื่อไฟล์ชั่วคราวที่ด้านบนของต้นฉบับ

คุณสามารถดูสิ่งที่เกิดขึ้นโดยใช้strace:

$ strace -e trace=file sed -i -e '' a
execve("/usr/bin/sed", ["sed", "-i", "-e", "", "a"], [/* 34 vars */]) = 0
<...trimmed...>
open("a", O_RDONLY)                     = 3
open("./sedxvhRY8", O_RDWR|O_CREAT|O_EXCL, 0600) = 4
rename("./sedxvhRY8", "a")              = 0
+++ exited with 0 +++

บันทึกทั้งหมดนี้การดำเนินงานไฟล์sedทำให้มันสร้างไฟล์ใหม่ (อย่างปลอดภัยด้วยO_CREAT|O_EXCL) aเขียนข้อมูลลงไปและจากนั้นย้ายกลับไปด้านบนของไฟล์ต้นฉบับของฉัน

sed -iยอมรับคำต่อท้ายที่จะใช้สำหรับการสำรองข้อมูลและในกรณีนั้นมันจะย้ายต้นฉบับออกไปก่อน (แทนที่จะเปลี่ยนชื่อเป็นด้านบน) ข้อโต้แย้งที่มีผลบังคับใช้ในส่วน BSD seds ในกรณีนี้มีช่วงเวลาสั้น ๆ เมื่อไม่มีไฟล์ชื่อที่ถูกต้องในไดเรกทอรีเลย

perl ในเวอร์ชันล่าสุดเปิดไฟล์อินพุตจากนั้นลบมันและสร้างไฟล์ใหม่ด้วยชื่อเดียวกัน:

open("a", O_RDONLY)               = 3
unlink("a")                       = 0
open("a", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4

เมื่อคุณลบ ( unlink) ไฟล์ที่คุณเปิดไว้แล้วคุณจะยังคงเข้าถึงมันได้ตราบใดที่คุณยังคงใช้งานได้ดังนั้นมันสามารถอ่านข้อมูลออกจากไฟล์ที่ถูกลบได้ ด้วยวิธีนี้perlเขียนลงในไฟล์เอาต์พุตโดยตรงแทนที่จะเป็นไฟล์ชั่วคราว: ไม่มีการสร้างไฟล์เพิ่มเติม แต่ถ้าคุณอ่านไฟล์ในระหว่างกระบวนการคุณจะได้รับเนื้อหาบางส่วนซึ่งแตกต่างจากsedวิธีการของ นอกจากนี้ยังมีช่วงเวลาสั้น ๆ เมื่อไม่มีไฟล์ที่มีชื่อที่ถูกต้องซึ่งเป็นจุดเริ่มต้นของกระบวนการแทนการสิ้นสุด (เหมือนในsed -i .bak)


ทั้งsedและperlจะ:

  • แทนที่ลิงก์สัญลักษณ์ด้วยไฟล์ธรรมดา
  • ทำลายการเชื่อมโยงอย่างหนัก
  • รักษาความเป็นเจ้าของกลุ่มถ้าเป็นไปได้
  • สร้างไฟล์ที่มีกลุ่มเริ่มต้นของคุณ (หรือกลุ่มของไดเรกทอรีหลักหากไดเรกทอรีนั้นมีsetgidบิต) หากเป็นกลุ่มที่คุณไม่ได้เป็นเจ้าของและคุณไม่ได้รูท
  • รักษาความเป็นเจ้าของไฟล์หากคุณรูท
  • รักษาสิทธิ์พื้นฐาน
  • รักษาsetuidและsetgrpบิตถ้ากลุ่มผลลัพธ์เหมือนกันกับกลุ่มที่เริ่ม
  • รักษาความเหนียว
  • ไม่รักษา xattrs

sed จะ:

  • รักษา ACLs (บนลินุกซ์; ผมไม่ทราบว่าเกี่ยวกับคนอื่น)

perl จะ:

  • ไม่รักษา ACL

ดังกล่าวข้างต้นเป็นความจริงบน Linux กับ GNU sedและ Mac OS X ด้วย (FreeBSD sedมา)


3

นอกเหนือจากคำตอบ @ Homer's จากperldoc perlrun:

ระบุว่าไฟล์ที่ประมวลผลโดยการสร้าง "<>" จะถูกแก้ไขแบบแทนที่ ทำได้โดยเปลี่ยนชื่อไฟล์อินพุตเปิดไฟล์เอาต์พุตโดยใช้ชื่อดั้งเดิมและเลือกไฟล์เอาต์พุตนั้นเป็นค่าเริ่มต้นสำหรับคำสั่ง print () ส่วนขยายหากให้มาจะถูกใช้เพื่อแก้ไขชื่อของไฟล์เก่าเพื่อทำสำเนาสำรองตามกฎเหล่านี้:

หากไม่ได้ระบุนามสกุลจะไม่มีการสำรองข้อมูลและไฟล์ปัจจุบันจะถูกเขียนทับ

หากส่วนขยายไม่มี * แสดงว่าส่วนขยายนั้นต่อท้ายท้ายชื่อไฟล์ปัจจุบันเป็นคำต่อท้าย หากส่วนขยายมีอักขระ * อย่างน้อยหนึ่งตัวเครื่องหมาย * แต่ละรายการจะถูกแทนที่ด้วยชื่อไฟล์ปัจจุบัน

และโปรดจำไว้ว่าจะไม่มีการเก็บลิงก์อ่อนหรือฮาร์ดลิงก์ไว้:

โปรดทราบว่าเนื่องจาก -i เปลี่ยนชื่อหรือลบไฟล์ต้นฉบับก่อนที่จะสร้างไฟล์ใหม่ที่มีชื่อเดียวกันการเชื่อมโยง soft และฮาร์ดสไตล์ UNIX จะไม่ถูกสงวนไว้

ท้ายที่สุดสวิตช์ -i ไม่ขัดขวางการทำงานเมื่อไม่มีไฟล์ใด ๆ อยู่ในบรรทัดคำสั่ง ในกรณีนี้ไม่มีการสำรองข้อมูล (แน่นอนว่าไฟล์ต้นฉบับไม่สามารถกำหนดได้) และประมวลผลการดำเนินการจาก STDIN ถึง STDOUT ตามที่คาดไว้

นอกจากนี้ยังอธิบายว่าทำไมคุณต้องใช้-iกับ-pตัวเลือกหรือใช้printคำสั่งที่ชัดเจนหากคุณต้องการแก้ไข inplace ด้วยperl:

# Opps, file will be truncated, becomes empty
$ perl -i.bak -ne 's/123/qwe/' file

# Right way
$ perl -i.bak -ne 's/123/qwe/;print' file

# Or
$ perl -i.bak -pe 's/123/qwe/' file
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.