ทำไมการเปลี่ยนเส้นทางเอาต์พุตของไฟล์ไปยังตัวมันเองสร้างไฟล์เปล่า?


19

ทำไมการเปลี่ยนเส้นทางเอาต์พุตของไฟล์ไปยังตัวมันเองสร้างไฟล์เปล่า?

ระบุใน Bash แล้วทำไมต้องทำ

less foo.txt > foo.txt

และ

fold foo.txt > foo.txt

ผลิตที่ว่างเปล่าfoo.txt? เนื่องจากการผนวกเช่นless eggs.py >> eggs.pyสร้างข้อความสองฉบับeggs.pyหนึ่งอาจคาดว่าการเขียนทับจะสร้างสำเนาข้อความหนึ่งชุด

หมายเหตุฉันไม่ได้บอกว่านี่เป็นข้อผิดพลาดมันมีแนวโน้มที่จะชี้ไปที่บางสิ่งบางอย่างเกี่ยวกับ Unix


คำตอบ:


20

เมื่อคุณใช้>ไฟล์จะถูกเปิดในโหมดการตัดเพื่อให้เนื้อหาถูกลบออกก่อนที่คำสั่งจะพยายามอ่าน

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

หากคุณต้องการใช้ไฟล์ทั้งอินพุตและเอาต์พุตด้วยคำสั่งที่ไม่สนับสนุนการแก้ไขสถานที่คุณสามารถใช้วิธีแก้ไขปัญหาสองสามข้อ:

  • ใช้ไฟล์ตัวกลางและเขียนทับไฟล์ต้นฉบับเมื่อเสร็จสิ้นและหากไม่มีข้อผิดพลาดเกิดขึ้นขณะเรียกใช้ยูทิลิตี้ (นี่เป็นวิธีที่ปลอดภัยที่สุดและพบได้บ่อยกว่า)

    fold foo.txt > fold.txt.$$ && mv fold.txt.$$ foo.txt
  • หลีกเลี่ยงไฟล์ตัวกลางที่มีค่าใช้จ่ายในการสูญหายของข้อมูลบางส่วนหรือทั้งหมดหากเกิดข้อผิดพลาดหรือการหยุดชะงัก ในตัวอย่างนี้เนื้อหาของfoo.txtจะถูกส่งเป็นอินพุตไปยังsubshell (ภายในวงเล็บ) ก่อนที่ไฟล์จะถูกลบ ไอโหนดก่อนหน้านี้ยังมีชีวิตอยู่ขณะที่ subshell เปิดอยู่ขณะที่อ่านข้อมูล ไฟล์ที่เขียนโดยยูทิลิตี้ภายใน (ที่นี่fold) ในขณะที่มีชื่อเดียวกัน (foo.txt) ชี้ไปที่ inode ที่แตกต่างกันเนื่องจากรายการไดเรกทอรีเก่าถูกลบดังนั้นเทคนิคมีสอง "ไฟล์" ที่แตกต่างกันที่มีชื่อเดียวกันในระหว่างกระบวนการ เมื่อ subshell สิ้นสุดลงไอโหนดเก่าจะถูกปล่อยออกมาและข้อมูลจะหายไป ระวังเพื่อให้แน่ใจว่าคุณมีพื้นที่เพียงพอที่จะเก็บทั้งไฟล์เก่าและไฟล์ใหม่ในเวลาเดียวกันเป็นการชั่วคราวมิฉะนั้นคุณจะสูญเสียข้อมูล

    (rm foo.txt; fold > foo.txt) < foo.txt

3
spongeจากmoreutilsสามารถช่วยได้ fold foo.txt | sponge foo.txt- หรือfold foo.txt | sponge !$ควรทำเช่นกัน
slhck

@slhck แน่นอนฟองน้ำก็สามารถทำงานได้เช่นกัน อย่างไรก็ตามไม่ได้ระบุโดย POSIX หรือกระแสหลักใน Unix เช่น OSes มันไม่น่าจะเกิดขึ้น
jlliagre

มันไม่ได้เป็นเช่นนั้นไม่สามารถทำปัจจุบันแม้ว่า;)
slhck

7

ไฟล์ถูกเปิดสำหรับการเขียนโดยเชลล์ก่อนที่แอ็พพลิเคชันจะมีโอกาสอ่าน การเปิดไฟล์เพื่อการตัดทอนไฟล์


0

ในทุบตีผู้ประกอบการสตรีมเปลี่ยนเส้นทาง... > foo.txtเปล่าก่อนที่จะประเมินตัวถูกดำเนินการทางด้านซ้ายfoo.txt

อาจใช้การทดแทนคำสั่งและพิมพ์ผลลัพธ์เป็นวิธีแก้ปัญหา วิธีนี้ใช้อักขระเพิ่มเติมน้อยกว่าคำตอบอื่น ๆ :

printf "%s\n" "$(less foo.txt)" > foo.txt

ระวัง: คำสั่งนี้ไม่ได้รักษาขึ้นบรรทัดใหม่ trailling (s) foo.txtใน ดูในส่วนความคิดเห็นด้านล่างสำหรับข้อมูลเพิ่มเติม

ที่นี่เชลล์ย่อย$(...)จะถูกประเมินก่อนตัวดำเนินการเปลี่ยนเส้นทางสตรีม>ดังนั้นการเก็บรักษาข้อมูล


@KamilMaciorowski: tmp=$(cmd; printf q);  printf '%s' "${tmp%q}"ที่จริงมี แต่คุณพลาดปัญหาอื่น ๆ ด้วยคำตอบนี้: มันบอกว่า "subshell" เมื่อมันหมายถึง "การทดแทนคำสั่ง" ใช่การแทนที่คำสั่งโดยทั่วไปแล้วจะเป็น subshells แต่ไม่ใช่ในทางกลับกันและ subshells โดยทั่วไปจะไม่มีความช่วยเหลือสำหรับปัญหานี้
สกอตต์

@ KamilMaciorowski ฉันรู้สึกแย่มากที่คิดถึงสิ่งนี้ทั้งหมด ขอบคุณสำหรับการชี้ทั้งหมดนี้ สำหรับจุดที่ (4) ของคุณ: backquotes จะทำเคล็ดลับเช่นรักษาบรรทัดใหม่หรือไม่
Louis-Jacob Lebel

@Scott ขอบคุณสำหรับคำตอบของคุณ ฉันเปลี่ยน "subshell" เป็น "การแทนที่คำสั่ง" โดยวิธีการที่ฉันสงสัยว่าสิ่งที่แตกต่างที่แน่นอนระหว่างทั้งสอง
Louis-Jacob Lebel

ไม่ backquotes (backticks) ตัดอักขระขึ้นบรรทัดใหม่ตามด้วย
Kamil Maciorowski

เอาล่ะฉันเพิ่มข้อความเตือนในตอนนี้ ฉันจะลบมันหากฉันพบทางออก
Louis-Jacob Lebel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.