¿Cómo eliminar múltiples líneas en blanco de un archivo?

14

Tengo algunos archivos de texto que utilizo para tomar notas, solo texto simple, generalmente solo uso cat >> file. Ocasionalmente uso una o dos líneas en blanco (solo regreso - el carácter de nueva línea) para especificar un nuevo asunto / línea de pensamiento. Al final de cada sesión, antes de cerrar el archivo con Ctrl+ D, típicamente agrego lotes (5-10) líneas en blanco (tecla de retorno) solo para separar las sesiones.

Obviamente, esto no es muy inteligente, pero funciona para mí con este propósito. Yo no obstante terminar con las porciones y las porciones de líneas en blanco innecesarios, por lo que estoy buscando una manera de quitar (la mayoría de) las líneas adicionales. ¿Hay un comando de Linux (cortar, pegar, grep, ...?) Que pueda usarse directamente con algunas opciones? Alternativamente, ¿alguien tiene una idea para un script sed, awk o perl (bueno en cualquier lenguaje de script realmente, aunque preferiría sed o awk) que haría lo que quisiera? Escribir algo en C ++ (que en realidad podría hacer yo mismo), parece una exageración.

Caso # 1: Lo que necesito es un script / comando que elimine más de dos (3 o más) líneas en blanco consecutivas y las reemplace con solo dos líneas en blanco. Aunque sería bueno si también se pudiera ajustar para eliminar más de una línea (2 o más) y / o reemplazar varias líneas en blanco con solo una línea en blanco.

Caso # 2: También podría usar un script / comando que eliminaría una sola línea en blanco entre dos líneas de texto, pero dejaría varias líneas en blanco tal como están (aunque eliminar una de las líneas en blanco también sería aceptable).

Baard Kopperud
fuente
2
@ l0b0, esa es una pregunta completamente diferente (la otra era una vimy era reemplazar las líneas en blanco con una línea en blanco).
Stéphane Chazelas

Respuestas:

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}'
Stéphane Chazelas
fuente
1 de awk en lugar de sed
Rob
Dado que este caso de uso se repite con frecuencia, sugeriría crear un script.
ChuckCottrill
15

Puede usar uniqpara contraer varias instancias de líneas en blanco en una línea en blanco, pero también colapsará las líneas que contienen texto si son iguales y están una debajo de la otra.

Anthon
fuente
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 ? "" : $_ '
Basharat Sialvi
fuente
+1 perl ftw! Awk es (probablemente) canónico para esto, pero (DRY) me obliga a escribir scripts para casos de uso que se repiten así.
ChuckCottrill
3

Puede abordar el Caso # 1 de esta manera con GNU sed:

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

Es decir, recolecte líneas vacías en el espacio del patrón, y si hay más de tres o más líneas, reduzca a dos líneas.

Para unir líneas separadas, como en el Caso # 2, puede hacerlo así:

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

O en forma comentada:

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
fuente
1

Esta solución también se ocupa de las últimas líneas en blanco en el archivo:

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
fuente
0

Siguiendo la sugerencia de Anthon de usar "uniq" ...

Elimine las líneas en blanco iniciales, finales y duplicadas.

# 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 una larga línea:

(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 simplemente use "cat -s".

Cambié de paréntesis a llaves para permanecer en el contexto actual de shell, que supongo es más eficiente. Tenga en cuenta que las llaves requieren punto y coma después del último comando y necesitan un espacio para la separación.

# 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 una sola linea.

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

Las soluciones publicadas me parecieron un poco crípticas. Aquí está la solución en 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')

Puede llamar a las funciones desde un intérprete o ejecutarlo desde el shell como:

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