วิธีการเพิ่มบรรทัดใหม่ในตอนท้ายของไฟล์?


190

No newline at end of fileโดยใช้ระบบการควบคุมรุ่นที่ฉันได้รับรำคาญที่เสียงเมื่อต่างกล่าวว่า

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


1
ดูเพิ่มเติมได้ที่/ q / 10082204/155090
RubyTuesdayDONO

1
วิธีการแก้ปัญหาที่ดีด้านล่างที่ฆ่าเชื้อไฟล์ทั้งหมดซ้ำ ตอบโดย @Patrick Oscity
Qwerty

1
นอกจากนี้ยังสะท้อนไบต์ไปยังแฟ้ม

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

คำตอบ:


44

ในการฆ่าเชื้อซ้ำโครงการฉันใช้ oneliner นี้:

git ls-files -z | while IFS= read -rd '' f; do tail -c1 < "$f" | read -r _ || echo >> "$f"; done

คำอธิบาย:

  • git ls-files -zแสดงรายการไฟล์ในที่เก็บ ใช้รูปแบบเผื่อเลือกเป็นพารามิเตอร์เพิ่มเติมซึ่งอาจมีประโยชน์ในบางกรณีหากคุณต้องการ จำกัด การทำงานกับไฟล์ / ไดเรกทอรีบางตัว เป็นทางเลือกที่คุณสามารถใช้find -print0 ...หรือโปรแกรมคล้ายกับรายการแฟ้มได้รับผลกระทบ - เพียงให้แน่ใจว่ามันเปล่งNULรายการ -delimited

  • while IFS= read -rd '' f; do ... done วนซ้ำผ่านรายการจัดการชื่อไฟล์ที่มีช่องว่างและ / หรือบรรทัดใหม่ได้อย่างปลอดภัย

  • tail -c1 < "$f" อ่านอักขระตัวสุดท้ายจากไฟล์

  • read -r _ ออกด้วยสถานะการออกที่ไม่ใช่ศูนย์หากบรรทัดใหม่ต่อท้ายหายไป

  • || echo >> "$f" ผนวกบรรทัดใหม่เข้ากับไฟล์หากสถานะการออกของคำสั่งก่อนหน้านี้ไม่ใช่ศูนย์


คุณสามารถทำเช่นนี้ได้หากคุณต้องการฆ่าเชื้อไฟล์บางส่วนของคุณ:find -name \*.java | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
ต่อ Lundberg

@ StéphaneChazelasข้อเสนอแนะที่ดีจะพยายามรวมนี้เป็นคำตอบของฉัน
Patrick Oscity

@PerLundberg คุณสามารถส่งรูปแบบgit ls-filesที่จะช่วยให้คุณประหยัดจากการแก้ไขไฟล์ที่ไม่ได้ถูกติดตามในการควบคุมเวอร์ชัน
Patrick Oscity

@ StéphaneChazelasการเพิ่มIFS= เพื่อยกเลิกการตั้งค่าตัวคั่นเป็นสิ่งที่ดีเพื่อรักษาช่องว่างโดยรอบ รายการที่ถูกยกเลิกเป็นโมฆะมีความเกี่ยวข้องเฉพาะเมื่อคุณมีไฟล์หรือไดเรกทอรีที่มีบรรทัดใหม่ในชื่อของพวกเขาซึ่งดูเหมือนจะดึงข้อมูลได้ไกล แต่เป็นวิธีที่ถูกต้องมากขึ้นในการจัดการกรณีทั่วไปฉันเห็นด้วย เช่นเดียวกับข้อแม้เล็ก ๆ : -dตัวเลือกที่readจะไม่สามารถใช้ได้ใน POSIX sh
Patrick Oscity

ใช่แล้วzsh / bash ของฉัน ดูการใช้ของฉันtail -n1 < "$f"เพื่อหลีกเลี่ยงปัญหาเกี่ยวกับชื่อไฟล์ที่ขึ้นต้นด้วย-(ใช้tail -n1 -- "$f"ไม่ได้กับไฟล์ที่เรียกว่า-) คุณอาจต้องการชี้แจงว่าคำตอบคือตอนนี้ zsh / bash เฉพาะ
Stéphane Chazelas

202

ไปเลย :

sed -i -e '$a\' file

และอีกทางเลือกหนึ่งสำหรับ OS X sed:

sed -i '' -e '$a\' file

สิ่งนี้จะเพิ่ม\nที่ส่วนท้ายของไฟล์ก็ต่อเมื่อมันยังไม่จบด้วยการขึ้นบรรทัดใหม่ ดังนั้นหากคุณรันสองครั้งจะไม่เพิ่มบรรทัดใหม่:

$ cd "$(mktemp -d)"
$ printf foo > test.txt
$ sed -e '$a\' test.txt > test-with-eol.txt
$ diff test*
1c1
< foo
\ No newline at end of file
---
> foo
$ echo $?
1
$ sed -e '$a\' test-with-eol.txt > test-still-with-one-eol.txt
$ diff test-with-eol.txt test-still-with-one-eol.txt
$ echo $?
0

1
@jwd: จากman sed: $ Match the last line.แต่บางทีมันอาจทำงานได้โดยบังเอิญ โซลูชันของคุณยังใช้งานได้
l0b0

1
โซลูชันของคุณสวยหรูกว่ามากและฉันได้ทดสอบและทำสัญญาแล้ว แต่จะใช้ได้อย่างไร หาก$ตรงกับบรรทัดสุดท้ายเหตุใดจึงไม่เพิ่มบรรทัดใหม่ให้กับสตริงที่มีบรรทัดใหม่อยู่แล้ว
l0b0

27
$มีสองความหมายที่แตกต่างกันอยู่ ภายใน regex เช่นกับฟอร์ม/<regex>/มันมีความหมาย "ตรงกับจุดสิ้นสุดของบรรทัด" ตามปกติ มิฉะนั้นใช้เป็นที่อยู่ sed ให้ความหมายพิเศษ "บรรทัดสุดท้ายในไฟล์" รหัสใช้งานได้เพราะ sed โดยค่าเริ่มต้นจะขึ้นบรรทัดใหม่ต่อท้ายเอาต์พุตหากยังไม่มีอยู่ รหัส "$ a \" เพิ่งพูดว่า "ตรงกับบรรทัดสุดท้ายของไฟล์และไม่ต้องเพิ่มอะไรเลย" แต่โดยนัยแล้ว sed เพิ่มบรรทัดใหม่ให้กับทุกบรรทัดที่ประมวลผล (เช่น$บรรทัดนี้) หากยังไม่มีอยู่
jwd

1
เกี่ยวกับ manpage: ข้อความอ้างอิงที่คุณอ้างถึงนั้นอยู่ในส่วน "ที่อยู่" การวางไว้ข้างใน/regex/นั้นให้ความหมายที่ต่างออกไป manpages ของ FreeBSD นั้นให้ความรู้น้อยกว่านี้ฉันคิดว่า: freebsd.org/cgi/man.cgi?query=sed
jwd

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

39

ไปดูกัน:

$ echo -n foo > foo 
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo

ดังนั้นecho "" >> noeol-fileควรทำเคล็ดลับ (หรือคุณหมายถึงขอให้ระบุไฟล์เหล่านี้และแก้ไขไฟล์เหล่านั้น?)

แก้ไขลบออก""จากจากecho "" >> foo(ดูความคิดเห็นของ @ yuyichao) edit2เพิ่ม""อีกครั้ง ( แต่ดูความคิดเห็นของ @Keith Thompson)


4
""ไม่จำเป็น (อย่างน้อยสำหรับทุบตี) และtail -1 | wc -lสามารถนำมาใช้เพื่อหาไฟล์ได้โดยไม่ต้องขึ้นบรรทัดใหม่ในตอนท้าย
yuyichao

5
@ yuyichao: ""ไม่จำเป็นสำหรับทุบตี แต่ฉันได้เห็นechoการใช้งานที่พิมพ์อะไรเมื่อเรียกโดยไม่มีข้อโต้แย้ง (แม้ว่าไม่มีสิ่งที่ฉันสามารถหาได้ทำตอนนี้) echo "" >> noeol-fileอาจจะแข็งแกร่งกว่าเล็กน้อย printf "\n" >> noeol-fileยิ่งไปกว่านั้น
Keith Thompson

2
@KeithThompson, csh's echoเป็นหนึ่งที่รู้จักกันไปไม่มีอะไรส่งออกเมื่อไม่ได้ผ่านการโต้แย้งใด ๆ แต่ถ้าเราจะให้การสนับสนุนเปลือกหอยที่ไม่ใช่บอร์นเราควรทำให้มันecho ''เป็นแทนที่จะecho ""เป็นecho ""ouput ""<newline>กับrcหรือesเป็นตัวอย่าง
Stéphane Chazelas

1
@ StéphaneChazelas: และtcshไม่เหมือนcsh, พิมพ์ขึ้นบรรทัดใหม่เมื่อเรียกด้วยไม่มีข้อโต้แย้ง - $echo_styleไม่คำนึงถึงการตั้งค่าของ
Keith Thompson

16

edทางออกก็ใช้ วิธีนี้มีผลกับบรรทัดสุดท้ายเท่านั้นและ\nจะหายไป:

ed -s file <<< w

มันเป็นงานเปิดไฟล์สำหรับการแก้ไขผ่านสคริปต์สคริปต์เป็นwคำสั่งเดียวที่เขียนไฟล์กลับไปที่ดิสก์ มันขึ้นอยู่กับประโยคนี้พบในed(1)หน้าคน:

ข้อ จำกัด
       ( ... )

       หากไฟล์ข้อความ (ไม่ใช่ไบนารี) ไม่ได้ถูกยกเลิกด้วยอักขระขึ้นบรรทัดใหม่
       จากนั้น ed จะผนวกหนึ่งในการอ่าน / เขียนมัน ในกรณีของเลขฐานสอง
       ไฟล์ ed ไม่ต่อท้ายบรรทัดใหม่ในการอ่าน / เขียน

1
สิ่งนี้ไม่ได้เพิ่มบรรทัดใหม่ให้ฉัน
Olhovsky

4
ได้ผลสำหรับฉัน มันยังพิมพ์ "Newline ผนวก" (ed-1.10-1 บน Arch Linux)
Stefan Majewsky

12

วิธีที่ง่ายและพกพาได้และเป็นไปตามมาตรฐาน POSIX เพื่อเพิ่มบรรทัดใหม่ที่ขาดหายไปซึ่งเป็นไฟล์ข้อความ:

[ -n "$(tail -c1 file)" ] && echo >> file

วิธีนี้ไม่จำเป็นต้องอ่านไฟล์ทั้งหมด มันสามารถหา EOF และทำงานได้จากที่นั่น

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

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

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

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


1
โปรดทราบว่ามันไม่สามารถใช้งานได้yashหากอักขระตัวสุดท้ายในไฟล์เป็นอักขระแบบหลายไบต์ (ในโลแคล UTF-8 เป็นต้น) หรือหากโลแคลเป็น C และไบต์สุดท้ายในไฟล์มีชุดบิตที่ 8 ด้วยเชลล์อื่น (ยกเว้น zsh), มันจะไม่เพิ่มบรรทัดใหม่หากไฟล์สิ้นสุดในไบต์ NUL (แต่หลังจากนั้นอีกครั้ง, นั่นหมายความว่าอินพุตจะไม่ใช่ข้อความแม้ว่าจะเพิ่มบรรทัดใหม่)
Stéphane Chazelas


1
เป็นไปได้หรือไม่ที่จะเรียกใช้สิ่งนี้สำหรับทุกไฟล์ในโฟลเดอร์และโฟลเดอร์ย่อย?
Qwerty

12

เพิ่มบรรทัดใหม่โดยไม่คำนึงถึง:

echo >> filename

นี่คือวิธีตรวจสอบว่ามีการขึ้นบรรทัดใหม่ที่จุดสิ้นสุดก่อนที่จะเพิ่มอีกอันหรือไม่โดยใช้ Python:

f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f

1
ฉันจะไม่ใช้รุ่นหลามในการวนรอบใด ๆ เนื่องจากเวลาเริ่มต้นช้าลง แน่นอนว่าคุณสามารถทำลูปในไพ ธ อนได้ถ้าคุณต้องการ
Kevin Cox

2
เวลาเริ่มต้นของ Python คือ 0.03 วินาทีที่นี่ คุณคิดว่าเป็นปัญหาหรือไม่?
อเล็กซานเด

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

2
echo ""echo -n '\n'ดูเหมือนว่ามีประสิทธิภาพมากขึ้นกว่า หรือคุณสามารถใช้printf '\n'
Keith Thompson

2
สิ่งนี้ใช้ได้ผลดีสำหรับฉัน
Daniel Gomez Rico

8

ทางออกที่เร็วที่สุดคือ:

[ -n "$(tail -c1 file)" ] && printf '\n' >>file 

  1. มันเร็วจริงๆ
    ในไฟล์ขนาดกลางseq 99999999 >fileจะใช้เวลาหนึ่งวินาที
    โซลูชันอื่น ๆ ใช้เวลานาน:

    [ -n "$(tail -c1 file)" ] && printf '\n' >>file  0.013 sec
    vi -ecwq file                                    2.544 sec
    paste file 1<> file                             31.943 sec
    ed -s file <<< w                             1m  4.422 sec
    sed -i -e '$a\' file                         3m 20.931 sec
  2. ใช้งานได้กับเถ้า, ทุบตี, lksh, mksh, ksh93, attsh และ zsh แต่ไม่ได้ล้าง

  3. ห้ามเปลี่ยนการประทับเวลาไฟล์หากไม่จำเป็นต้องเพิ่มบรรทัดใหม่
    โซลูชันอื่น ๆ ทั้งหมดที่นำเสนอที่นี่เปลี่ยนการประทับเวลาของไฟล์
  4. โซลูชันทั้งหมดข้างต้นเป็น POSIX ที่ถูกต้อง

หากคุณต้องการโซลูชันแบบพกพาเพื่อ yash (และเชลล์อื่น ๆ ทั้งหมดที่ระบุไว้ข้างต้น) มันอาจจะซับซ้อนกว่านี้เล็กน้อย:

f=file
if       [ "$(tail -c1 "$f"; echo x)" != "$(printf '\nx')" ]
then     printf '\n' >>"$f"
fi

7

วิธีที่เร็วที่สุดในการทดสอบว่าไบต์สุดท้ายของไฟล์คือการขึ้นบรรทัดใหม่คือการอ่านไบต์สุดท้ายเท่านั้น tail -c1 fileที่สามารถทำได้ด้วย อย่างไรก็ตามวิธีง่าย ๆ ในการทดสอบว่าค่าไบต์เป็นบรรทัดใหม่หรือไม่ขึ้นอยู่กับการลบเชลล์ตามปกติของการขึ้นบรรทัดใหม่ภายในการขยายคำสั่งล้มเหลว (ตัวอย่าง) ใน yash เมื่ออักขระตัวสุดท้ายในไฟล์เป็น UTF- 8 ค่า

วิธีเชลล์ที่ถูกต้องสอดคล้องกับ POSIX ทั้งหมด (สมเหตุสมผล) เพื่อค้นหาว่าไบต์สุดท้ายของไฟล์เป็นบรรทัดใหม่คือการใช้ xxd หรือ hexdump:

tail -c1 file | xxd -u -p
tail -c1 file | hexdump -v -e '/1 "%02X"'

จากนั้นเปรียบเทียบผลลัพธ์ที่ได้จากด้านบนเพื่อ0Aให้การทดสอบมีประสิทธิภาพ
มันมีประโยชน์ที่จะหลีกเลี่ยงการเพิ่มบรรทัดใหม่ไปยังไฟล์ที่ว่างเปล่า
ไฟล์ที่จะไม่สามารถระบุตัวอักษรสุดท้าย0Aของหลักสูตร:

f=file
a=$(tail -c1 "$f" | hexdump -v -e '/1 "%02X"')
[ -s "$f" -a "$a" != "0A" ] && echo >> "$f"

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

ไม่มีไฟล์ temp ที่จำเป็นและไม่ได้ใช้ ไม่มีผลกระทบต่อการเชื่อมโยง

หากการทดสอบนี้ทำงานสองครั้งจะไม่เพิ่มบรรทัดใหม่


1
@crw ฉันเชื่อว่ามันเพิ่มข้อมูลที่เป็นประโยชน์
sorontar

2
โปรดทราบว่าค่าxxdมิได้hexdumpเป็นสาธารณูปโภค POSIX ใน POSIX toolchest od -An -tx1จะต้องมีค่า hex ของไบต์
Stéphane Chazelas

@ StéphaneChazelasกรุณาโพสต์ว่าเป็นคำตอบ; ฉันได้มาที่นี่มองหาความคิดเห็นนี้หลายครั้งเกินไป :)
เคลวิน

@kelvin ฉันได้อัปเดตคำตอบของฉันแล้ว
Stéphane Chazelas

โปรดทราบว่า POSIX ไม่รับประกันว่าค่าของ LF จะเป็น 0x0a ยังคงมีระบบ POSIX ที่ไม่ใช่ (ระบบที่ใช้ EBCDIC) แม้ว่ามันจะหายากมากในสมัยนี้
Stéphane Chazelas

4

คุณดีกว่าการแก้ไขตัวแก้ไขของผู้ใช้ที่แก้ไขไฟล์ครั้งล่าสุด หากคุณเป็นคนสุดท้ายที่จะแก้ไขไฟล์ - คุณใช้โปรแกรมแก้ไขอะไรฉันเดาข้อความ?


2
เป็นกลุ่มแก้ไขในคำถาม แต่โดยทั่วไปคุณพูดถูกฉันไม่ควรแก้ไข
อาการ

6
สำหรับเสียงเรียกเข้าคุณต้องออกนอกเส้นทางของคุณและทำการเต้นแบบไบนารีไฟล์บนบันทึกเพื่อรับเสียงเรียกเข้าเพื่อไม่เพิ่มบรรทัดใหม่ในตอนท้ายของไฟล์ - เพียงแค่ไม่เต้นรำแบบนั้น หรือไฟล์ที่มีอยู่เพื่อให้ถูกต้องเพียงแค่เปิดพวกเขาในการเป็นกลุ่มและบันทึกไฟล์และเป็นกลุ่มจะ 'แก้ไขฯ ขึ้นบรรทัดใหม่ที่ขาดหายไปสำหรับคุณ (สามารถ scripted ได้อย่างง่ายดายสำหรับหลายไฟล์)
AD7six

3
ฉันemacsจะไม่เพิ่มบรรทัดใหม่เมื่อสิ้นสุดไฟล์
enzotib

2
ขอบคุณสำหรับความคิดเห็น @ AD7six ฉันได้รับรายงาน phantom จากที่ต่างกันเมื่อฉันยืนยันสิ่งต่าง ๆ เกี่ยวกับวิธีที่ไฟล์ต้นฉบับไม่มีการขึ้นบรรทัดใหม่ในตอนท้าย ไม่ว่าฉันจะแก้ไขไฟล์ด้วยเสียงเรียกเข้าแบบใดฉันไม่สามารถทำให้มันขึ้นบรรทัดใหม่ได้ ดังนั้นมันเป็นเพียงการทำมัน
Steven Lu

1
@enzotib: ฉันมี(setq require-final-newline 'ask)ในของฉัน.emacs
Keith Thompson

3

หากคุณต้องการเพิ่มบรรทัดใหม่อย่างรวดเร็วเมื่อประมวลผลไปป์ไลน์ให้ใช้:

outputting_program | { cat ; echo ; }

มันยังสอดคล้องกับ POSIX

แน่นอนว่าคุณสามารถเปลี่ยนเส้นทางไปยังไฟล์ได้


2
ความจริงที่ว่าฉันสามารถใช้สิ่งนี้ในไปป์ไลน์มีประโยชน์ สิ่งนี้ทำให้ฉันสามารถนับจำนวนแถวในไฟล์ CSV ได้โดยไม่รวมส่วนหัว และช่วยให้ได้จำนวนบรรทัดที่ถูกต้องในไฟล์ windows ที่ไม่ได้ขึ้นบรรทัดใหม่หรือขึ้นบรรทัดใหม่ cat file.csv | tr "\r" "\n" | { cat; echo; } | sed "/^[[:space:]]*$/d" | tail -n +2 | wc -l
Kyle Tolle

3

ระบุว่าไม่มีค่า null ในอินพุต:

paste - <>infile >&0

... จะพอเพียงต่อท้ายบรรทัดใหม่ต่อท้ายท้ายของ infile ถ้ามันไม่มีอยู่แล้ว และมันต้องการเพียงอ่านไฟล์อินพุตผ่านครั้งเดียวเพื่อทำให้ถูกต้อง


นั่นจะไม่ทำงานอย่างนั้นเพราะ stdin และ stdout แบ่งปันคำอธิบาย open-file เดียวกัน (ดังนั้นเคอร์เซอร์ภายในไฟล์) คุณต้องการpaste infile 1<> infileแทน
Stéphane Chazelas

2

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

find . -type f | # sort |        # sort file names if you like
/usr/bin/perl -lne '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

สคริปต์ perl อ่านรายการชื่อไฟล์ (เรียงเป็นทางเลือก) จาก stdin และสำหรับทุกไฟล์ที่อ่านเป็นไบต์สุดท้ายเพื่อพิจารณาว่าไฟล์สิ้นสุดในบรรทัดใหม่หรือไม่ มันเร็วมากเพราะหลีกเลี่ยงการอ่านเนื้อหาทั้งหมดของแต่ละไฟล์ มันส่งออกหนึ่งบรรทัดสำหรับแต่ละไฟล์ที่อ่านนำหน้าด้วย "ข้อผิดพลาด:" หากเกิดข้อผิดพลาดบางประเภท "ว่าง:" ถ้าไฟล์นั้นว่างเปล่า (ไม่ลงท้ายด้วยการขึ้นบรรทัดใหม่!), "EOL:" ("ท้ายสุดของ บรรทัด ") หากไฟล์ลงท้ายด้วย newline และ" no EOL: "หากไฟล์ไม่ลงท้ายด้วย newline

หมายเหตุ: สคริปต์ไม่ได้จัดการชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ หากคุณอยู่ในระบบ GNU หรือ BSD คุณสามารถจัดการชื่อไฟล์ที่เป็นไปได้ทั้งหมดโดยเพิ่ม -print0 เพื่อค้นหา, -z เพื่อจัดเรียงและ -0 ถึง Perl เช่นนี้:

find . -type f -print0 | sort -z |
/usr/bin/perl -ln0e '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

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

ผลลัพธ์อาจถูกกรองหากต้องการเพื่อเพิ่มบรรทัดใหม่ให้กับไฟล์เหล่านั้นซึ่งไม่มีไฟล์

 echo >> "$filename"

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

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

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

/usr/bin/perl -ne 'print "$ARGV\n" if /.\z/' -- FILE1 FILE2 ...

1

vi/ vim/ exบรรณาธิการโดยอัตโนมัติเพิ่ม<EOL>ที่ EOF เว้นแต่ไฟล์แล้วมีมัน

ดังนั้นลอง:

vi -ecwq foo.txt

ซึ่งเทียบเท่ากับ:

ex -cwq foo.txt

การทดสอบ:

$ printf foo > foo.txt && wc foo.txt
0 1 3 foo.txt
$ ex -scwq foo.txt && wc foo.txt
1 1 4 foo.txt

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

ทำไมสิ่งนี้จึงสำคัญ เพื่อให้ไฟล์ของเราPOSIX เข้ากันได้


0

วิธีใช้คำตอบที่ยอมรับกับไฟล์ทั้งหมดในไดเรกทอรีปัจจุบัน (รวมถึงไดเรกทอรีย่อย):

$ find . -type f -exec sed -i -e '$a\' {} \;

สิ่งนี้ใช้ได้กับ Linux (Ubuntu) บน OS X คุณอาจต้องใช้-i ''(ยังไม่ทดลอง)


4
โปรดทราบว่าfind .รายการไฟล์ทั้งหมดรวมถึงไฟล์.gitมา วิธียกเว้น:find . -type f -not -path './.git/*' -exec sed -i -e '$a\' {} \;
friederbluemle

หวังว่าฉันจะได้อ่านความคิดเห็น / คิดเกี่ยวกับมันก่อนที่ฉันจะวิ่งมัน โอ้ดี
kstev

0

อย่างน้อยในเวอร์ชัน GNU เพียงgrep ''awk 1ป้อน canonicalizes หรือป้อนให้เพิ่มบรรทัดใหม่สุดท้ายหากยังไม่ปรากฏ พวกเขาจะคัดลอกไฟล์ในกระบวนการซึ่งจะใช้เวลาหากมีขนาดใหญ่ (แต่แหล่งที่มาไม่ควรใหญ่เกินไปที่จะอ่านต่อไปหรือไม่) และอัปเดต modtime เว้นแต่คุณจะทำสิ่งที่ชอบ

 mv file old; grep '' <old >file; touch -r old file

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


หรือเพียงแค่grep '' file 1<> fileว่าจะยังคงอ่านและเขียนไฟล์อย่างเต็มที่
Stéphane Chazelas

-1

สิ่งนี้ใช้ได้ใน AIX ksh:

lastchar=`tail -c 1 *filename*`
if [ `echo "$lastchar" | wc -c` -gt "1" ]
then
    echo "/n" >> *filename*
fi

ในกรณีของฉันหากไฟล์หายไปขึ้นบรรทัดใหม่wcคำสั่งจะคืนค่าเป็น2และเราเขียนขึ้นบรรทัดใหม่


คำติชมจะมาในรูปแบบของขึ้นหรือลงหรือคุณจะถูกถามในความคิดเห็นเพื่อร่างคำตอบ / คำถามของคุณให้มากขึ้นไม่มีจุดถามในเนื้อหาของคำตอบ เก็บไว้ตรงประเด็นยินดีต้อนรับสู่ stackexchange!
k0pernikus

-1

การเพิ่มคำตอบของ Patrick Oscityหากคุณต้องการนำไปใช้กับไดเรกทอรีเฉพาะคุณสามารถใช้:

find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done

เรียกใช้สิ่งนี้ภายในไดเรกทอรีที่คุณต้องการเพิ่มบรรทัดใหม่


-1

echo $'' >> <FILE_NAME> จะเพิ่มบรรทัดว่างที่ท้ายไฟล์

echo $'\n\n' >> <FILE_NAME> จะเพิ่ม 3 บรรทัดว่างในตอนท้ายของไฟล์


StackExchange มีการจัดรูปแบบตลกฉันคงมันสำหรับคุณ :-)
peterh

-1

หากไฟล์ของคุณถูกยุติด้วยการสิ้นสุดบรรทัดWindows\r\nและคุณอยู่ใน Linux คุณสามารถใช้sedคำสั่งนี้ มันจะเพิ่ม\r\nไปยังบรรทัดสุดท้ายหากยังไม่มี:

sed -i -e '$s/\([^\r]\)$/\1\r\n/'

คำอธิบาย:

-i    replace in place
-e    script to run
$     matches last line of a file
s     substitute
\([^\r]\)$    search the last character in the line which is not a \r
\1\r\n    replace it with itself and add \r\n

หากบรรทัดสุดท้ายมีอยู่\r\nแล้ว regexp การค้นหาจะไม่ตรงกันดังนั้นจึงไม่มีอะไรเกิดขึ้น


-1

คุณสามารถเขียนfix-non-delimited-lineสคริปต์เช่น:

#! /bin/zsh -
zmodload zsh/system || exit
ret=0
for file do
  if sysopen -rwu0 -- "$file"; then
    if sysseek -w end -1; then
      read -r x || print -u0
    else
      syserror -p "Can't seek in $file before the last byte: "
      ret=1
    fi
  else
    ret=1
  fi
done
exit $ret

ตรงกันข้ามกับโซลูชันบางตัวที่ให้ไว้ที่นี่

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

คุณสามารถใช้มันเป็นเช่น:

that-script *.txt

หรือ:

git ls-files -z | xargs -0 that-script

POSIXly คุณสามารถทำสิ่งที่เทียบเท่ากับ

export LC_ALL=C
ret=0
for file do
  [ -s "$file" ] || continue
  {
    c=$(tail -c 1 | od -An -vtc)
    case $c in
      (*'\n'*) ;;
      (*[![:space:]]*) printf '\n' >&0 || ret=$?;;
      (*) ret=1;; # tail likely failed
    esac
  } 0<> "$file" || ret=$? # record failure to open
done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.