จะต้องมีวิธีที่ดีกว่าในการแทนที่บรรทัดใหม่เดียวเท่านั้นหรือ


27

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

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

sed 's/$^/NEWLINE/' file.txt | awk '{printf "%s ",$0}' | sed 's/NEWLINE/\n\n/g' > linebreakfile.txt

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

ดูเหมือนว่าจะเป็นวิธีที่ยืดยาวในการทำสิ่งที่เรียบง่าย มีวิธีที่ง่ายกว่านี้ไหม? นอกจากนี้หากมีวิธีการแทนที่ช่องว่างหลาย ๆ ช่อง (ซึ่งบางครั้งอาจคืบไปด้วยเหตุผลบางอย่าง) ด้วยช่องว่างเดียวนั่นก็จะดีเช่นกัน

ฉันใช้ emacs ดังนั้นหากมีเคล็ดลับเฉพาะของ emac ที่ดี แต่ฉันอยากเห็นรุ่นที่บริสุทธิ์หรือรุ่น awk ที่บริสุทธิ์


คุณหมายถึง ^ $ ไม่ใช่ $ ^ ในคำสั่ง sed แรก
ผู้ใช้ไม่รู้จัก

@ ผู้ใช้ใช่ใช่ฉันทำ
เชมัส

tr -d "\n"วิธีที่ง่ายกว่าที่จะเอาทั้งหมดแบ่งบรรทัด:
jfg956

คำตอบ:


18

คุณสามารถใช้ awk แบบนี้:

$ awk ' /^$/ { print; } /./ { printf("%s ", $0); } ' test

หรือถ้าคุณต้องการขึ้นบรรทัดใหม่ตอนท้าย:

$ awk ' /^$/ { print; } /./ { printf("%s ", $0); } END { print ""; } ' test

หรือถ้าคุณต้องการแยกย่อหน้าด้วยบรรทัดใหม่:

$ awk ' /^$/ { print "\n"; } /./ { printf("%s ", $0); } END { print ""; } ' test

คำสั่ง awk เหล่านี้ใช้ประโยชน์จากการกระทำที่ได้รับการปกป้องโดยรูปแบบ:

/regex/

หรือ

END

การดำเนินการต่อไปนี้จะดำเนินการเฉพาะเมื่อรูปแบบตรงกับบรรทัดปัจจุบัน

และ^$.ตัวละครมีความหมายพิเศษในการแสดงออกปกติซึ่ง^ตรงกับจุดเริ่มต้นของบรรทัด$สิ้นสุดและ.ตัวละครโดยพลการ


สิ่งนี้ดีแม้ว่าฉันต้องการเก็บบรรทัดว่างไว้ระหว่างย่อหน้า ฉันคิดว่าคุณสามารถทำสิ่งนี้โดยเพิ่มบรรทัดใหม่ที่อื่นในคำสั่งพิมพ์แรก? นอกจากนี้สิ่งที่กำลัง/./ทำ: ดูเหมือนว่าจะทำหน้าที่เหมือนและelseสำหรับการ/^$/จับคู่สายที่ถูกต้อง?
เชมัส

1
@Seamus แน่นอน - เพียงแทนที่การพิมพ์ครั้งแรก (อัปเดตคำตอบ) - /./ จับคู่ทุกบรรทัดที่มีความยาวอย่างน้อยหนึ่งตัวอักษรนั่นคือส่วนเติมเต็มของ / ^ $ / รูปแบบที่ตรงกับบรรทัดว่างเท่านั้น
maxschlepzig

9

ใช้โหมดย่อหน้าของ Awk หรือ Perl เพื่อประมวลผลย่อหน้าไฟล์ตามย่อหน้าโดยที่ย่อหน้าถูกคั่นด้วยบรรทัดว่าง

awk -vRS= '
  NR!=1 {print ""}      # print blank line before every record but the first
  {                     # do this for every record (i.e. paragraph):
    gsub(" *\n *"," "); # replace newlines by spaces, compressing spaces
    sub(" *$","");      # remove spaces at the end of the paragraph
    print
  }
'
perl -000 -pe '             # for every paragraph:
  print "\n" unless $.==1;  # print a blank line, except before the first paragraph
  s/ *\n *(?!$)/ /g;        # replace newlines by spaces, compressing spaces, but not at the end of the paragraph
  s/ *\n+\z/\n/             # normalize the last line end of the paragraph
'

แน่นอนเนื่องจากสิ่งนี้ไม่ได้แยกวิเคราะห์ (La) TeX มันจะทำให้เสียความคิดเห็นอย่างน่ากลัวสภาพแวดล้อมแบบคำต่อคำและไวยากรณ์พิเศษอื่น ๆ คุณอาจต้องการดูในDeTeXหรือตัวแปลง TeX เป็นข้อความตัวอักษรอื่น ๆ (La)


8

วิธีการแก้ปัญหา Sed

$ sed -e ':a;N;$!ba;s/\(.\)\n/\1 /g' -e 's/\n/\n\n/' test.text

โปรดทราบว่าในโซลูชันนี้:aกำลังสร้างป้ายกำกับและไม่ได้ใช้aคำสั่ง

แทนที่ช่องว่างหลายช่อง

การใช้tr:$ tr -s ' ' <test.text


8

\n\nถ้าฉันได้เข้าใจอย่างถูกต้องบรรทัดว่างหมายถึงสองบรรทัดใหม่ต่อเนื่อง

ถ้าเป็นเช่นนั้นวิธีแก้ปัญหาที่เป็นไปได้วิธีหนึ่งคือการกำจัดการขึ้นบรรทัดใหม่ของเอกพจน์ทั้งหมด

ใน Perl การอ้างสิทธิ์ lookahead เป็นวิธีหนึ่งในการบรรลุเป้าหมายนี้:

$ perl -0777 -i -pe 's/\n(?=[^\n])//g' test
  • การ-0777ตั้งค่าสถานะ slurps ไฟล์ทั้งหมดเป็นสตริงเดียวได้อย่างมีประสิทธิภาพ
  • -p บอก Perl เพื่อพิมพ์สตริงมันทำงานบนโดยค่าเริ่มต้น
  • -i ระบุการแก้ไขในสถานที่
  • การจับคู่ส่วนกลางทำให้แน่ใจได้ว่ามีการดำเนินการขึ้นบรรทัดใหม่ทั้งหมดเดียว

ปัญหาหนึ่งที่เกิดขึ้นคือไม่มีช่องว่างระหว่างประโยค
Steven D

6

(ฟื้นฟูคำถามโบราณ)

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

fmt เป็นยูทิลิตี้ยูนิกซ์มาตรฐานและสามารถพบได้ใน GNU Coreutils

parเป็นอย่างมากเพิ่มfmtเขียนโดยอดัมเอ็มคอสเตลโลซึ่งสามารถพบได้ที่http://www.nicemice.net/par/ (มันยังได้รับการบรรจุสำหรับการกระจายหลายแห่งรวมถึงเดเบียน - ฉันมันสำหรับแพคเกจเดเบียนมกราคม 1996, แม้ว่าจะมีผู้ดูแลใหม่สำหรับ pkg ในขณะนี้)


6
sed -e'/./{H;$!d;}' -e'x;s/\n//g'

sedจะต่อท้ายบรรทัดใด ๆ กับHพื้นที่เก่าซึ่งมีอักขระอย่างน้อยหนึ่งตัว มันจะกำจัดdพวกมันทั้งหมดทันทียกเว้นบางทีสุดท้าย บรรทัดเดียวที่สามารถคงอยู่คือช่องว่างและอยู่บนบรรทัดเหล่านี้เมื่อsede xเปลี่ยนช่องว่างการพักและรูปแบบและลบ\nอักขระ ewline ที่สะสมทั้งหมด

ถ้าคุณต้องการเส้นซึ่งมีเพียง<แท็บ>หรือ<เว้นวรรค>ได้รับการพิจารณาว่างเปล่าเปลี่ยนที่อยู่ข้างต้นด้วย/./ /[^[:blank:]]/หากต้องการบีบช่องว่างให้ทำ:

 sed -e'/./{H;$!d;}'    \
     -e'x;s/\n//g'      \
     -e's/\([[:blank:]]\)*/\1/g'

5

หลังจากเห็นตัวอย่างที่กะทัดรัดและ Gilles 'Gilles ฉันก็ลังเลที่จะโพสต์สิ่งนี้ แต่ฉันได้ผ่านการฝึกมาแล้วและมันก็เป็นสคริปต์ที่ใช้งานได้ซึ่งมีการบันทึกไว้อย่างสมเหตุสมผล จุดนี้เพียงอย่างเดียวอาจเป็นที่สนใจของบางคน .. (มีความคิดเห็น! :)

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

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

ใช้การ
เรียกใช้ไวยากรณ์ regex เพิ่มเติม: $ sed -rf script text-file

  :first-empty-line
  #================
  /^[[:space:]]*$/ { # if pattern-space is empty...
      $q  # last line # flush-quit 
      n   # pattern-flush=nextline-continue

      :subsequent-empty-line
      #=====================
      /^[[:space:]]*$/ { # if pattern-space is empty...
          $d        # last line # pattern-delete-cycle
          N         # pattern+=nl+nextline
          s/.*\n//  # scrap the leading 'blank' line
          t subsequent-empty-line # branch-on-substitute
      }
  }

  :text-line
  #=========
  $q                       # last line # flush-quit 
  s/^(.*)[[:space:]]*/\1/  # trim trailing whitespace
  s/ +/ /g                 # condense mulltiple spaces
  N                        # pattern+=nl+nextline
  /^.*\n[[:space:]]*$/ { # if newly-read line is blank 
      P          # pattern-first-line-print
      s/^.*\n//  # remove the leading 'text' line
      t first-empty-line   # branch-on-substitute
  }
  # read line is text
  s/\n/ /      # replace \n with a space
  t text-line  # branch-on-substitute

หมายเหตุ: flushในความคิดเห็นหมายถึง: ส่ง pattern-space ไปยังการจัดการ stdout ภายในของ sed ไม่ได้หมายถึงการพิมพ์ที่แน่นอนไปยัง stdout ผลลัพธ์ขึ้นอยู่กับ-nตัวเลือกของ sed เช่น. qคำสั่งวิธีการล้างและเลิก ... เปรียบเทียบทั้งสองตัวอย่าง: echo x |sed -e qพิมพ์ x, echo x |sed -ne qพิมพ์อะไรในขณะที่การใช้pคำสั่งจะพิมพ์ 'x' สองครั้งหรือครั้งขึ้นอยู่กับ-nตัวเลือก


+1 สำหรับความคิดเห็นที่ดี ฉันเคยเห็นโปรแกรมมากเกินไปโดยไม่มีความเห็นเลย
David Cary

4

ต่อไปนี้เป็นอีกsedโซลูชันที่เชื่อมต่อทุกบรรทัดในsed"พื้นที่พัก" เพื่อให้เราได้รับสตริงยาวหนึ่งอันซึ่งในที่สุดก็ถูกคัดลอกไปยัง "พื้นที่ว่างรูปแบบ" เพื่อจับคู่รูปแบบ

ขณะที่การขึ้นบรรทัดใหม่จะได้รับการเก็บรักษาไว้ในสายยาวสุดท้ายในsed'พื้นที่รูปแบบ' ของเส้นที่ว่างเปล่าในแง่ของการแบ่งบรรทัดคู่สามารถจับคู่และการแก้ไขให้[^\n]\n\n[^\n][^\n]\n[^\n]

สำหรับข้อมูลเพิ่มเติมโปรดดูตัวอย่างเช่นsed และ Multi-Line ค้นหาและแทนที่

text='
line 1

line 2
line 3





line 4


line     5



line 6
line 7

line 8
'

# FreeBSD sed
# first sed deletes first / last line if empty and squeezes multiple spaces
printf '%s' "$text" |
sed -e '1{/^$/d;}' -e '${/^$/d;}' -e '/[[:space:]]\{2,\}/s// /g' | 
sed -n -e '1h;1!H;${;g;/\([^[:cntrl:]]\)\n\n\([^[:cntrl:]]\)/s//\1\
\2/g;p;}' |
nl -b a


# GNU sed
# alternative using ...;x;... instead of ...;g;...
# cf. man sed | less -p '\]x'
printf '%s' "$text" |
gsed -e '1{/^$/d;}' -e '${/^$/d;}' -e '/[[:space:]]\{2,\}/s// /g' | 
gsed -E -n '1h;1!H;${;x;/([^\n])\n\n([^\n])/s//\1\
\2/g;p;}' | 
nl -b a


# remove all the single linebreaks but leave the double linebreaks intact
printf '%s' "$text" | 
   sed -n -e '1h;1!H;${;g;/\([^[:cntrl:]]\)\n\([^[:cntrl:]]\)/s//\1 \2/g;p;}' | 
   nl -b a

3

นี่อาจเป็นโรงเรียนเก่า:

(echo ".pl 1" ; echo ".ll 80" ; echo ".ad l" ; cat your_file) | nroff

การดำเนินการนี้จะส่งข้อความของคุณชิดซ้าย ( .ad l) โดยมีความยาวบรรทัด 80 ( .ll 80) ตัวเลือกความยาวหน้า ( .pl) บอกให้ตัวประมวลผลข้อความทำการขยายหน้าสำหรับความยาวของหน้า 1 ดังนั้นจึงไม่มีการขยายหน้า

หากคุณต้องการย่อหน้าทั้งหมดของคุณในบรรทัดเดียวคุณสามารถใช้จำนวนมากสำหรับ.ll:

(echo ".pl 1" ; echo ".ll 1000000" ; echo ".ad l" ; cat your_file) | nroff

man 7 groffสำหรับตัวเลือกการจัดรูปแบบเพิ่มเติม


1

ใน Emacs บางครั้งฉันใช้สิ่งนี้regex:

^J\([^^J]\) -> \1

หมายถึง:

แทนที่ทุกบรรทัดใหม่ที่ตามด้วยบางสิ่งที่ไม่ใช่บรรทัดใหม่ด้วยสิ่งเดียวที่ตามบรรทัดใหม่นั่นคือวิธีที่ฉันจะกำจัดบรรทัดใหม่ทั้งหมดภายในย่อหน้า แต่เก็บย่อหน้า (ขึ้นบรรทัดใหม่)


0

ปรากฎว่าauto-fill-modeเมื่อเปิด emacs ก็ทำได้ดีทีเดียวสำหรับเคสที่ใช้งานง่ายของฉันที่มีเพียงM-q...


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