Як видалити з файлу кілька порожніх рядків?


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
Rob

Оскільки цей випадок використання часто повторюється, я б запропонував створити сценарій.
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

Ви можете вирішити подібний випадок №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
'

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

Або просто використовувати "cat -s".

Я перейшов з круглих дужок на фігурні дужки, щоб залишатися в поточному контексті оболонки, який, напевно, вважаю більш ефективним. Зауважте, що фігурним дужкам потрібна крапка з комою після останньої команди та потрібен пробіл для розділення.

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

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.