มีวิธีที่จะทำให้ Perl - ฉันไม่ได้เชื่อมโยงการอุดตัน?


12

เพื่อนของฉันชี้ให้เห็นว่าถ้าคุณ:

perl -pi.bak -e 's/foo/bar/' somefile

เมื่อ "somefile" เป็น symlink จริง Perl จะทำตามที่เอกสารบอกว่าจะทำ:

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

ซึ่งส่งผลให้ symlink ใหม่ "somefile.bak" ชี้ไปที่ไฟล์จริงที่ไม่เปลี่ยนแปลงและไฟล์ใหม่ "somefile" ที่มีการเปลี่ยนแปลงปกติพร้อมการเปลี่ยนแปลง

ในหลายกรณีการติดตาม symlink จะเป็นพฤติกรรมที่ต้องการ (แม้ว่าจะออกจากตำแหน่งที่ถูกต้องของไฟล์. bak ที่ไม่ชัดเจน) มีวิธีง่ายๆในการทำเช่นนี้นอกเหนือจากการทดสอบ symlink ใน wrapper และการจัดการกรณีอย่างเหมาะสมหรือไม่?

( sedทำสิ่งเดียวกันเพื่อสิ่งที่คุ้มค่า)


1
โทรเป็นกลุ่มหรือ emacs (ฉันคิดว่าทั้งสองทำตามลิงก์) อย่างจริงจังฉันกลัวคำตอบคือการนำ-p -iสคริปต์ของคุณไปใช้ใหม่
Gilles 'หยุดความชั่วร้าย'

จริง ๆ แล้ว Sed ไม่ได้ทำในสิ่งเดียวกันอย่างน้อยก็ไม่ใช่ตั้งแต่เวอร์ชั่น 4.2.1 ที่วางจำหน่ายในเดือนมิถุนายน 2009 คุณต้องมีตัวเลือก --follow-symlinks สำหรับการแก้ไขเพื่อแก้ไขไฟล์ที่ลิงก์ไปยังไม่ใช่การปิดบัง symlink ; ฉันคิดว่าสิ่งนี้ทำเพื่อหลีกเลี่ยงการแตกของสคริปต์ที่มีอยู่ซึ่งอาจขึ้นอยู่กับพฤติกรรมเก่า
Garrett

คำตอบ:


6

ฉันสงสัยว่าspongeอรรถประโยชน์อเนกประสงค์ขนาดเล็ก("ดูดข้อมูลมาตรฐานและเขียนลงไฟล์") จากmoreutilsจะเป็นประโยชน์ในกรณีนี้หรือไม่และจะเป็นไปตาม symlink

ผู้เขียนอธิบาย spongeเช่นนี้:

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

ปกติฉันใช้ฟองน้ำเล็กน้อยเช่นนี้:

sed '...' file | grep '...' | sponge file

เฮ้เจ๋งนั่นมันใช้ได้ ฉันสงสัยว่าความคิดเห็นของ Gilles ข้างต้นเป็นคำตอบที่ "จริง" แต่เนื่องจากเขาไม่ได้ทำให้มันเป็นคำตอบและเมื่อฉันได้เรียนรู้ยูทิลิตี้ใหม่ฉันจะรับสิ่งนี้ :)
mattdm

แต่คุณได้ทดสอบspongeการใช้งานในทางปฏิบัติแล้วหรือยัง? สำหรับฉันยังไม่ได้ คุณช่วยกรุณาฝากความคิดเห็นที่ระบุว่ามันมีพฤติกรรมในการทดสอบตามที่ต้องการหรือไม่? โอ้ฉันเห็นความคิดเห็น ขอบคุณสำหรับการยืนยัน!
imz - Ivan Zakharyaschev

- ใช่ฉันลองก่อนจะตอบกลับ เมื่อfileในตัวอย่างด้านบนของคุณคือ symlink ลิงก์จะถูกทิ้งไว้ตามลำพังและไฟล์จริงจะเปลี่ยนไป
mattdm

@mattdm: ใช่ขอบคุณสำหรับการยืนยัน! (ฉันสังเกตเห็นคำพูดของคุณ "ที่ใช้งานได้" ช้ากว่าที่ฉันเขียนความคิดเห็นของฉัน)
imz - Ivan Zakharyaschev

3

หากคุณทราบว่าsomefileเป็น symlink คุณสามารถระบุพา ธ เต็มไปยังไฟล์อย่างชัดเจนด้วยสิ่งต่อไปนี้:

perl -pi.bak -e 's/foo/bar/' $(readlink somefile)

ด้วยวิธีนี้ symlink ดั้งเดิมจะยังคงสภาพเดิมเนื่องจากตอนนี้การแทนที่ถูกทำโดยตรงกับไฟล์ต้นฉบับ


แต่ผลลัพธ์ของreadlinkอาจยังคงเป็น symlink อื่น ที่ดีที่สุดที่จะใช้-fหรือ-eที่พร้อมใช้งานหรือzsh's $file:Pหรือ(:P)glob รอบคัดเลือกที่แสดงโดย @ikegamiเพื่อให้ได้เส้นทาง symlink ฟรี
Stéphane Chazelas

3

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

perl -i -pe'...' -- "$qfn"

วิธีแก้ปัญหามีดังต่อไปนี้:

perl -i -pe'...' -- "$( readlink -e -- "$qfn" )"

สิ่งนี้จัดการทั้ง symlink และไม่ใช่ symlinks


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

... | xargs -r perl -i -pe'...' --

วิธีแก้ปัญหามีดังต่อไปนี้:

... | xargs -r readlink -ze -- | xargs -r0 perl -i -pe'...' --

สิ่งนี้จัดการทั้ง symlink และไม่ใช่ symlinks


Caveat readlinkไม่ใช่คำสั่งมาตรฐานดังนั้นการมีอยู่การมีข้อโต้แย้งและฟังก์ชันการทำงานนั้นแตกต่างกันไปตามระบบดังนั้นโปรดตรวจสอบเอกสารของคุณ ตัวอย่างเช่นการใช้งานบางอย่างมี-f(ซึ่งคุณสามารถใช้ที่นี่) แต่ไม่มี-eบางอย่าง busybox's readlink -fพฤติกรรมเช่น readlink -eGNU และระบบบางรายที่มีrealpathคำสั่งแทนreadlinkซึ่งโดยทั่วไปพฤติกรรมเช่น readlink -fGNU

ระวังด้วย GNU การลบreadlink -eลิงก์ที่ไม่สามารถแก้ไขไฟล์ที่มีอยู่จะถูกลบอย่างเงียบ ๆ


@ Stéphane Chazelas $var:Pไม่ได้ผลสำหรับฉัน ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'ผลลัพธ์tmp:P)
ikegami

คุณจำเป็นต้อง zsh 5.3 (2016) $var:Pหรือสูงกว่าสำหรับ ด้วยเวอร์ชันที่เก่ากว่าคุณสามารถใช้$var:Aแทนได้เสมอ ดูคู่มือสำหรับความแตกต่างและzsh.org/mla/users/2016/msg00593.htmlสำหรับเหตุผลสำหรับการแนะนำ:P( realpath()อินเทอร์เฟซจริง)
Stéphane Chazelas

( :Aเพิ่มใน 4.3.10 (2009), GNU readlink -zในปี 2012 ฉันไม่ทราบถึงreadlinkการใช้งานอื่น ๆที่สนับสนุน-z) โปรดทราบว่าด้วย GNU xargsโดยทั่วไปคุณต้องการ-rตัวเลือกเว้นแต่ว่าคุณจะสามารถรับประกันได้ว่าอินพุตจะไม่ว่างเปล่า ที่นี่ไม่ใช่เรื่องใหญ่ (ยกเว้นว่าperlรหัสมีคำสั่งBEGIN/ END) คุณจะได้-i used with no filenames on the command line, reading from STDIN.รับคำเตือนถ้ามันเกิดขึ้น
Stéphane Chazelas

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