Ci deve essere un modo migliore per sostituire solo le nuove righe?


27

Ho l'abitudine di scrivere una riga per frase perché in genere compilo cose su LaTex o scrivo in qualche altro formato in cui le interruzioni di riga vengono ignorate. Uso una riga vuota per indicare l'inizio di un nuovo paragrafo.

Ora, ho un file scritto in questo stile che vorrei solo inviare come testo normale. Voglio rimuovere tutte le singole interruzioni di riga ma lasciare intatte le doppie interruzioni di riga. Questo è quello che ho fatto:

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

Questo sostituisce le righe vuote con un po 'di testo che sono sicuro che non appare nel file: NEWLINEe quindi elimina tutte le interruzioni di riga con awk (ho trovato quel trucco su alcuni siti Web) e quindi sostituisce le NEWLINEs con le due interruzioni di riga richieste .

Sembra un modo lungo e tortuoso di fare una cosa abbastanza semplice. c'è un modo più facile? Inoltre, se ci fosse un modo per sostituire più spazi (che a volte si insinuano per qualche motivo) con spazi singoli, sarebbe anche bene.

Uso emacs, quindi se c'è qualche trucco specifico di emacs che va bene, ma preferirei vedere una versione pura di sed o pura awk.


Intendevi ^ $, non $ ^ nel primo comando sed.
utente sconosciuto

@utente sì, sì l'ho fatto.
Seamus,

Un modo più semplice per rimuovere tutte le interruzioni di riga: tr -d "\n".
jfg956,

Risposte:


18

Puoi usare awk in questo modo:

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

O se hai bisogno di una nuova riga aggiuntiva alla fine:

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

O se vuoi separare i paragrafi da una nuova riga:

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

Questi comandi awk fanno uso di azioni protette da schemi:

/regex/

o

END

Un'azione seguente viene eseguita solo se il modello corrisponde alla riga corrente.

E i ^$.personaggi hanno un significato speciale nelle espressioni regolari, dove ^corrisponde l'inizio della riga, $la fine e .un carattere arbitrario.


Questo va bene, anche se preferirei mantenere la linea vuota tra i paragrafi. Suppongo che potresti fare qualcosa del genere aggiungendo una nuova riga in più da qualche parte nel primo comando di stampa? Inoltre, cosa sta /./facendo: sembra che si stia comportando come e elseper la /^$/corrispondenza delle stringhe, giusto?
Seamus,

1
@Seamus, certo - basta sostituire la prima stampa (aggiornata la risposta) - /./ corrisponde a tutte le righe che contengono almeno un carattere, ovvero il complemento del modello / ^ $ / che corrisponde solo a righe vuote.
maxschlepzig,

9

Utilizzare la modalità paragrafo di Awk o Perl per elaborare un file paragrafo per paragrafo, in cui i paragrafi sono separati da righe vuote.

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
'

Naturalmente, poiché questo non analizza (La) TeX, orribilmente mutilerà commenti, ambienti testuali e altre sintassi speciali. Potresti voler esaminare DeTeX o altri convertitori TeX-to-text (La).


8

Soluzione Sed

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

Si noti che in questa soluzione :aviene creata un'etichetta e non si utilizza il acomando.

Sostituzione di più spazi

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


8

Se ho capito bene, una riga vuota implica due nuove righe consecutive, \n\n.

In tal caso, una possibile soluzione sarebbe quella di eliminare tutte le singolari occorrenze di newline.

In Perl, un'asserzione lookahead è un modo per raggiungere questo obiettivo:

$ perl -0777 -i -pe 's/\n(?=[^\n])//g' test
  • Il -0777flag effettivamente assorbe l'intero file in una singola stringa
  • -p dice a perl di stampare la stringa su cui sta lavorando di default
  • -i specifica la modifica sul posto
  • La corrispondenza globale garantisce che tutte le singole occorrenze di nuova riga vengano gestite

Un problema che questo ha è che non ci sono spazi tra le frasi.
Steven D,

6

(ravvivare una domanda antica)

Questo sembra essere esattamente ciò fmtche parserve per la riformattazione dei paragrafi. Come te (e anche come molti programmi) definiscono i limiti di paragrafo come una (o più) righe vuote. Prova a reindirizzare il tuo testo attraverso uno di questi.

fmt è un'utilità unix standard e può essere trovata in GNU Coreutils.

parè un testo fortemente migliorato fmtscritto da Adam M. Costello che può essere trovato su http://www.nicemice.net/par/ (è stato anche impacchettato per diverse distribuzioni, tra cui debian - l'ho confezionato per debian nel gennaio 1996, anche se ora c'è un nuovo manutentore per il pkg.).


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

sedaggiungerà qualsiasi riga al Hvecchio spazio che contiene almeno un singolo carattere. Subito dopo delimina tutti quelli tranne forse l'ultimo. Le uniche righe che possono rimanere sono gli spazi vuoti, ed è su queste righe quando sede xcambia gli spazi di attesa e di pattern ed elimina tutti i \ncaratteri di ewline accumulati .

Se si desidera che le righe che contengono solo <tabs> o <spaces> siano considerate vuote, sostituire l' /./indirizzo sopra con /[^[:blank:]]/. Per stringere anche gli spazi fare:

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

5

Dopo aver visto il perl e gli imbarazzanti esempi di Gilles, ero riluttante a pubblicarlo, ma avevo già svolto l'esercizio ed è una sceneggiatura funzionante, che è ragionevolmente documentata; questo punto da solo potrebbe essere di interesse per alcuni .. (sed con commenti! :)

Questo script considera le righe vuote vuote anche se contengono spazi bianchi.
Più spazi nel testo sono condensati in un unico spazio.
Gli spazi vuoti finali vengono rimossi dalle righe di testo. Le righe vuote consecutive vengono compresse in un'unica riga. Lo script lascia intatte le righe vuote superiore e inferiore.

Per qualcosa di più degli script più banali, sed può essere scritto molto più facilmente in una forma strutturata, come un file di script separato. Ecco un esempio.

usando la
chiamata di sintassi regex estesa : $ 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

Nota: flushnei commenti significa: inviare lo spazio-pattern alla gestione stdout interna di sed. Non significa una stampa definita su stdout. L'output dipende -ndall'opzione sed . per esempio. il qcomando significa flush ed esci ... Confronta questi due frammenti: echo x |sed -e qstampa x, echo x |sed -ne qnon stampa nulla, mentre usando il pcomando si stampa 'x' due volte o una volta, a seconda -ndell'opzione.


+1 per buoni commenti. Ho visto troppi programmi senza commenti.
David Cary,

4

Ecco un'altra sedsoluzione che concatena tutte le linee nello sed"spazio di attesa" in modo da ottenere una lunga stringa che alla fine viene copiata nello "spazio modello" per la corrispondenza dei motivi.

Poiché le nuove righe verranno conservate nella stringa lunga finale nello sed"spazio modello", è [^\n]\n\n[^\n]possibile abbinare e modificare le righe vuote in termini di doppie interruzioni [^\n]\n[^\n].

Per ulteriori informazioni, ad esempio, sed e Ricerca su più righe e Sostituisci .

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

Questa potrebbe essere la vecchia scuola:

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

Questo produrrà il tuo testo allineato a sinistra ( .ad l), con una lunghezza della linea di 80 ( .ll 80). L'opzione lunghezza pagina ( .pl) indica all'elaboratore di testi di eseguire il riempimento pagina per una lunghezza pagina pari a 1, quindi nessuna riempimento pagina.

Se desideri tutti i tuoi paragrafi su un'unica riga, puoi utilizzare un numero elevato per .ll:

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

man 7 groff per ulteriori opzioni di formattazione.


1

In Emacs, a volte uso questo regex:

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

Si intende:

sostituire ogni newline seguita da qualcosa che NON è una newline con solo la cosa, che ha seguito la newline In questo modo mi libero di tutte le newline all'interno di un paragrafo ma mantengo i paragrafi (double-newline)


0

Si scopre che con auto-fill-modeon, emacs fa un ottimo lavoro per i miei semplici casi d'uso con solo M-q...


I dettagli di ciò auto-fill-modeche dipende da quale modalità principale hai attivo.
dmckee,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.