Grep xóa dòng bằng 0 nhưng không 0,2?


12

Tôi có một tập tin có nội dung tương tự như sau.

0
0
0.2
0
0
0
0

Tôi cần phải loại bỏ tất cả các dòng với một số không.
Tôi đã suy nghĩ để sử dụng grep -v "0", nhưng điều này cũng loại bỏ dòng chứa 0,2. Tôi thấy tôi có thể sử dụng -wtùy chọn, nhưng điều này dường như cũng không hoạt động.

Làm cách nào tôi có thể xóa tất cả các dòng chỉ chứa một 0 và giữ tất cả các dòng đó bắt đầu bằng 0?


2
Có thể trùng lặp chuỗi chính xác
Julien Lopez

1
@JulienLopez Nó không phải là một bản sao của câu hỏi đó. Câu hỏi đó là về việc kết hợp một từ và trả lời với -w, thất bại ở đây.
Sparhawk

Tại sao bạn buộc phải sử dụng grepcho nhiệm vụ này? Và chính xác những gì bạn có nghĩa là bởi một số không ? Điều này nghe có vẻ rất giống một vấn đề XY .
Roland Illig

1
@RolandIllig là 1 giờ trước khi đi ngủ và tôi muốn bắt đầu xử lý một chuỗi 500.000 chuỗi để kiểm tra xem chúng có phải là khóa riêng của bitcoin hay không và nếu có thì hãy lấy số dư. Lần sau tôi có thời gian để xem nó tôi đã xử lý hàng ngàn chuỗi và tôi chỉ muốn phân tích cú pháp cho bất kỳ giá trị khác không.
Philip Kirkbride

Câu trả lời:


35
grep -vx 0

Từ man grep:

-x, --line-regexp
       Select only those matches that exactly match the whole line.
       For a regular expression pattern, this is like parenthesizing
       the pattern and then surrounding it with ^ and $.

-wthất bại vì là người đầu tiên 0trong 0.02được coi là một "chữ", và do đó dòng này là phù hợp. Điều này là do nó được theo sau bởi một ký tự "không từ". Bạn có thể thấy điều này nếu bạn chạy lệnh gốc mà không có -v, tức là grep -w "0".


Bạn cũng có thể sử dụng -Ftùy chọn vì chúng tôi không sử dụng các mẫu biểu thức chính quy, chỉ cần khớp chuỗi đơn giản
glenn jackman

@glennjackman Có lẽ tôi đã đọc cái này sớm hơn, nhưng dường như tôi không thể tìm thấy nó bây giờ. Chạy với -F(đáng ngạc nhiên với tôi) dường như mất một khoảng thời gian tương tự hoặc thậm chí chậm hơn một chút (~ 5 thép10%). Do đó, tôi không chắc lợi thế sẽ là gì.
Sparhawk

2
Có thể công cụ RegEx được sử dụng thường xuyên và được sử dụng rộng rãi đến mức họ đã triển khai phiên bản rất hiệu quả của nó, nhưng "tìm kiếm đơn giản" có thể đã không được nâng cấp trong 30 năm.
Nelson

@Sparhawk: grepcó lẽ có trường hợp đặc biệt cho các biểu thức không có ký tự đại diện, vì đó là trường hợp sử dụng phổ biến. Thật đáng ngạc nhiên là fgrepsẽ chậm hơn, nhưng không ngạc nhiên khi chi phí để nhận thấy trường hợp đặc biệt này trong khi biên dịch một mẫu ngắn là không đáng kể so với thời gian để quét một tệp lớn. (Nếu nó đòi hỏi một trường hợp đặc biệt để đi nhanh đến mức, so với một mẫu có lớp nhân vật hoặc x.*y.)
Peter Cordes

Nhưng đó có thể là một sự đơn giản hóa bởi vì đầu vào thực sự là nhiều dòng ngắn (không phải là một chuỗi khổng lồ). Tôi quên nếu grepnhận ra bất kỳ ký tự nào ngoài \ndòng mới là dấu phân cách dòng. Nếu không, ẩn ^$ vẫn có thể biến thành một tìm kiếm chuỗi cố định như thế nào strstr(big_buf, "\n0\n"). (Hoặc 0\nkhi bắt đầu bộ đệm.) Nhưng chúng tôi không chỉ tìm kiếm trận đấu đầu tiên có khả năng tiến xa đến một bộ đệm lớn, chúng tôi muốn lọc hiệu quả. Nhưng dù sao, về mặt lý thuyết, vâng, đó chỉ là một memcmp 2 byte ở đầu mỗi dòng và bạn hy vọng rằng cả fgrep và grep sẽ thấy điều đó.
Peter Cordes

28

Với grep:

grep -v "^0$" file

^có nghĩa là bắt đầu của dòng, $có nghĩa là kết thúc của dòng.


2
Đây là những gì người dùng yêu cầu: tránh bất kỳ dòng nào chỉ chứa 1 "0".
Olivier Dulac

1
Tôi sẽ không đặt một ký hiệu đô la theo nghĩa đen trong dấu ngoặc kép như thế.
dùng541686

@mehrdad không phải là vấn đề lớn với regex vì nó thường là char cuối cùng hoặc tiếp theo sẽ không[a-Z0-9]
Sampo Sarrala - codidact.org 17/2/19

14

Mặc dù grep có thể được sử dụng cho việc này (như các câu trả lời khác hiển thị rõ ràng), chúng ta hãy lùi lại một bước và suy nghĩ về những gì bạn thực sự muốn:

  • Bạn có một tập tin chứa số
  • Bạn muốn thực hiện lọc dựa trên giá trị số .

Regex diễn giải dữ liệu chuỗi ký tự. Họ không biết về các con số, chỉ về các chữ số riêng lẻ (và các kết hợp thông thường của chúng). Mặc dù trong trường hợp cụ thể của bạn có một hack đơn giản xung quanh giới hạn này, cuối cùng nó vẫn là một yêu cầu không phù hợp.

Trừ khi có một lý do rất chính đáng để sử dụng grepở đây (ví dụ vì bạn đã đo nó và nó hiệu quả hơn rất nhiều và hiệu quả là rất quan trọng trong trường hợp của bạn), tôi khuyên bạn nên sử dụng một công cụ khác.

awk, ví dụ, có thể lọc dựa trên so sánh số, ví dụ:

awk '$1 == 0' your_file

Nhưng ngoài ra, để có được tất cả các dòng chứa số lớn hơn 0:

awk '$1 > 0' your_file

Tôi yêu regex, nó là một công cụ tuyệt vời. Nhưng nó không phải là công cụ duy nhất . Như đã nói, nếu tất cả những gì bạn có là grep, mọi thứ trông giống như một ngôn ngữ thông thường.


3
Tôi hoàn toàn đồng ý rằng awk có thể thanh lịch hơn ở đây ... tuy nhiên, nó cũng sẽ phù hợp hơn một chút so với những gì người dùng mong đợi (mọi giá trị số đánh giá là 0). Ví dụ, printf '0\n1\n-1\na\nb\n0\n0 also\n0.0\n-0.0\n0*0\n' | awk '($1 == 0)'sẽ phù hợp: 0, 0.0-0.0... và cũng có thể 0 also! Không chỉ là "0". (đó là đôi khi những gì cần thiết, đôi khi không). Nếu người dùng chỉ muốn "0": awk '/^0$/' (hoặc grep '^0$'). Ngoài ra, bạn nên chỉnh sửa: người dùng cần thêm !để phủ định bài kiểm tra, để nó ẩn 0(và các số 0 khác) và hiển thị phần còn lại. tức là:awk '!( $0 == 0)'
Olivier Dulac

1
@Olivier, hoặc kiểm tra giá trị chuỗi:$1 == "0"
glenn Jackman

1
@OlivierDulac Tôi rõ ràng đã sử dụng >chứ không phải !=(hoặc, tương đương, ! (… == …)) để làm nổi bật rằng đây là một so sánh số tùy ý, không chỉ là bình đẳng. Đối với nhận xét khác của bạn, điều này hoàn toàn đúng nhưng về cơ bản chúng tôi quay lại lãnh thổ so sánh chuỗi và giải pháp hiện có bằng cách sử dụng grepcác tác phẩm (mặc dù awktất nhiên cũng hoạt động).
Konrad Rudolph

@KonradRudolph điểm công bằng :)
Olivier Dulac

1
@glennjackman: Thủ thuật thật hay. Nhưng sau đó OP thà làm thử nghiệm$0=="0"
Olivier Dulac

5

grep's -wlà một chút phức tạp trong một cách mà nó chia tách lên chuỗi ban đầu vào từ và phi từ các thành phần (bất cứ điều gì ngoại trừ chữ cái, chữ số hoặc dấu gạch dưới). Vì nó đã gặp phải một thành phần từ hợp lệ 0trong 0.02nó đã khẳng định logic phủ định để loại bỏ dòng.

Sử dụng sedlà một chút dễ dàng trong bối cảnh này để chỉ cần loại bỏ toàn bộ các từ phù hợp

sed '/^0$/d' file

3

Khi các dòng bạn muốn xóa chỉ chứa một 0 dòng tiếp theo, bạn có thể chọn các dòng đó bằng cách ban hành lệnh sau:

grep -v "^0$"

Điều này sẽ chỉ in các lần xuất hiện của 0điều đó là ở phần cuối của một dòngvào lúc bắt đầu của một dòng cùng một lúc. Các -vtùy chọn sau đó đảo ngược lựa chọn của chúng tôi.


1
Câu trả lời này gần giống với Arkadiusz Drabchot, nhưng bạn đã quên -v, vì vậy nó không hoạt động.
Sparhawk

Bạn đúng. Tôi đã gõ trong khi anh ấy đăng câu trả lời của mình vì vậy tôi không thấy nó đã được đưa ra. Tôi đã đọc sai phần đó với -vtùy chọn, cảm ơn!
hùng

0
  • \ b - viền từ

grep -v "\b0\b"

  • khớp đầu dòng, mẫu của bạn và cuối dòng

grep -v "^0$"

  • hoặc như @Sparhawk đề xuất -vx lineregEx

-w hoạt động, nhưng trong trường hợp của bạn 0,2 là hai từ vì ký tự dấu chấm là dấu phân cách từ.


grep -v "\b0\b"không thực sự làm việc ở đây. Bạn sử dụng phiên bản grep nào?
Arkadiusz Drabchot

hoạt động với grep (BSD grep) 2.5.1-FreeBSDtrên macOS và grep (GNU grep) 2.16trên Ubuntu
Jakub Jindra

1
GNU regex sử dụng \<\>làm ranh giới từ, nhưng điều đó sẽ có tác dụng tương tự như-w
glenn jackman

0

Một câu trả lời khác vì lợi ích của sự đa dạng, giả sử bạn có hỗ trợ PCRE grep

grep -Pv "^0(?!\.)"

cái này thực hiện một cái nhìn tiêu cực để khớp với các dòng bắt đầu 0không được theo sau bởi một dấu chấm. Sau đó -vloại bỏ các dòng không phù hợp. Bạn có thể thấy trong hành động ở đây


1
Điều này cũng sẽ xóa các dòng như 0123, đó không phải là những gì OP muốn
iruvar

0

Giả sử bất kỳ dòng nào không chỉ là một 0 có một khoảng thời gian

grep '\.' file

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.