Làm thế nào để loại bỏ nhiều dòng trống từ một tập tin?


14

Tôi có một số tệp văn bản tôi sử dụng để ghi chú - chỉ là văn bản thuần túy, thường chỉ sử dụng cat >> file. Thỉnh thoảng tôi sử dụng một hoặc hai dòng trống (chỉ cần trả về - ký tự dòng mới) để chỉ định một chủ đề / dòng suy nghĩ mới. Vào cuối mỗi phiên, trước khi đóng tệp bằng Ctrl+ D, tôi thường thêm nhiều dòng (5-10) dòng trống (phím trả về) chỉ để phân tách các phiên.

Điều này rõ ràng là không thông minh lắm, nhưng nó hoạt động với tôi cho mục đích này. Tôi làm tuy nhiên kết thúc-up với rất nhiều và rất nhiều dòng trống không cần thiết, vì vậy tôi đang tìm kiếm một cách để loại bỏ (đa số) các dòng thêm. Có một lệnh Linux (cắt, dán, grep, ...?) Có thể được sử dụng trực tiếp với một vài tùy chọn không? Ngoài ra, có ai có ý tưởng cho một kịch bản sed, awk hoặc perl (thực sự trong bất kỳ ngôn ngữ kịch bản nào, mặc dù tôi thích sed hoặc awk) sẽ làm những gì tôi muốn không? Viết một cái gì đó bằng C ++ (mà tôi thực sự có thể tự làm), có vẻ như quá mức cần thiết.

Trường hợp 1: Điều tôi cần là một tập lệnh / lệnh sẽ loại bỏ nhiều hơn hai (3 hoặc nhiều hơn) các dòng trống liên tiếp và thay thế chúng chỉ bằng hai dòng trống. Mặc dù thật tuyệt nếu nó cũng có thể được điều chỉnh để loại bỏ nhiều hơn một dòng (2 hoặc nhiều hơn) và / hoặc thay thế nhiều dòng trống chỉ bằng một dòng trống.

Trường hợp # 2: Tôi cũng có thể sử dụng tập lệnh / lệnh sẽ xóa một dòng trống duy nhất giữa hai dòng văn bản, nhưng để lại nhiều dòng trống như vậy (mặc dù loại bỏ một trong các dòng trống cũng sẽ được chấp nhận).



2
@ l0b0, đó hoàn toàn là một câu hỏi hoàn toàn khác (câu hỏi còn lại là một câu hỏi vimvà là thay thế các dòng trống bằng một dòng trống).
Stéphane Chazelas

Câu trả lời:


14

Trường hợp 1:

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

Trường hợp 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 cho awk thay vì sed
Rob

Vì trường hợp sử dụng này được lặp lại thường xuyên, tôi sẽ đề nghị tạo một tập lệnh.
ChuckCottrill

15

Bạn có thể sử dụng uniqđể thu gọn nhiều thể hiện của các dòng trống thành một dòng trống, nhưng nó cũng sẽ thu gọn các dòng có chứa văn bản nếu chúng giống nhau và nằm dưới nhau.


6

Trường hợp 1:

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

Trường hợp 2:

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

1 perl ftw! Awk là (có thể) hợp quy cho việc này, nhưng (DRY) buộc tôi phải viết các kịch bản cho các trường hợp sử dụng được lặp lại như thế này.
ChuckCottrill

3

Bạn có thể giải quyết Trường hợp # 1 như thế này với GNU sed:

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

Nghĩa là, thu thập các dòng trống trong không gian mẫu và nếu có nhiều hơn ba dòng trở lên, hãy giảm nó xuống hai dòng.

Để tham gia các dòng có một khoảng cách, như trong trường hợp # 2, bạn có thể làm như thế này:

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

Hoặc ở dạng nhận xét:

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

Giải pháp này cũng quan tâm đến các dòng trống cuối cùng trong tệp:

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

Theo đề nghị của Anthon để sử dụng "uniq" ...

Loại bỏ các dòng trống hàng đầu, dấu và trùng lặp.

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

Trong một dòng dài:

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

Hoặc chỉ sử dụng "cat -s".

Tôi đã chuyển từ dấu ngoặc đơn sang dấu ngoặc nhọn để duy trì trong bối cảnh shell hiện tại mà tôi cho là hiệu quả hơn. Lưu ý rằng dấu ngoặc nhọn yêu cầu dấu chấm phẩy sau lệnh cuối cùng và cần một khoảng trắng để phân tách.

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

Trong một dòng duy nhất.

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

0

Các giải pháp được đăng có vẻ hơi khó hiểu đối với tôi. Đây là giải pháp trong 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')

Bạn có thể gọi các chức năng từ một trình thông dịch hoặc chạy nó từ trình bao như:

$ ./remove_multiple_lines.py '*' /tmp/ --strip-right
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.