Harus ada cara yang lebih baik untuk mengganti baris tunggal saja?

27

Saya memiliki kebiasaan menulis satu baris per kalimat karena saya biasanya mengkompilasi sesuatu ke LaTex, atau saya menulis dalam beberapa format lain di mana jeda baris diabaikan. Saya menggunakan baris kosong untuk menunjukkan awal paragraf baru.

Sekarang, saya memiliki file yang ditulis dengan gaya ini yang ingin saya kirimkan sebagai teks biasa. Saya ingin menghapus semua linebreak tunggal tetapi membiarkan double linebreak utuh. Inilah yang telah saya lakukan:

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

Ini menggantikan baris kosong dengan beberapa teks yang saya yakin tidak muncul dalam file: NEWLINEdan kemudian menghapus semua jeda baris dengan awk (saya menemukan trik itu di beberapa situs web) dan kemudian mengganti huruf NEWLINEs dengan dua linebreak yang diperlukan. .

Ini sepertinya cara yang panjang lebar untuk melakukan hal yang cukup sederhana. Apakah ada cara yang lebih sederhana? Juga, jika ada cara untuk mengganti beberapa ruang (yang terkadang merayap karena suatu alasan) dengan ruang tunggal, itu juga bagus.

Saya menggunakan emacs, jadi jika ada beberapa trik spesifik emacs yang bagus, tetapi saya lebih suka melihat versi sed murni atau versi awk murni.

Seamus
sumber
Maksudmu ^ $, bukan $ ^ di perintah sed pertama.
pengguna tidak diketahui
@ pengguna ya, ya saya lakukan.
Seamus
Cara yang lebih mudah untuk menghapus semua jeda baris: tr -d "\n".
jfg956

Jawaban:

18

Anda dapat menggunakan awk seperti ini:

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

Atau jika Anda membutuhkan baris baru tambahan di akhir:

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

Atau jika Anda ingin memisahkan paragraf dengan baris baru:

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

Perintah awk ini menggunakan tindakan yang dijaga oleh pola:

/regex/

atau

END

Tindakan berikut hanya dijalankan jika polanya cocok dengan garis saat ini.

Dan ^$.karakter memiliki makna khusus dalam ekspresi reguler, di mana ^cocok dengan awal garis, $akhir, dan .karakter yang berubah-ubah.

maxschlepzig
sumber
Ini bagus, meskipun saya lebih suka menjaga garis kosong di antara paragraf. Saya berasumsi Anda bisa melakukan sesuatu seperti ini dengan menambahkan baris baru di suatu tempat di perintah cetak pertama? Juga, apa yang /./dilakukan: tampaknya bertindak seperti dan elseuntuk /^$/pertandingan string, apakah itu benar?
Seamus
1
@ Seamus, tentu - ganti saja cetakan pertama (perbarui jawabannya) - /./ cocok dengan semua baris yang panjangnya paling tidak satu karakter, yaitu pelengkap pola / ^ $ / yang cocok dengan hanya baris kosong.
maxschlepzig
9

Gunakan mode paragraf Awk atau Perl untuk memproses file paragraf demi paragraf, di mana paragraf dipisahkan oleh baris kosong.

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
'

Tentu saja, karena ini tidak mem-parsing (La) TeX, itu akan sangat buruk memutilasi komentar, lingkungan kata demi kata dan sintaks khusus lainnya. Anda mungkin ingin melihat ke DeTeX atau konverter TeX-to-text (La) lainnya.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
8

Solusi Sed

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

Perhatikan, bahwa dalam solusi :aini membuat label dan tidak menggunakana perintah.

Mengganti Beberapa Spasi

Gunakan tr:$ tr -s ' ' <test.text

Steven D
sumber
8

Jika saya mengerti dengan benar, baris kosong menyiratkan dua baris baru berturut-turut \n\n,.

Jika demikian, salah satu solusi yang mungkin untuk menghilangkan semua kejadian tunggal baris baru.

Dalam Perl, pernyataan lookahead adalah salah satu cara untuk mencapai ini:

$ perl -0777 -i -pe 's/\n(?=[^\n])//g' test
  • Itu -0777 bendera efektif slurps seluruh file ke dalam string tunggal
  • -p memberitahu perl untuk mencetak string yang berfungsi secara default
  • -i menentukan pengeditan di tempat
  • Pencocokan global memastikan bahwa semua kejadian garis tunggal tunggal ditangani
Zaid
sumber
Satu masalah yang ada adalah tidak ada spasi di antara kalimat.
Steven D
6

(Menghidupkan kembali pertanyaan kuno)

Ini tampaknya persis seperti apa fmtdan paruntuk format ulang paragraf. Seperti Anda (dan juga seperti banyak program) mereka mendefinisikan batas paragraf sebagai satu (atau lebih) baris kosong. Coba perpip teks Anda melalui salah satu dari ini.

fmt adalah utilitas unix standar dan dapat ditemukan di GNU Coreutils.

paradalah tulisan yang sangat ditingkatkan fmtoleh Adam M. Costello yang dapat ditemukan di http://www.nicemice.net/par/ (juga telah dikemas untuk beberapa distribusi, termasuk debian - Saya mengemasnya untuk debian pada Januari 1996, meskipun ada pengelola baru untuk pkg sekarang.).

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

sedakan menambahkan baris apa pun ke Hruang lama yang berisi setidaknya satu karakter. Segera setelah itu dmenghapus semua dari mereka kecuali mungkin yang terakhir. Satu-satunya garis yang bisa tetap kosong, dan itu ada di garis-garis ini ketika sede xmengubah ruang penahanan dan pola dan menghapus semua \nkarakter garis turunan terakumulasi .

Jika Anda ingin baris yang hanya berisi <tab> atau <spasi> dianggap kosong, ganti /./alamat di atas dengan /[^[:blank:]]/. Untuk juga memeras spasi, lakukan:

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

Setelah melihat perl dan awk contoh-contoh kompak Gilles, saya enggan memposting ini, tetapi saya sudah menjalani latihan ini, dan ini adalah skrip yang berfungsi, yang didokumentasikan dengan cukup; titik ini saja mungkin menarik bagi beberapa .. (sed dengan komentar! :)

Skrip ini menganggap baris kosong sebagai kosong meskipun mengandung spasi.
Beberapa spasi dalam teks diringkas menjadi satu ruang.
Trailing whitespace dihapus dari baris teks. Baris kosong berturut-turut diciutkan menjadi satu baris. Script meninggalkan baris kosong atas dan bawah tetap utuh.

Untuk apa pun selain skrip yang paling sepele, sed dapat ditulis lebih mudah dalam bentuk terstruktur, sebagai file skrip terpisah. Inilah contohnya.

menggunakan
panggilan sintaks regex yang diperluas : $ 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

Catatan:, flushdalam komentar, berarti: mengirim pola-ruang untuk penanganan stdout internal sed. Itu tidak berarti cetakan pasti untuk stdout. Outputnya tergantung pada -nopsi sed . misalnya. yang qperintah berarti flush dan berhenti ... Bandingkan dua potongan ini: echo x |sed -e qcetakan x, echo x |sed -ne qcetakan tidak ada, sedangkan dengan menggunakan pperintah akan mencetak 'x' dua kali atau sekali, tergantung pada -npilihan.

Peter.O
sumber
+1 untuk komentar yang bagus. Saya telah melihat terlalu banyak program tanpa komentar sama sekali.
David Cary
4

Berikut ini sedsolusi lain yang menggabungkan semua garis ke seddalam "ruang pegang" sehingga kami mendapatkan satu string panjang yang akhirnya disalin ke "ruang pola" untuk pencocokan pola.

Karena baris baru akan dipertahankan dalam string panjang terakhir dalam sed"ruang pola", garis kosong dalam hal linebreak ganda [^\n]\n\n[^\n]dapat dicocokkan dan dimodifikasi untuk[^\n]\n[^\n] .

Untuk informasi lebih lanjut, lihat, misalnya dan Pencarian dan Ganti 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
deso
sumber
3

Ini mungkin sekolah tua:

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

Ini akan menampilkan teks Anda rata kiri ( .ad l), dengan panjang garis 80 ( .ll 80). Opsi panjang halaman ( .pl) memberi tahu pemroses teks untuk melakukan padding halaman untuk panjang halaman 1, jadi tidak ada padding halaman.

Jika Anda ingin semua paragraf Anda dalam satu baris, Anda bisa menggunakan banyak untuk .ll:

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

man 7 groff untuk opsi pemformatan lainnya.

jfg956
sumber
1

Di Emacs, saya terkadang menggunakan ini regex:

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

Cara:

ganti setiap baris baru yang diikuti oleh sesuatu yang BUKAN baris baru dengan hanya hal itu, yang mengikuti baris baru Dengan cara itu saya menyingkirkan semua baris baru dalam satu paragraf tetapi menjaga paragraf (double-newlines)

pengguna emacs
sumber
0

Ternyata dengan auto-fill-modeaktif, emacs melakukan pekerjaan yang cukup baik untuk kasus penggunaan sederhana saya hanya dengan M-q...

Seamus
sumber
Rincian tentang apa yang auto-fill-modebergantung pada mode utama apa yang Anda aktifkan.
dmckee