Come rimuovere più righe vuote da un file?


14

Ho alcuni file di testo che uso per prendere appunti - solo testo normale, di solito solo usando cat >> file. Di tanto in tanto uso una o due righe vuote (solo ritorno - il carattere di nuova riga) per specificare un nuovo soggetto / linea di pensiero. Alla fine di ogni sessione, prima di chiudere il file con Ctrl+ D, in genere aggiungo molte (5-10) righe vuote (tasto invio) solo per separare le sessioni.

Questo ovviamente non è molto intelligente, ma funziona per me a questo scopo. Io faccio comunque fine-up con un sacco e un sacco di righe vuote non necessarie, quindi sto cercando un modo per rimuovere (la maggior parte) le linee extra. Esiste un comando Linux (cut, paste, grep, ...?) Che potrebbe essere usato direttamente con alcune opzioni? In alternativa, qualcuno ha un'idea per una sceneggiatura sed, awk o perl (bene in qualsiasi linguaggio di scripting, anche se preferirei sed o awk) che farebbe quello che voglio? Scrivere qualcosa in C ++ (cosa che potrei davvero fare da solo), sembra eccessivo.

Caso n. 1: ho bisogno di uno script / comando che rimuova più di due (3 o più) righe vuote consecutive e le sostituisca con solo due righe vuote. Sebbene sarebbe bello se anche potesse essere ottimizzato per rimuovere più di una riga (2 o più) e / o sostituire più righe vuote con una sola riga vuota.

Caso n. 2: potrei anche usare uno script / comando che rimuova una singola riga vuota tra due righe di testo, ma lasci più righe vuote così com'è (anche se rimuovere una delle righe vuote sarebbe accettabile).



2
@ l0b0, questa è una domanda completamente diversa (l'altra era una vime doveva sostituire le righe vuote con una riga vuota).
Stéphane Chazelas,

Risposte:


14

Caso 1:

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

Caso 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 per awk invece di sed
Rob,

Poiché questo caso d'uso viene ripetuto frequentemente, suggerirei di creare uno script.
ChuckCottrill,

15

Puoi utilizzare uniqper comprimere più istanze di righe vuote in una riga vuota, ma comprimerà anche le righe che contengono testo se sono uguali e una sotto l'altra.


6

Caso 1:

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

Caso 2:

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

+1 perl ftw! Awk è (probabilmente) canonico per questo, ma (DRY) mi costringe a scrivere script per casi d'uso che si ripetono in questo modo.
ChuckCottrill,

3

È possibile risolvere il caso n. 1 in questo modo con GNU sed:

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

Cioè, raccogli linee vuote nello spazio del motivo e, se ci sono più di tre o più linee, riducilo a due linee.

Per unire le linee a spaziatura singola, come nel caso n. 2, puoi farlo in questo modo:

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

O in forma commentata:

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

Questa soluzione si occupa anche delle ultime righe vuote nel file:

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

Seguendo il suggerimento di Anthon di usare "uniq" ...

Rimuovere le righe vuote iniziali, finali e duplicate.

# 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//"

In una lunga fila:

(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//")

O semplicemente usa "cat -s".

Sono passato dalla parentesi alle parentesi graffe per rimanere nell'attuale contesto di shell che presumo sia più efficiente. Nota che le parentesi graffe richiedono punto e virgola dopo l'ultimo comando e hanno bisogno di uno spazio per la separazione.

# 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'

In una sola riga.

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

0

Le soluzioni pubblicate mi sono sembrate un po 'enigmatiche. Ecco la soluzione in 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')

Puoi chiamare le funzioni da un interprete o eseguirlo dalla shell come:

$ ./remove_multiple_lines.py '*' /tmp/ --strip-right
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.