ฉันสามารถทำการ `ตัด 'เปลี่ยนไฟล์ได้หรือไม่?


คำตอบ:


14

คุณทำไม่ได้ ใช้ ed หรือ GNU sed หรือ perl หรือทำสิ่งที่พวกเขาทำอยู่เบื้องหลังซึ่งก็คือการสร้างไฟล์ใหม่สำหรับเนื้อหา

edแบบพกพา:

ed foo <<EOF
1,$s/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/
w
q
EOF

GNU sed:

sed -i -e 's/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/' foo

Perl:

perl -i -l -F, -pae 'print @F[1,3]' foo

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

cut -d , -f 1,3 <foo >foo.new &&
mv -f foo.new foo

cutแทนที่ไฟล์ในสถานที่ (เก็บความเป็นเจ้าของและสิทธิ์ของfooแต่ต้องการการป้องกันจากการขัดจังหวะ):

cp -f foo foo.old &&
cut -d , -f 1,3 <foo.old >foo &&
rm foo.old

ผมขอแนะนำให้ใช้หนึ่งในcutวิธีชั่น ด้วยวิธีนี้คุณไม่ต้องพึ่งพาเครื่องมือที่ไม่ได้มาตรฐานใด ๆ คุณสามารถใช้เครื่องมือที่ดีที่สุดสำหรับงานและควบคุมพฤติกรรมการขัดจังหวะ


ดีกว่า.oldวิธีการเปลี่ยนแปลงแบบแทนที่echo "$(cut -d , -f 1,3 <foo)" > foo
GypsyCosmonaut

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

oO ขอบคุณฉันไม่รู้ว่าอาจมีปัญหากับ null byte
GypsyCosmonaut

ฉันคิดว่ามันง่ายกว่า:cut -d , -f 1,3 foo > foo.new rm foo mv foo.new foo
LoMaPh

@LoMaPh ที่จริงฉันไม่รู้ว่าทำไมฉันจึงเปลี่ยนชื่อไฟล์เก่า: มันไม่มีประโยชน์อะไรเลยในการเปลี่ยนชื่อไฟล์ใหม่ rm fooนอกจากนี้ยังเป็นที่เรียบง่ายเพราะคุณไม่จำเป็นต้องมีขั้นตอน และคุณไม่ควรโทรหาrm fooเพราะmv foo.new fooมันเป็นอะตอมมิกมันจะลบเวอร์ชันเก่าออกและวางเวอร์ชันใหม่ไว้ในเวลาเดียวกัน
Gilles 'หยุดชั่วร้าย'

23

moreutilsแพคเกจจากอูบุนตู (และเดเบียน ) มีโปรแกรมที่เรียกว่าspongeซึ่งการจัดเรียงของยังแก้ปัญหาของคุณ

จากฟองน้ำมนุษย์:

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

ซึ่งจะช่วยให้คุณทำสิ่งที่ชอบ:

cut -d <delim> -f <fields> somefile | sponge somefile

9

ฉันไม่คิดว่าเป็นไปได้ที่จะใช้cutคนเดียว ฉันหามันไม่เจอในหน้า man หรือ info คุณสามารถทำอะไรบางอย่างเช่น

mytemp=$(mktemp) && cut -d" " -f1 file > $mytemp && mv $mytemp file

mktempทำให้คุณเป็นไฟล์ชั่วคราวที่ค่อนข้างปลอดภัยที่คุณสามารถไปป์cutเอาท์พุทเข้า


1

ลองเป็นกลุ่ม:

$ ex -s +'%!cut -c 1-10' -cxa file.txt

วิธีนี้จะแก้ไขไฟล์ในตำแหน่ง (ดังนั้นควรสำรองข้อมูลก่อน)

อีกวิธีหนึ่งคือใช้grep, หรือsedgawk



0

เนื่องจากcutผลผลิตน้อยกว่าที่อ่านดังนั้นคุณสามารถ:

cut -c1 < file 1<> file

นั่นคือทำให้ stdin fileเปิดในโหมดอ่านอย่างเดียวและ stdout fileเปิดในโหมดอ่าน + เขียนโดยไม่มีการตัดทอน ( <>)

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

foo
bar

ผลลัพธ์จะกลายเป็น:

f
b
bar

f\nb\nได้เปลี่ยนfoo\nแต่barยังคงมี คุณต้องตัดทอนไฟล์หลังจากcutเสร็จสิ้น

ด้วยksh93, คุณสามารถทำได้ด้วย<>;โอเปอเรเตอร์ซึ่งทำหน้าที่เหมือน<>ยกเว้นว่าถ้าคำสั่งสำเร็จ, ftruncate()จะถูกเรียกบน file descriptor ดังนั้น:

cut -c1 < file 1<>; file

ด้วยกระสุนอื่น ๆ คุณต้องทำftruncate()ผ่านวิธีอื่นเช่น:

{ cut -c1 < file; perl -e 'truncate STDOUT, tell STDOUT';} 1<> file

แม้ว่าการกล่าวอ้างperlเพียงอย่างเดียวนั้นค่อนข้างเกินความจำเป็นโดยเฉพาะอย่างยิ่งเมื่อพิจารณาว่าperlสามารถทำงานได้อย่างง่ายดายcutเช่น:

perl -pi -e '$_ = substr($_, 0, 1)' file

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

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