grep untuk garis bukan setelah pola

Aug 17 2020

Saya mencoba menemukan semua baris file yang tidak mengikuti pola tertentu.

Untuk beberapa waktu saya mengalami masalah dengan historypenggunaan GNU bash(versi 4 dan 5) di mana perintah muncul dalam duplikat. Saya berasumsi ini karena fakta bahwa saya .bashrcmemiliki baris berikut:

 PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"

dan karena saya menggunakan multiplexer terminal ( screendan / atau tmux) perintah yang disebutkan di atas dieksekusi beberapa kali (karena itu echo $PROMPT_COMMANDmenghasilkanhistory -a; history -n; history -a; history -n;

Dalam beberapa situasi (terutama ketika melakukan hal-hal secara bersamaan di panel / windows / frame / buffer yang berbeda) perintah terakhir yang saya masukkan disimpan dua kali atau bahkan lebih sering di my ~/.bash_history. Ini menyebabkan entri seperti berikut:

#1596110297
yadm list -a | xargs -t ls -l
yadm list -a | xargs -t ls -l

Tak perlu dikatakan, ini cukup mengganggu. Saya baru saja (semoga) menemukan perbaikan untuk history-issue (dengan mengubah perintah menjadi PROMPT_COMMAND="history -a; history -n) tetapi koreksi: ini TIDAK menyelesaikan masalah dengan entri duplikat di file history.

Sekarang saya ingin menyingkirkan entri yang digandakan.

Oleh karena itu, saat ini saya mencoba mencari ekspresi reguler untuk menandai semuanya kecuali baris yang dimulai dengan #dan satu baris setelah itu. Ide pertama saya adalah menggabungkan grep -v(untuk membalikkan seleksi) dan grep -A 1(untuk mendapatkan tambahan satu baris setelah pola pencocokan). Tapi

grep -v "^#" -A 1 ~/.bash_history

tidak memberikan hasil yang saya harapkan.

Oleh karena itu pertanyaan saya: apakah ada yang punya ide bagus tentang bagaimana melakukannya dengan menggunakan grep? Jika tidak: bagaimana saya bisa melakukannya dengan alat-alat lain ( sed, awk, ...)?

Jawaban

ilkkachu Aug 17 2020 at 03:48

Sejauh yang saya mengerti grep -v "^#" -A 1berarti mencetak garis yang tidak dimulai dengan tanda hash, dan satu baris setelah masing-masing. Tapi apakah Anda tidak ingin sebaliknya, mencetak baris yang tidak dimulai dengan tanda hash, dan satu baris setelah?

Diberikan file tes:

#123
echo this
echo this
#456
echo that
echo that
echo that
#789
echo third

grep -A1 ^# history.txt |grep -vxFe -- cetakan:

#123
echo this
#456
echo that
#789
echo third

Yang kedua grepadalah menyingkirkan grep -Acetakan pemisah kelompok .

Alternatifnya uniq history.txtharus bekerja untuk mencetak hanya satu dari setiap rangkaian baris identik yang berurutan.

jubilatious1 Aug 17 2020 at 18:40

menggunakan Raku (née Perl6)

Ini sepertinya pekerjaan untuk operator "flip-flop", tersedia dalam sejumlah bahasa skrip. Di bawah ini adalah jawaban menggunakan bahasa pemrograman Raku (sebelumnya dikenal sebagai Perl6). Pertama, mulailah dengan membuat file pengujian yang lebih luas:

$ cat repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
B_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
D_yadm list -a | xargs -t ls -l
E_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
G_yadm list -a | xargs -t ls -l
H_yadm list -a | xargs -t ls -l
I_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5

Sekarang untuk one-liner yang menggunakan fffoperator flip-flop Raku , yang mengimplementasikan perilaku "sed-like". Jepretan AKTIF untuk baris di mana ekspresi reguler pertama melihat (di awal baris ^^) karakter "#" literal. Setelah AKTIF, tangkapan mengabaikan ekspresi reguler pertama dan mengevaluasi terhadap ekspresi reguler kedua, NONAKTIF ketika menemukan kecocokan dengan garis yang tidak ada (di awal baris ^^) karakter "#". Regex 'negatif' diimplementasikan dalam kode di bawah ini menggunakan <-[#]>, yang merupakan "Enumerated Character Class" negatif dan fitur nyata dari bahasa Raku:

$ raku -ne '.put if /^^ "#" / fff /^^ <-[#]> /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5

Sebenarnya, regex pertama (di sebelah kiri fffoperator infix) dapat ditulis menggunakan <+[#]>"Enumerated Character Class" yang positif, untuk konstruksi yang lebih paralel:

$ raku -ne '.put if /^^ <+[#]> / fff /^^ <-[#]> /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5

Selain itu, menurut saya Anda dapat meningkatkan regex Anda dengan menuntut kecocokan untuk-atau-melawan start-of-line "#" diikuti dengan satu atau lebih digit, yaitu <digit>+, lihat di bawah:

$ raku -ne '.put if /^^ <+[#]> <digit>+ / fff /^^ <-[#]> <-digit>+ /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5

[Semua kode di atas menghapus baris duplikat yang dimulai dengan B, D, E, G, H, dan I. Satu-satunya kekhasan yang saya perhatikan adalah dua baris target berurutan seperti "# 1596110297" akan muncul di keluaran Anda, tetapi tidak jelas bagi saya jika file masukan Anda akan berisi baris berurutan seperti itu].

https://raku.org/