Как удалить несколько пустых строк из файла?

14

У меня есть несколько текстовых файлов, которые я использую, чтобы делать заметки - просто обычный текст, обычно просто использующий cat >> file. Иногда я использую пустую строку или две (просто возврат - символ новой строки), чтобы указать новую тему / направление мысли. В конце каждого сеанса, перед закрытием файла с помощью Ctrl+ D, я обычно добавляю лоты (5-10) пустых строк (ключ возврата) просто для разделения сеансов.

Это явно не очень умно, но для меня это работает. Я бы , однако конечный с большим и большим количеством ненужных пустых строк, поэтому я ищу способ , чтобы удалить (большинство) дополнительные строки. Есть ли Linux-команда (cut, paste, grep, ...?), Которую можно использовать напрямую с несколькими опциями? В качестве альтернативы, есть ли у кого-нибудь идея для сценария sed, awk или perl (на самом деле на любом языке сценариев, хотя я бы предпочел sed или awk), который бы делал то, что я хочу? Писать что-то на C ++ (что я на самом деле мог бы сделать сам) просто кажется излишним.

Случай № 1: Мне нужен скрипт / команда, которая удаляет более двух (3 или более) последовательных пустых строк и заменяет их всего двумя пустыми строками. Хотя было бы неплохо, если бы его также можно было настроить, чтобы удалить более одной строки (2 или более) и / или заменить несколько пустых строк одной пустой строкой.

Случай № 2: Я также мог бы использовать скрипт / команду, которая удаляла бы одну пустую строку между двумя строками текста, но оставляла несколько пустых строк как есть (хотя удаление одной из пустых строк также было бы приемлемо).

Баард Копперуд
источник
2
@ l0b0, это совсем другой вопрос (другой был vimодин, и должен был заменить пустые строки одной пустой строкой).
Стефан Шазелас

Ответы:

14

Дело 1:

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

Случай 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 за awk вместо sed
Роб
Поскольку этот вариант использования часто повторяется, я бы предложил создать сценарий.
ChuckCottrill
15

Вы можете использовать, uniqчтобы свернуть несколько экземпляров пустых строк в одну пустую строку, но также будут свернуты строки, содержащие текст, если они одинаковы и расположены ниже друг друга.

Энтон
источник
6

Дело 1:

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

Случай 2:

perl -i -ane '$n=(@F==0) ? $n+1 : 0; print $n==2 ? "\n$_" : $n==1 ? "" : $_ '
Башарат Сиалви
источник
+1 perl ftw! Awk (вероятно) каноничен для этого, но (DRY) заставляет меня писать сценарии для сценариев использования, которые повторяются следующим образом.
ChuckCottrill
3

Вы можете обратиться к Case # 1 следующим образом с помощью GNU sed:

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

То есть собирать пустые строки в пространстве шаблонов, а если их больше трех или более, уменьшите их до двух строк.

Чтобы соединить строки через один интервал, как в случае № 2, вы можете сделать это следующим образом:

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

Или в комментариях:

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

Это решение заботится также о последних пустых строках в файле:

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
источник
0

Следуя предложению Энтона использовать «uniq» ...

Удалите начальные, конечные и дублирующие пустые строки.

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

В одну длинную очередь:

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

Или просто используйте "кошки".

Я переключился с круглых скобок на фигурные скобки, чтобы остаться в текущем контексте оболочки, который, как я полагаю, более эффективен. Обратите внимание, что фигурные скобки требуют точку с запятой после последней команды и пробел для разделения

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

В одной строке.

{ { echo; cat; echo; } | cat -s | sed '1d;$d'; }
JohnMudd
источник
0

Опубликованные решения показались мне немного загадочными. Вот решение в 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')

Вы можете вызвать функции из интерпретатора или запустить его из оболочки, например:

$ ./remove_multiple_lines.py '*' /tmp/ --strip-right
rominf
источник