ลบบรรทัดแบบแทนที่ในระบบไฟล์แบบเต็ม?


11

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

ฉันเขียนsedคำสั่งง่ายๆเพื่อตรวจสอบหาบรรทัดที่เพิ่มผิดและลบออกแล้วทดสอบในสำเนาของไฟล์ มันทำงานได้ตามที่ตั้งใจไว้

อย่างไรก็ตามเมื่อฉันลองบนเซิร์ฟเวอร์ด้วยดิสก์เต็มฉันได้รับข้อผิดพลาดดังต่อไปนี้ (มาจากหน่วยความจำไม่ใช่คัดลอกและวาง):

sed: couldn't flush /path/to/file/sed8923ABC: No space left on deviceServerHostname

แน่นอนฉันรู้ว่าไม่มีพื้นที่เหลือ นั่นเป็นเหตุผลที่ฉันพยายามลบเนื้อหา! ( sedคำสั่งที่ฉันใช้จะลดไฟล์บรรทัด 4000+ เป็นประมาณ 90 บรรทัด)

sedคำสั่งของฉันเป็นเพียงsed -i '/myregex/d' /path/to/file/filename

มีวิธีที่ฉันสามารถใช้คำสั่งนี้แม้จะมีดิสก์เต็มหรือไม่?

(ต้องเป็นแบบอัตโนมัติเนื่องจากฉันต้องใช้กับเซิร์ฟเวอร์หลายร้อยเครื่องเพื่อแก้ไขปัญหาอย่างรวดเร็ว)

(เห็นได้ชัดว่าข้อผิดพลาดของแอปพลิเคชันจะต้องได้รับการวินิจฉัย แต่ในขณะเดียวกันเซิร์ฟเวอร์ก็ทำงานไม่ถูกต้อง .... )


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

/tmpคือไม่ต้องไป มันอยู่ในระบบไฟล์เดียวกัน

ก่อนที่ผมจะอิสระขึ้นพื้นที่ดิสก์ที่ผมได้ทดสอบและพบว่าฉันสามารถลบบรรทัดในviโดยการเปิดไฟล์และทำงานแล้วประสบความสำเร็จในการบันทึกการเปลี่ยนแปลงด้วย:g/myregex/d :wqดูเหมือนว่าเป็นไปได้ที่จะทำสิ่งนี้โดยอัตโนมัติโดยไม่ต้องใช้ระบบไฟล์แยกต่างหากเพื่อเก็บไฟล์ temp .... (?)


ที่เกี่ยวข้อง: unix.stackexchange.com/q/75889/135943
Wildcard

1
sed -iสร้างสำเนาชั่วคราวเพื่อใช้งาน ฉันสงสัยว่าedมันจะดีกว่าสำหรับเรื่องนี้แม้ว่าฉันจะไม่คุ้นเคยเพียงพอที่จะแก้ปัญหาที่เกิดขึ้นจริง
Eric Renouf

2
ด้วยที่edคุณต้องการเรียกใช้: printf %s\\n g/myregex/d w q | ed -s infileแต่โปรดทราบว่าการใช้งานบางอย่างยังใช้ไฟล์ชั่วคราวเช่นเดียวกับsed(คุณสามารถลองใช้busybox ed - afaik มันไม่ได้สร้างไฟล์ชั่วคราว)
don_crissti

1
@Wildcard - ไม่น่าเชื่อถือ w echo/ printfใช้ และsedเพิ่มอักขระที่คุณวางที่บรรทัดสุดท้ายเพื่อให้คุณสามารถหลีกเลี่ยงการสูญเสียช่องว่างต่อท้าย นอกจากนี้เชลล์ของคุณจะต้องสามารถจัดการไฟล์ทั้งหมดในบรรทัดคำสั่งเดียว นั่นคือความเสี่ยงของคุณ - ทดสอบก่อน bashไม่ดีโดยเฉพาะอย่างยิ่งที่(ฉันคิดว่ามันจะทำ w / พื้นที่สแต็ค?)และอาจทำให้คุณเมื่อใดก็ได้ sedอย่างน้อยสองคำแนะนำจะใช้บัฟเฟอร์ท่อของเคอร์เนลเพื่อผลดีระหว่างพวกเขา แต่วิธีการค่อนข้างคล้ายกัน สิ่งย่อยคำสั่งของคุณจะตัดทอนfileว่า sed w / in นั้นสำเร็จหรือไม่
mikeserv

1
@Wildcard - ลองsed '/regex/!H;$!d;x' <file|{ read v && cat >file;}และถ้ามันใช้ได้โปรดอ่านคำตอบที่เหลือของฉัน '
mikeserv

คำตอบ:


10

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

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

นอกจากนี้ถ้าคุณเพียงแค่ลบเส้นที่ตรงกับ regexp คุณสามารถใช้แทนgrepsed

grep -v 'myregex' /path/to/filename > /tmp/filename && mv /tmp/filename /path/to/filename

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

หากคุณไม่ต้องการใช้ไฟล์ชั่วคราวคุณสามารถลองแคชเนื้อหาไฟล์ในหน่วยความจำ:

file=$(< /path/to/filename)
echo "$file" | grep -v 'myregex' > /path/to/filename

1
มันรักษาสิทธิ์การเป็นเจ้าของและการประทับเวลาหรือไม่ อาจจะrsync -a --no-owner --no-group --remove-source-files "$backupfile" "$destination"มาจากที่นี่
Hastur

@Hastur - คุณหมายถึงการที่sed -iจะรักษาสิ่งนั้นหรือไม่?
mikeserv

2
@Hastur sed -iไม่ได้อนุรักษ์สิ่งเหล่านั้น ฉันเพิ่งลองกับไฟล์ที่ฉันไม่ได้เป็นเจ้าของ แต่อยู่ในไดเรกทอรีที่ฉันเป็นเจ้าของและให้ฉันแทนที่ไฟล์ ฉันเป็นเจ้าของแทนไม่ใช่เจ้าของเดิม
Barmar

1
@ RalphRönnquistเพื่อให้แน่ใจว่าคุณต้องทำในสองขั้นตอน:var=$(< FILE); echo "$FILE" | grep '^"' > FILE
Barmar

1
@Barmar - คุณไม่ทำงาน - คุณไม่รู้ด้วยซ้ำว่าคุณได้เปิดอินพุทสำเร็จแล้ว มากน้อยคุณจะทำคือแต่คุณไม่ได้ใช้v=$(<file)&& printf %s\\n "$v" >file &&ผู้ถามกำลังพูดถึงการเรียกใช้ในสคริปต์โดยอัตโนมัติเขียนทับไฟล์ด้วยส่วนของตัวเอง อย่างน้อยคุณควรตรวจสอบว่าคุณสามารถเปิดอินพุตและเอาต์พุตได้สำเร็จ นอกจากนี้เปลือกอาจระเบิด
mikeserv

4

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

มันเหมือนกับว่าคุณทำงานต่อไปนี้ในเปลือก:

sed 'whatever' file >tmp_file
mv tmp_file file

ณ จุดนี้sedพยายามล้างข้อมูลบัฟเฟอร์ไปยังไฟล์ที่กล่าวถึงในข้อความแสดงข้อผิดพลาดด้วยการfflush()เรียกระบบ:

สำหรับสตรีมเอาต์พุตfflush()บังคับให้เขียนข้อมูลบัฟเฟอร์พื้นที่ผู้ใช้ทั้งหมดสำหรับเอาต์พุตที่กำหนดหรืออัพเดตสตรีมผ่านฟังก์ชันการเขียนพื้นฐานของสตรีม


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


3

ตั้งแต่โพสต์คำถามนี้ฉันได้เรียนรู้ว่าexเป็นโปรแกรมที่สอดคล้องกับ POSIX มันเกือบจะเป็น symlinked สากลvimแต่อย่างใดต่อไปนี้คือ (ฉันคิดว่า) จุดสำคัญเกี่ยวกับexความสัมพันธ์กับระบบไฟล์ (นำมาจากสเปค POSIX):

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

"... จะส่งผลกระทบต่อไฟล์ใด ๆ ... " ฉันเชื่อว่าการวางบางอย่างในระบบไฟล์ (เลยแม้แต่ไฟล์ชั่วคราว) จะนับเป็น "มีผลกับไฟล์ใด ๆ " อาจจะ?*

การศึกษาข้อกำหนด POSIX อย่างละเอียดเพื่อexระบุ "gotchas" บางอย่างเกี่ยวกับการใช้งานแบบพกพาที่ตั้งใจเมื่อเปรียบเทียบกับการใช้สคริปต์ทั่วไปที่exพบทางออนไลน์ (ซึ่งทิ้งไว้กับvimคำสั่ง -specific)

  1. การนำไปใช้+cmdนั้นเป็นทางเลือกตาม POSIX
  2. การอนุญาตให้ใช้หลาย-cตัวเลือกก็ได้เช่นกัน
  3. คำสั่งระดับโลก:g"กิน" ทุกอย่างจนถึงบรรทัดใหม่ที่ไม่ได้ใช้ Escape ถัดไป (ดังนั้นจึงรันหลังจากการจับคู่แต่ละครั้งสำหรับ regex มากกว่าหนึ่งครั้งในตอนท้าย) ดังนั้น-c 'g/regex/d | x'จะลบเพียงหนึ่งอินสแตนซ์แล้วออกจากไฟล์

ดังนั้นตามสิ่งที่ฉันได้วิจัยวิธีการที่สอดคล้องกับ POSIX สำหรับการแก้ไขไฟล์ในระบบไฟล์แบบเต็มเพื่อลบบรรทัดทั้งหมดที่ตรงกับ regex เฉพาะคือ:

ex -sc 'g/myregex/d
x' /path/to/file/filename

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

* หากคุณพบสิ่งใดที่บ่งบอกเป็นอย่างอื่นโปรดพูดถึงมันในความคิดเห็น


2
แต่ ex เขียนไปยัง tmpfiles ... เสมอ ข้อมูลจำเพาะของมันจะเขียนบัฟเฟอร์ลงดิสก์เป็นระยะ มีคำสั่งที่ระบุแม้กระทั่งสำหรับการค้นหาบัฟเฟอร์ไฟล์ tmp บนดิสก์
mikeserv

ขอขอบคุณสำหรับการแบ่งปัน @Wildcard ผมได้เชื่อมโยงกลับไปที่โพสต์ใกล้เคียงที่ SO ฉันถือว่าex +g/match/d -scx fileเป็นไปตาม POSIX เช่นกัน?
kenorb

@kenorb ไม่ค่อยตามการอ่านรายละเอียดของฉัน - ดูจุดของฉัน 1 ในคำตอบข้างต้น การเสนอราคาที่แน่นอนจาก POSIX คือ "ยูทิลิตี้ ex จะต้องเป็นไปตามหลักเกณฑ์ไวยากรณ์ของยูทิลิตี้ XBD ยกเว้นการใช้ '-' ที่ไม่ได้ระบุไว้และ'+' อาจถูกจดจำเป็นตัวคั่นตัวเลือกเช่นเดียวกับ '-'"
ไวด์การ์ด

1
ฉันไม่สามารถพิสูจน์มันได้ยกเว้นโดยดึงดูดความสนใจไปทั่วสามัญสำนึก แต่ฉันเชื่อว่าคุณกำลังอ่านเพิ่มเติมเกี่ยวกับแถลงการณ์นั้นจากสเปคมากกว่าที่เป็นจริง ฉันขอแนะนำว่าการตีความที่ปลอดภัยยิ่งขึ้นคือไม่มีการเปลี่ยนแปลงใด ๆ ในบัฟเฟอร์การแก้ไขจะมีผลกับไฟล์ใด ๆ ที่มีอยู่ก่อนที่เซสชันการแก้ไขจะเริ่มขึ้นหรือชื่อผู้ใช้ ดูความเห็นของฉันในคำตอบของฉันด้วย
G-Man กล่าวว่า 'Reinstate Monica'

@ G-Man ฉันคิดว่าคุณพูดถูก การตีความครั้งแรกของฉันอาจเป็นความคิดที่ปรารถนา อย่างไรก็ตามเนื่องจากการแก้ไขไฟล์ในvi การทำงานบนระบบไฟล์เต็มผมเชื่อว่าในที่สุดกรณีก็จะทำงานร่วมกับexเช่นเดียวแม้ว่าอาจจะไม่ได้สำหรับไฟล์ ginormous sed -iไม่ทำงานบนระบบไฟล์แบบเต็มโดยไม่คำนึงถึงขนาดไฟล์
Wildcard

2

ใช้ท่อลุค!

อ่านไฟล์ | ตัวกรอง | เขียนกลับ

sed 's/PATTERN//' BIGFILE | dd of=BIGFILE conv=notrunc

ในกรณีนี้sedไม่ได้สร้างไฟล์ใหม่และเพียงแค่การส่งออกส่งประปาddซึ่งจะเปิดไฟล์เดียวกัน แน่นอนหนึ่งสามารถใช้grepในกรณีพิเศษ

grep -v 'PATTERN' BIGFILE | dd of=BIGFILE conv=notrunc

จากนั้นตัดส่วนที่เหลือ

dd if=/dev/null of=BIGFILE seek=1 bs=BYTES_OF_SED_OUTPUT

1
คุณสังเกตเห็นส่วน"full filesystem"ของคำถามหรือไม่
ไวด์การ์ด

1
@ Wildcard sedใช้ไฟล์ temp เสมอหรือไม่ grepต่อไปจะไม่ได้
Leben Gleben

นี่ดูเหมือนจะเป็นทางเลือกในการspongeออกคำสั่ง ใช่sedด้วย-iเสมอสร้างไฟล์ lilke "seduyUdmw" ด้วยสิทธิ 000
Pablo A

1

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

ลองนี้ (ที่หน้าจอโต้ตอบเชลล์ของคุณ):

$ ed / path / to / file / filename
P
g / myregex / d
W
Q

P(ซึ่งเป็นเมืองหลวง P) ไม่จำเป็นอย่างเคร่งครัด มันจะเปิดขึ้นพร้อมท์; ไม่ว่าคุณกำลังทำงานในความมืดและบางคนพบว่าอึกอักนี้ wและqมีWพระราชพิธีและQ uit

edฉาวโฉ่สำหรับการวินิจฉัยความลับ หาก ณ จุดใดก็ตามจะแสดงสิ่งอื่นใดที่พร้อมต์ (ซึ่งคือ*) หรือสิ่งที่ชัดเจนยืนยันการดำเนินการที่ประสบความสำเร็จ ( โดยเฉพาะอย่างยิ่งถ้ามันมี?) อย่าเขียนไฟล์ (ด้วยw) เพียงออกจาก ( q) ถ้ามันไม่ทำให้คุณออกไปลองพูดqอีกครั้ง

หาก/tmpไดเรกทอรีของคุณอยู่ในระบบไฟล์ที่เต็ม (หรือหากระบบไฟล์เต็มเช่นกัน) ให้ลองหาที่ว่างสักแห่ง ความโกลาหลที่กล่าวถึงการติดตั้ง tmpfs หรืออุปกรณ์เก็บข้อมูลภายนอก (เช่นแฟลชไดรฟ์) แต่ถ้าคุณมีระบบไฟล์หลายและพวกเขาจะไม่ทั้งหมดเต็มคุณสามารถเพียงแค่ใช้เป็นหนึ่งในคนที่มีอยู่อื่น ๆ ความสับสนวุ่นวายแนะนำให้คัดลอกไฟล์ไปยังระบบไฟล์อื่นแก้ไขมันที่นั่น (พร้อมsed) แล้วคัดลอกกลับ ณ จุดนี้อาจเป็นทางออกที่ง่ายที่สุด แต่ทางเลือกที่จะสร้างไดเรกทอรีที่สามารถเขียนได้ในระบบแฟ้มที่มีเนื้อที่ว่างบางตัวแปรสภาพแวดล้อมชุดที่จะชี้ไปที่ไดเรกทอรีที่และเรียกใช้แล้วTMPDIR ed(การเปิดเผย: ฉันไม่แน่ใจว่าสิ่งนี้จะได้ผลหรือไม่ แต่ก็ไม่สามารถทำร้ายได้)

เมื่อคุณedทำงานคุณสามารถทำสิ่งนี้ได้โดยอัตโนมัติ

ed ชื่อไฟล์ << EOF
g / myregex / d
W
Q
EOF

ในสคริปต์ หรือ ตามที่ don_crissti แนะนำprintf '%s\n' 'g/myregex/d' w q | ed -s filename


อืมม สามารถทำสิ่งเดียวกัน (ไม่ว่าจะมีedหรือมีex) หน่วยความจำที่ใช้มากกว่าระบบไฟล์แยกกันหรือไม่? นั่นคือสิ่งที่ฉันกำลังจะทำ (และเหตุผลที่ฉันยังไม่ได้รับคำตอบ)
Wildcard

อืมมม นี่อาจซับซ้อนกว่าที่ฉันรู้ ฉันศึกษาที่มาedหลายปีแล้ว ยังมีสิ่งต่าง ๆ เช่นคอมพิวเตอร์ 16 บิตซึ่งกระบวนการ จำกัด พื้นที่ที่อยู่ 64K (!) ดังนั้นความคิดของผู้แก้ไขที่อ่านไฟล์ทั้งหมดลงในหน่วยความจำจึงไม่ใช่การเริ่มต้น ตั้งแต่นั้นมาหน่วยความจำก็ใหญ่ขึ้นเรื่อย ๆ แต่ก็มีดิสก์และไฟล์ เนื่องจากดิสก์มีขนาดใหญ่มากผู้คนจึงไม่จำเป็นต้องจัดการกับ/tmpพื้นที่ว่างที่อาจเกิดขึ้น ฉันเพิ่งดูซอร์สโค้ดของเวอร์ชันล่าสุดอย่างรวดเร็วedและดูเหมือนว่า ... (ต่อ)
G-Man พูดว่า 'Reinstate Monica'

(ต่อ) ... เพื่อใช้งาน "แก้ไขบัฟเฟอร์" เป็นไฟล์ชั่วคราวโดยไม่มีเงื่อนไข - และฉันไม่สามารถหาข้อบ่งชี้ว่ารุ่นใด ๆ ของed(หรือexหรือvi) เสนอตัวเลือกในการเก็บบัฟเฟอร์ในหน่วยความจำ  ในขณะที่การ แก้ไขข้อความด้วย ed และ vi - บทที่ 11: การประมวลผลข้อความ - ส่วนที่ II: การสำรวจ Red Hat Linux - Red Hat Linux 9 Professional Secrets - ระบบ Linuxบอกว่าedบัฟเฟอร์การแก้ไขของอยู่ในหน่วยความจำ ... (ต่อ )
G-Man พูดว่า 'Reinstate Monica'

(ต่อ) ... และการประมวลผลเอกสาร UNIX และการเรียงพิมพ์โดย Balasubramaniam Srinivasanพูดในสิ่งเดียวกันเกี่ยวกับvi(ซึ่งเป็นโปรแกรมเดียวกันกับex) ฉันเชื่อว่าพวกเขากำลังใช้ถ้อยคำเลอะเทอะไม่แน่ชัด - แต่ถ้าอยู่บนอินเทอร์เน็ต (หรือในการพิมพ์) มันจะต้องเป็นจริงใช่ไหม? คุณจ่ายเงินของคุณและคุณเลือก
G-Man กล่าวว่า 'Reinstate Monica'

แต่อย่างไรก็ตามฉันได้เพิ่มคำตอบใหม่
G-Man กล่าวว่า 'Reinstate Monica'

1

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

o=$(sed -ne'/regex/q;p' <file|wc -c)
dd if=/dev/null of=file bs="$o" seek=1

หรือถ้าคุณ${TMPDIR:-/tmp}อยู่ในระบบไฟล์อื่น ๆ :

{   cut -c2- | sed "$script" >file
} <file <<FILE
$(paste /dev/null -)
FILE

เพราะเชลล์(ส่วนใหญ่)วางเอกสารที่นี่ไว้ในไฟล์ชั่วคราวที่ถูกลบ มันปลอดภัยอย่างสมบูรณ์แบบตราบใดที่<<FILEdescriptor ได้รับการดูแลตั้งแต่ต้นจนจบและ${TMPDIR:-/tmp}มีพื้นที่มากเท่าที่คุณต้องการ

เชลล์ที่ไม่ใช้ไฟล์ temp ใช้ไพพ์ดังนั้นจึงไม่ปลอดภัยที่จะใช้วิธีนี้ เปลือกหอยเหล่านี้มักจะashอนุพันธ์ชอบbusybox, dash, BSD sh- zsh, bash, kshและบอร์นเปลือก แต่ไฟล์ทั้งหมดในการใช้งานชั่วคราว

เห็นได้ชัดว่าฉันเขียนโปรแกรมเปลือกหอยเล็ก ๆเมื่อเดือนกรกฎาคมปีที่แล้วเพื่อทำสิ่งนี้


หาก/tmpไม่สามารถใช้งานได้ตราบใดที่คุณสามารถใส่ไฟล์ในหน่วยความจำบางอย่างเช่น ...

sed 'H;$!d;x' <file | { read v &&
sed "$script" >file;}

... อย่างน้อยกรณีทั่วไปจะทำให้แน่ใจว่าไฟล์นั้นถูกบัฟเฟอร์อย่างสมบูรณ์โดยsedกระบวนการแรกก่อนที่จะพยายามตัดทอนไฟล์ in / out

โซลูชันที่ตรงเป้าหมายและมีประสิทธิภาพมากขึ้นอาจเป็น:

sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}

... เพราะจะไม่รบกวนเส้นบัฟเฟอร์ที่คุณต้องการลบต่อไป

การทดสอบกรณีทั่วไป:

{   nums=/tmp/nums
    seq 1000000 >$nums
    ls -lh "$nums"
    wc -l  "$nums"
    sed 'H;$!d;x' <$nums | { read script &&  ### read always gets a blank
    sed "$script" >$nums;}
    wc -l  "$nums"
    ls -lh "$nums"
}

-rw-r--r-- 1 mikeserv mikeserv 6.6M Dec 22 20:26 /tmp/nums
1000000 /tmp/nums
1000000 /tmp/nums
-rw-r--r-- 1 mikeserv mikeserv 6.6M Dec 22 20:26 /tmp/nums

ฉันสารภาพว่าฉันไม่ได้อ่านคำตอบของคุณในรายละเอียดมาก่อนเพราะมันเริ่มต้นด้วยวิธีแก้ปัญหาที่ไม่สามารถใช้งานได้ (สำหรับฉัน) ที่เกี่ยวข้องกับจำนวนไบต์ (แตกต่างกันระหว่างเซิร์ฟเวอร์แต่ละเซิร์ฟเวอร์) และ/tmpอยู่ในระบบไฟล์เดียวกัน ฉันชอบsedรุ่นคู่ของคุณ ฉันคิดว่าการรวมกันของ Barmar และคำตอบของคุณอาจจะดีที่สุดเช่น: myvar="$(sed '/myregex/d' < file)" && [ -n "$myvar" ] && echo "$myvar" > file ; unset myvar (สำหรับกรณีนี้ฉันไม่สนใจเกี่ยวกับการรักษาบรรทัดใหม่ที่ตามมา)
Wildcard

2
@ Wildcard - อาจเป็นได้ แต่คุณไม่ควรใช้เชลล์เช่นฐานข้อมูล the sed| catสิ่งข้างต้นจะไม่เปิดเอาต์พุตเว้นแต่ว่าsedได้บัฟเฟอร์ไฟล์ทั้งหมดแล้วและพร้อมที่จะเริ่มเขียนไฟล์ทั้งหมดไปยังเอาต์พุต ถ้ามันพยายามที่จะ buffer ไฟล์และล้มเหลว - readไม่ประสบความสำเร็จเพราะพบ EOF ใน|ท่อก่อนที่จะอ่านบรรทัดใหม่ครั้งแรกและcat >out ไม่เคยเกิดขึ้นจนกว่าจะถึงเวลาที่จะเขียนมันออกมาจากหน่วยความจำทั้งหมด การโอเวอร์โฟลว์หรืออะไร ๆ ที่มันล้มเหลว ไปป์ไลน์ทั้งหมดคืนความสำเร็จหรือล้มเหลวทุกครั้ง การจัดเก็บไว้ใน var มีความเสี่ยงมากกว่า
mikeserv

@Wildcard - ถ้าฉันจริงๆอยากให้มันในตัวแปรมากเกินไปผมคิดว่ารหัสทำมันชอบfile=$(sed '/regex/!H;$!d;x' <file | read v && tee file) && cmp - file <<<"$file" || shiteเพื่อให้ไฟล์ที่ส่งออกและ var จะเขียนไปพร้อม ๆ กันซึ่งจะทำให้ทั้งสองหรือที่มีประสิทธิภาพการสำรองข้อมูลซึ่งเป็นเหตุผลเดียวที่คุณต้องการอยากจะลอง ทำสิ่งที่ซับซ้อนเกินกว่าที่คุณต้องการ
mikeserv

@mikeserv: ฉันกำลังจัดการปัญหาเดียวกับ OP ในขณะนี้และฉันพบว่าทางออกของคุณมีประโยชน์ แต่ฉันไม่เข้าใจการใช้งานread scriptและread vในคำตอบของคุณ หากคุณสามารถอธิบายเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ฉันจะได้รับการชื่นชมมากขอบคุณ!
sylye

1
@sylye - $scriptเป็นsedสคริปต์ที่คุณจะใช้เพื่อกำหนดเป้าหมายส่วนใด ๆ ของไฟล์ที่คุณต้องการ มันเป็นสคริปต์ที่ทำให้คุณได้รับผลลัพธ์สุดท้ายที่คุณต้องการในสตรีม vเป็นเพียงตัวยึดตำแหน่งสำหรับบรรทัดว่าง ในbashเชลล์ไม่จำเป็นเพราะbashจะใช้$REPLYตัวแปรเชลล์แทนโดยอัตโนมัติหากคุณไม่ได้ระบุไว้ แต่ POSIXly คุณควรทำเช่นนั้นเสมอ ฉันดีใจที่คุณพบว่ามีประโยชน์โดยวิธีการ ขอให้โชคดีกับมัน im mikeserv @ gmail หากคุณต้องการอะไรในเชิงลึก ฉันควรจะมีเครื่องคอมพิวเตอร์อีกครั้งในไม่กี่วัน
mikeserv

0

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

num_bytes = $ (sed '/ myregex / d' / path / to / file / filename | wc -c)
sed '/ myregex / d' / path / to / file / filename 1 <> / path / to / file / ชื่อไฟล์ 
dd if = / dev / null จาก = / path / to / file / filename bs = "$ num_bytes" = 1

บรรทัดแรกจะรันsedคำสั่งด้วยเอาต์พุตที่เขียนไปยังเอาต์พุตมาตรฐาน (และไม่ใช่ไฟล์) ไปยังไพพ์ไปwcยังเพื่อนับจำนวนอักขระ บรรทัดที่สองยังรันsedคำสั่งกับการส่งออกเขียนออกมาตรฐานซึ่งในกรณีนี้จะถูกเปลี่ยนเส้นทางไปยังแฟ้มข้อมูลในการอ่าน / เขียนทับโหมด (ไม่ตัด) ซึ่งจะกล่าวถึงที่นี่ นี่เป็นสิ่งที่อันตรายทีเดียว จะปลอดภัยเฉพาะเมื่อคำสั่งตัวกรองไม่เพิ่มปริมาณข้อมูล (ข้อความ); นั่นคือสำหรับทุก ๆnไบต์ที่อ่านมันเขียนnหรือน้อยกว่าไบต์ แน่นอนนี่เป็นเรื่องจริงสำหรับsed '/myregex/d'คำสั่ง สำหรับทุกบรรทัดที่อ่านมันจะเขียนบรรทัดเดียวกันหรือไม่มีอะไรเลย (ตัวอย่างอื่น ๆ :s/foo/fu/หรือs/foo/bar/จะปลอดภัย แต่s/fu/foo/และs/foo/foobar/จะไม่)

ตัวอย่างเช่น:

$ cat filename
It was
a dark and stormy night.
$ sed '/was/d' filename 1<> filename
$ cat filename
a dark and stormy night.
night.

เพราะข้อมูล 32 ไบต์เหล่านี้:

I  t     w  a  s \n  a     d  a  r  k     a  n  d     s  t  o  r  m  y     n  i  g  h  t  . \n

ถูกเขียนทับด้วย 25 ตัวอักษรเหล่านี้:

a     d  a  r  k     a  n  d     s  t  o  r  m  y     n  i  g  h  t  . \n

เหลือเจ็ดไบต์ที่night.\nเหลือในตอนท้าย

ในที่สุดddคำสั่งจะค้นหาข้อมูลใหม่ที่ถูกขัดข้อมูล (ไบต์ 25 ในตัวอย่างนี้) และลบส่วนที่เหลือของไฟล์ออก คือมันตัดไฟล์ที่จุดนั้น


หากด้วยเหตุผลใดก็ตาม1<>เคล็ดลับไม่ได้ผลคุณสามารถทำได้

sed '/ myregex / d' / path / to / file / filename | dd of = / path / to / file / filename conv = notrunc

นอกจากนี้โปรดทราบว่าตราบใดที่คุณกำลังทำคือลบบรรทัดสิ่งที่คุณต้องการคือgrep -v myregex(ตามที่Barmar ระบุ )


-3

sed -i 'd' / พา ธ / ไปยังไฟล์ / ชื่อไฟล์


1
Hi! มันจะเป็นการดีที่สุดที่จะอธิบายในรายละเอียดให้มากที่สุดเท่าที่เกี่ยวข้องกับวิธีแก้ปัญหาของคุณทำงานและตอบคำถาม
dhag

2
นี่เป็นคำตอบที่ไม่น่ากลัว (a) มันจะล้มเหลวในระบบไฟล์แบบเต็มเช่นเดียวกับคำสั่งเดิมของฉัน; (b) ถ้ามันประสบความสำเร็จมันจะลบไฟล์ WHOLE ทั้งหมดแทนที่จะเป็นแค่บรรทัดที่ตรงกับ regex ของฉัน
สัญลักษณ์แทน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.