Bagaimana cara menghapus beberapa baris kosong dari file?

14

Saya memiliki beberapa file teks yang saya gunakan untuk mencatat - hanya teks biasa, biasanya hanya menggunakan cat >> file. Kadang-kadang saya menggunakan satu atau dua baris kosong (hanya kembali - karakter baris baru) untuk menentukan subjek / alur pemikiran baru. Pada akhir setiap sesi, sebelum menutup file dengan Ctrl+ D, saya biasanya menambahkan banyak (5-10) baris kosong (tombol kembali) hanya untuk memisahkan sesi.

Ini jelas tidak terlalu pintar, tetapi bekerja untuk saya untuk tujuan ini. Aku tidak Namun akhir-up dengan banyak dan banyak baris kosong yang tidak perlu, jadi saya sedang mencari cara untuk menghapus (sebagian) garis ekstra. Apakah ada perintah Linux (cut, paste, grep, ...?) Yang dapat digunakan langsung dengan beberapa opsi? Atau, apakah ada yang punya ide untuk skrip sed, awk atau perl (baik dalam bahasa scripting apa pun, walaupun saya lebih suka skrip sed atau awk) yang akan melakukan apa yang saya inginkan? Menulis sesuatu dalam C ++ (yang sebenarnya bisa saya lakukan sendiri), sepertinya berlebihan.

Kasus # 1: Yang saya butuhkan adalah skrip / perintah yang akan menghapus lebih dari dua (3 atau lebih) baris kosong berturut-turut, dan menggantinya dengan hanya dua baris kosong. Meskipun akan lebih baik jika itu juga bisa tweak untuk menghapus lebih dari satu baris (2 atau lebih) dan / atau mengganti beberapa baris kosong dengan hanya satu baris kosong.

Kasus # 2: Saya juga bisa menggunakan skrip / perintah yang akan menghapus satu baris kosong di antara dua baris teks, tetapi membiarkan beberapa baris kosong apa adanya (meskipun menghapus salah satu baris kosong juga dapat diterima).

Baard Kopperud
sumber
2
@ l0b0, itu pertanyaan yang sama sekali berbeda (yang lain adalah vimsatu, dan untuk mengganti baris kosong dengan satu baris kosong).
Stéphane Chazelas

Jawaban:

14

Kasus 1:

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

Kasus 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
sumber
+1 untuk awk dan bukan sed
Rob
Karena use case ini sering diulang, saya sarankan membuat skrip.
ChuckCottrill
15

Anda dapat menggunakan uniquntuk mengelompokkan banyak instance dari baris kosong menjadi satu baris kosong, tetapi juga akan menutup baris yang berisi teks jika sama dan di bawah satu sama lain.

Anthon
sumber
6

Kasus 1:

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

Kasus 2:

perl -i -ane '$n=(@F==0) ? $n+1 : 0; print $n==2 ? "\n$_" : $n==1 ? "" : $_ '
Basharat Sialvi
sumber
+1 perl ftw! Awk (mungkin) kanonik untuk ini, tetapi (KERING) memaksa saya untuk menulis skrip untuk use-case yang diulang seperti ini.
ChuckCottrill
3

Anda dapat mengatasi Kasus # 1 seperti ini dengan sed GNU:

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

Yaitu, kumpulkan garis-garis kosong di ruang pola, dan jika ada lebih dari tiga garis atau lebih, kurangi menjadi dua garis.

Untuk bergabung dengan garis spasi tunggal, seperti dalam Kasus # 2, Anda dapat melakukannya seperti ini:

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

Atau dalam bentuk komentar:

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

Solusi ini juga menangani baris kosong terakhir dalam file:

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

Mengikuti saran Anthon untuk menggunakan "uniq" ...

Hapus garis depan, belakang, dan duplikat kosong.

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

Dalam satu garis panjang:

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

Atau cukup gunakan "cat -s".

Saya beralih dari kurung ke kurung kurawal agar tetap dalam konteks shell saat ini yang saya anggap lebih efisien. Perhatikan bahwa kurung kurawal memerlukan tanda koma setelah perintah terakhir dan membutuhkan ruang untuk pemisahan.

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

Dalam satu baris.

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

Solusi yang diposting tampak agak samar bagi saya. Berikut ini solusinya dalam 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')

Anda dapat memanggil fungsi dari juru bahasa atau menjalankannya dari shell seperti:

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