Comment supprimer plusieurs lignes vides d'un fichier?

14

J'ai quelques fichiers texte que j'utilise pour prendre des notes - juste du texte brut, généralement juste en utilisant cat >> file. Parfois, j'utilise une ou deux lignes vides (il suffit de retourner - le caractère de nouvelle ligne) pour spécifier un nouveau sujet / ligne de pensée. À la fin de chaque session, avant de fermer le fichier avec Ctrl+ D, j'ajoute généralement beaucoup (5-10) lignes vides (touche retour) juste pour séparer les sessions.

Ce n'est évidemment pas très intelligent, mais cela fonctionne pour moi dans ce but. Je ne finis-up mais avec beaucoup, beaucoup de lignes vides inutiles, donc je suis à la recherche d'un moyen de supprimer ( la plupart) des lignes supplémentaires. Existe-t-il une commande Linux (couper, coller, grep, ...?) Qui pourrait être utilisée directement avec quelques options? Alternativement, quelqu'un a-t-il une idée pour un script sed, awk ou perl (enfin dans n'importe quel langage de script, bien que je préfère sed ou awk) un script qui ferait ce que je veux? Ecrire quelque chose en C ++ (que je pourrais réellement faire moi-même), semble juste exagéré.

Cas n ° 1: J'ai besoin d'un script / commande qui supprimerait plus de deux (3 ou plus) lignes vierges consécutives et les remplacerait par seulement deux lignes vides. Bien que ce serait bien s'il pouvait également être modifié pour supprimer plusieurs lignes (2 ou plus) et / ou remplacer plusieurs lignes vides par une seule ligne vierge.

Cas n ° 2: je pourrais également utiliser un script / commande qui supprimerait une seule ligne vierge entre deux lignes de texte, mais laisser plusieurs lignes vides telles quelles (bien que la suppression d'une des lignes vides soit également acceptable).

Baard Kopperud
la source
2
@ l0b0, c'est une question complètement différente (l'autre était une vimet devait remplacer les lignes vides par une ligne vide).
Stéphane Chazelas

Réponses:

14

Cas 1:

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

Cas 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}'
Stéphane Chazelas
la source
+1 pour awk au lieu de sed
Rob
Étant donné que ce cas d'utilisation est répété fréquemment, je suggère de créer un script.
ChuckCottrill
15

Vous pouvez utiliser uniqpour réduire plusieurs instances de lignes vides en une seule ligne vide, mais cela réduira également les lignes qui contiennent du texte si elles sont identiques et en dessous les unes des autres.

Anthon
la source
6

Cas 1:

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

Cas 2:

perl -i -ane '$n=(@F==0) ? $n+1 : 0; print $n==2 ? "\n$_" : $n==1 ? "" : $_ '
Basharat Sialvi
la source
+1 perl ftw! Awk est (probablement) canonique pour cela, mais (DRY) m'oblige à écrire des scripts pour des cas d'utilisation qui sont répétés comme ceci.
ChuckCottrill
3

Vous pouvez traiter le cas n ° 1 comme ceci avec GNU sed:

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

Autrement dit, collectez des lignes vides dans l'espace de motif et s'il y a plus de trois lignes ou plus, réduisez-la à deux lignes.

Pour joindre des lignes à simple interligne, comme dans le cas # 2, vous pouvez le faire comme ceci:

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

Ou sous forme commentée:

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
'
Thor
la source
1

Cette solution prend également en charge les dernières lignes vides du fichier:

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
'
PJ_Finnegan
la source
0

Suite à la suggestion d' Anthon d'utiliser "uniq" ...

Supprimez les lignes vides de début, de fin et en double.

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

En une longue file:

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

Ou utilisez simplement "cat -s".

Je suis passé des parenthèses aux accolades pour rester dans le contexte actuel du shell qui, je suppose, est plus efficace. Notez que les accolades nécessitent un point-virgule après la dernière commande et nécessitent un espace pour la séparation.

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

En une seule ligne.

{ { echo; cat; echo; } | cat -s | sed '1d;$d'; }
JohnMudd
la source
0

Les solutions publiées me semblaient un peu cryptiques. Voici la solution dans 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')

Vous pouvez appeler les fonctions à partir d'un interpréteur ou l'exécuter à partir du shell comme:

$ ./remove_multiple_lines.py '*' /tmp/ --strip-right
rominf
la source