grep cho các dòng không nằm sau một mẫu

Aug 17 2020

Tôi đang cố gắng tìm tất cả các dòng của tệp không nằm sau một mẫu cụ thể.

Trong một thời gian, tôi gặp sự cố với historyviệc sử dụng GNU bash(phiên bản 4 và 5) trong đó các lệnh xuất hiện trùng lặp. Tôi cho rằng điều này là do thực tế là trong tôi .bashrccó dòng sau:

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

và vì tôi đang sử dụng bộ ghép kênh đầu cuối ( screenvà / hoặc tmux), lệnh được đề cập ở trên được thực thi nhiều lần (do đó echo $PROMPT_COMMANDdẫn đếnhistory -a; history -n; history -a; history -n;

Trong một số tình huống (đặc biệt là khi thực hiện đồng thời các nội dung trên các ngăn / cửa sổ / khung / bộ đệm khác nhau), lệnh cuối cùng tôi nhập được lưu trữ hai lần hoặc thậm chí thường xuyên hơn trong của tôi ~/.bash_history. Điều này dẫn đến các mục nhập như sau:

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

Không cần phải nói, điều này là khá khó chịu. Tôi chỉ (hy vọng) tìm thấy một bản sửa lỗi cho history-issue (bằng cách thay đổi lệnh thành PROMPT_COMMAND="history -a; history -n) nhưng sửa: điều này KHÔNG giải quyết được vấn đề với các mục nhập trùng lặp trong history.

Bây giờ tôi muốn loại bỏ các mục trùng lặp.

Do đó, tôi hiện đang cố gắng tìm một biểu thức chính quy để đánh dấu mọi thứ ngoại trừ các dòng bắt đầu bằng #và một dòng sau đó. Ý tưởng đầu tiên của tôi là kết hợp grep -v(để đảo ngược vùng chọn) và grep -A 1(để có thêm một dòng sau mẫu phù hợp). Nhưng

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

đã không mang lại kết quả như tôi mong đợi.

Do đó câu hỏi của tôi: có ai có ý tưởng hay về cách sử dụng nó grepkhông? Nếu không được: làm thế nào tôi có thể thực hiện điều này với các công cụ khác ( sed, awk, ...)?

Trả lời

ilkkachu Aug 17 2020 at 03:48

Theo như tôi hiểu grep -v "^#" -A 1có nghĩa là in các dòng không bắt đầu bằng dấu thăng và một dòng sau mỗi dòng. Nhưng bạn không muốn điều ngược lại, in các dòng làm bắt đầu với một dấu thăng, và một dòng sau?

Đưa ra một tệp thử nghiệm:

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

grep -A1 ^# history.txt |grep -vxFe -- bản in:

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

Thứ hai greplà loại bỏ các grep -Abản in phân tách nhóm .

Ngoài ra, uniq history.txtsẽ làm việc để in chỉ một trong mỗi tập hợp các dòng giống nhau liên tiếp.

jubilatious1 Aug 17 2020 at 18:40

sử dụng Raku (nhũ danh Perl6)

Điều này có vẻ giống như một công việc cho toán tử "flip-flop", có sẵn trong một số ngôn ngữ kịch bản. Dưới đây là câu trả lời sử dụng ngôn ngữ lập trình Raku (trước đây được gọi là Perl6). Trước tiên, hãy bắt đầu bằng cách tạo một tệp thử nghiệm mở rộng hơn:

$ 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

Bây giờ đối với một lớp lót sử dụng ffftoán tử flip-flop của Raku , thực hiện hành vi "giống như sed". Chụp BẬT cho các dòng mà regex đầu tiên nhìn thấy (ở đầu dòng ^^) ký tự "#" theo nghĩa đen. Sau khi BẬT, quá trình chụp sẽ bỏ qua regex đầu tiên và đánh giá theo regex thứ hai, TẮT khi tìm thấy khớp với các dòng bị thiếu (ở đầu dòng ^^) một ký tự "#". Regex 'tiêu cực' được triển khai trong đoạn mã dưới đây bằng cách sử dụng <-[#]>, là "Lớp ký tự được liệt kê" phủ định và là một tính năng thực sự của ngôn ngữ 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

Trên thực tế, regex đầu tiên (ở bên trái của ffftoán tử infix) có thể được viết bằng cách sử dụng <+[#]>"Lớp ký tự được liệt kê" tích cực, để xây dựng song song hơn:

$ 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

Ngoài ra, đối với tôi, dường như bạn có thể cải thiện regex của mình bằng cách yêu cầu so khớp cho hoặc so với đầu dòng "#" theo sau là một hoặc nhiều chữ số, tức là <digit>+xem bên dưới:

$ 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

[Tất cả mã ở trên xóa các dòng trùng lặp bắt đầu bằng B, D, E, G, H và I. Điều kỳ lạ duy nhất tôi nhận thấy là hai dòng mục tiêu liên tiếp như "# 1596110297" sẽ xuất hiện trong đầu ra của bạn, nhưng nó không rõ ràng cho tôi nếu tệp đầu vào của bạn có bao giờ chứa các dòng liên tiếp như vậy].

https://raku.org/