วิธีลบบรรทัดว่างหลายบรรทัดออกจากไฟล์ได้อย่างไร


14

ฉันมีบางข้อความไฟล์ผมใช้ในการจดบันทึกใน - cat >> fileข้อความธรรมดาเพียงมักจะเพียงแค่ใช้ บางครั้งฉันใช้บรรทัดว่างหรือสอง (แค่กลับ - ตัวอักษรขึ้นบรรทัดใหม่) เพื่อระบุหัวเรื่อง / บรรทัดความคิดใหม่ ในตอนท้ายของแต่ละเซสชันก่อนที่จะปิดไฟล์ด้วยCtrl+ Dฉันมักจะเพิ่มบรรทัดว่าง (5-10) บรรทัด (ปุ่มย้อนกลับ) จำนวนมากเพื่อแยกเซสชัน

เห็นได้ชัดว่านี่ไม่ฉลาดมาก แต่ใช้งานได้สำหรับฉันเพื่อจุดประสงค์นี้ ฉันไม่แต่สิ้นสุดขึ้นกับจำนวนมากและจำนวนบรรทัดที่ว่างเปล่าที่ไม่จำเป็นดังนั้นฉันกำลังมองหาวิธีที่จะลบ (ส่วนใหญ่) สายพิเศษ มีคำสั่ง Linux (ตัด, วาง, grep, ... ?) ที่สามารถใช้โดยตรงกับตัวเลือกไม่กี่? อีกวิธีหนึ่งไม่มีใครมีความคิดสำหรับ sed, awk หรือ perl (ดีในภาษาสคริปต์ใด ๆ จริง ๆ แม้ว่าฉันจะชอบ sed หรือ awk) สคริปต์ที่จะทำสิ่งที่ฉันต้องการ? เขียนอะไรบางอย่างใน C ++ (ซึ่งจริง ๆ แล้วฉันสามารถทำเอง) ดูเหมือนว่าเกินความจริง

กรณี # 1: สิ่งที่ฉันต้องการคือสคริปต์ / คำสั่งที่จะลบบรรทัดว่างที่ต่อเนื่องกันมากกว่าสองบรรทัด (3 หรือมากกว่า) และแทนที่ด้วยบรรทัดว่างสองบรรทัด แม้ว่ามันจะดีถ้ามันสามารถ tweaked เพื่อลบมากกว่าหนึ่งบรรทัด (2 หรือมากกว่า) และ / หรือแทนที่หลายบรรทัดว่างด้วยบรรทัดว่างเพียงหนึ่งบรรทัด

กรณี # 2: ฉันสามารถใช้สคริปต์ / คำสั่งที่จะลบบรรทัดว่างเดี่ยวระหว่างข้อความสองบรรทัด แต่ปล่อยให้บรรทัดว่างหลายบรรทัดเป็นอยู่ (แม้ว่าการลบหนึ่งในบรรทัดว่างนั้นจะยอมรับได้)



2
@ l0b0 นั่นเป็นคำถามที่แตกต่างอย่างสิ้นเชิงโดยสิ้นเชิง (อีกคำถามหนึ่งคือคำถามvimเดียวและเพื่อแทนที่บรรทัดว่างด้วยบรรทัดว่างหนึ่งบรรทัด)
Stéphane Chazelas

คำตอบ:


14

กรณีที่ 1:

awk '!NF {if (++n <= 2) print; next}; {n=0;print}'

กรณีที่ 2:

awk '!NF {s = s $0 "\n"; n++; next}
     {if (n>1) printf "%s", s; n=0; s=""; print}
     END {if (n>1) printf "%s", s}'

+1 สำหรับ awk แทน sed
Rob

เนื่องจากกรณีการใช้งานนี้เกิดขึ้นบ่อยครั้งฉันจึงแนะนำให้สร้างสคริปต์
ChuckCottrill

15

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


6

กรณีที่ 1:

perl -i -ane '$n=(@F==0) ? $n+1 : 0; print if $n<=2'

กรณีที่ 2:

perl -i -ane '$n=(@F==0) ? $n+1 : 0; print $n==2 ? "\n$_" : $n==1 ? "" : $_ '

+1 perl ftw! Awk คือ (อาจ) บัญญัติของเรื่องนี้ แต่ (DRY) บังคับให้ฉันเขียนสคริปต์สำหรับกรณีใช้งานที่ทำซ้ำเช่นนี้
ChuckCottrill

3

คุณสามารถระบุ Case # 1 แบบนี้ด้วย GNU sed:

sed -r ':a; /^\s*$/ {N;ba}; s/( *\n *){2,}/\n\n/'

นั่นคือรวบรวมบรรทัดว่างในพื้นที่รูปแบบและหากมีมากกว่าสามบรรทัดขึ้นไปให้ลดเป็นสองบรรทัด

ในการเข้าร่วมบรรทัดเว้นวรรคเดียวเช่นในกรณีที่ # 2 คุณสามารถทำสิ่งนี้ได้:

sed -r '/^ *\S/!b; N; /\n *$/!b; N; /\S *$/!b; s/\n *\n/\n/'

หรือในรูปแบบความเห็น:

sed -r '
  /^ *\S/!b        # non-empty line
  N                # 
  /\n *$/!b        # followed by empty line
  N                # 
  /\S *$/!b        # non-empty line
  s/\n *\n/\n/     # remove the empty line
'

1

โซลูชันนี้จะดูแลบรรทัดว่างสุดท้ายในไฟล์ด้วย:

sed -r -n '
  /^ *$/!{p;b}  # non-blank line - print and next cycle
  h             # blank line - save it in hold space
  :loop
  $b end        # last line - go to end
  n             # read next line in pattern space
  /^ *$/b loop  # blank line - loop to next one
  :end          # pattern space has non-blank line or last blank line
  /^ *$/{p;b}   # last blank line: print and exit
  H;x;p         # non-blank line: print hold + pattern space and next cycle
'

0

ติดตามคำแนะนำของAnthonเพื่อใช้ "uniq" ...

ลบบรรทัดว่างนำหน้าและทำซ้ำบรรทัดว่าง

# Get large random string.
rand_str=; while [[ ${#rand_str} -lt 40 ]]; do rand_str=$rand_str$RANDOM; done

# Add extra lines at beginning and end of stdin.
(echo $rand_str; cat; echo $rand_str) |

# Convert empty lines to random strings.
sed "s/^$/$rand_str/" |

# Remove duplicate lines.
uniq |

# Remove first and last line.
sed '1d;$d' |

# Convert random strings to empty lines.
sed "s/$rand_str//"

ในหนึ่งบรรทัดยาว:

(rand_str=; while [[ ${#rand_str} -lt 40 ]]; do rand_str=$rand_str$RANDOM; done; (echo $rand_str; cat; echo $rand_str) | sed "s/^$/$rand_str/" | uniq | sed '1d;$d' | sed "s/$rand_str//")

หรือเพียงแค่ใช้ "cat -s"

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

# Add extra blank lines at beginning and end.
# These will be removed in final step.
{ echo; cat; echo; } |

# Replace multiple blank lines with a single blank line.
cat -s |

# Remove first and last line.
sed '1d;$d'

ในบรรทัดเดียว

{ { echo; cat; echo; } | cat -s | sed '1d;$d'; }

0

โซลูชันที่โพสต์ดูเป็นความลับเล็กน้อยสำหรับฉัน นี่คือวิธีแก้ปัญหาใน Python 3.6:

#!/usr/bin/env python3

from pathlib import Path                                                                                                                                                              
import sys                                                                                                                                                                            
import fileinput                                                                                                                                                                      


def remove_multiple_blank_lines_from_file(path, strip_right=True): 
    non_blank_lines_out_of_two_last_lines = [True, True] 
    for line in fileinput.input(str(path), inplace=True): 
        non_blank_lines_out_of_two_last_lines.pop(0) 
        non_blank_lines_out_of_two_last_lines.append(bool(line.strip())) 
        if sum(non_blank_lines_out_of_two_last_lines) > 0: 
            line_to_write = line.rstrip() + '\n' if strip_right else line 
            sys.stdout.write(line_to_write)


def remove_multiple_blank_lines_by_glob(rglob='*', path=Path('.'), strip_right=True): 
    for p in path.rglob(rglob): 
        if p.is_file(): 
            try:
                remove_multiple_blank_lines_from_file(p, strip_right=strip_right)
            except Exception as e:
                print(f"File '{p}' was not processed due the error: {e}")


if __name__ == '__main__':
    remove_multiple_blank_lines_by_glob(sys.argv[1], Path(sys.argv[2]), next(iter(sys.argv[3:]), None) == '--strip-right')

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

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