Làm thế nào để in tất cả các dòng sau khi khớp đến cuối tập tin?


48

Tệp đầu vào1 là:

dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Tôi đưa ra mẫu phù hợp từ trong other file(như dog 123 4335từ file2).

Tôi khớp với mẫu của dòng là dog 123 4335và sau khi in tất cả các dòng không có dòng khớp, đầu ra của tôi là:

cat 13123 23424
deer 2131 213132
bear 2313 21313

Nếu chỉ sử dụng mà không có địa chỉ của dòng chỉ sử dụng mẫu, ví dụ 1s làm thế nào để khớp và in các dòng?


Tập tin khác có thể chỉ chứa một mẫu duy nhất để tìm kiếm, hoặc một mẫu trên mỗi dòng và bắt đầu tìm kiếm ở bất kỳ dòng nào được tìm thấy đầu tiên trong tệp được tìm kiếm không?
Ciro Santilli 新疆 心 心

Câu trả lời:


27

Giả sử bạn muốn khớp toàn bộ dòng với mẫu của bạn, với GNU sed, điều này hoạt động:

sed -n '/^dog 123 4335$/ { :a; n; p; ba; }' infile

Tương đương tiêu chuẩn:

sed -ne '/^dog 123 4335$/{:a' -e 'n;p;ba' -e '}' infile

Với đầu vào sau ( infile):

cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Đầu ra là:

cat 13123 23424 
deer 2131 213132
bear 2313 21313

Giải trình:

  • /^dog 123 4335$/ tìm kiếm các mẫu mong muốn.
  • :a; n; p; ba;là một vòng lặp tìm nạp một dòng mới từ input ( n), in nó ( p) và các nhánh trở lại nhãn a :a; ...; ba;.

Cập nhật

Đây là một câu trả lời gần hơn với nhu cầu của bạn, tức là mẫu trong tệp2, lấy từ tệp1:

tail -n +$(( 1 + $(grep -m1 -n -f file2 file1 | cut -d: -f1) )) file1

Grep được nhúng và cắt tìm dòng đầu tiên chứa mẫu từ tệp2, số dòng này cộng với một dòng được truyền vào đuôi, dấu cộng có ở đó để bỏ qua dòng có mẫu.

Nếu bạn muốn bắt đầu từ trận đấu cuối cùng thay vì trận đấu đầu tiên thì đó sẽ là:

tail -n +$(( 1 + $(grep -n -f file2 file1 | tail -n1 | cut -d: -f1) )) file1

Lưu ý rằng không phải tất cả các phiên bản đuôi đều hỗ trợ ký hiệu cộng.


Đây là ví dụ đầu tiên về lệnh n và p trong sed mà tôi đã thấy rằng không cảm thấy muốn dùng sed quá xa. Dường như (từ các thử nghiệm ngắn của tôi) rằng sed -n '/^dog 123 4335$/ { :a; p; n; ba; }' infile(với p và n được chuyển đổi) thành công bao gồm cả dòng phù hợp.
Josiah Yoder

26

Nếu bạn có một tệp ngắn hợp lý grepmột mình có thể hoạt động:

grep -A5000 -m1 -e 'dog 123 4335' animals.txt

5000 chỉ là phỏng đoán của tôi ở mức "hợp lý ngắn", vì greptìm thấy kết quả khớp đầu tiên và xuất nó cùng với 5000 dòng tiếp theo (tệp không cần phải có nhiều như vậy). Nếu bạn không muốn trận đấu tự nó sẽ cần phải cắt bỏ nó, vd

grep -A5000 -m1 -e 'dog 123 4335' animals.txt | tail -n+2


Nếu bạn không muốn cái đầu tiên, nhưng trận đấu cuối cùng là dấu phân cách, bạn có thể sử dụng cái này:

tac animals.txt | sed -e '/dog 123 4335/q' | tac

Dòng này đọc animals.txttheo thứ tự ngược của dòng và đầu ra lên đến và bao gồm cả dòng với dog 123 4335và sau đó đảo ngược một lần nữa để khôi phục lại thứ tự đúng.

Một lần nữa, nếu bạn không cần kết quả khớp, hãy nối đuôi. (Bạn cũng có thể làm phức tạp biểu thức sed để loại bỏ bộ đệm của nó trước khi thoát.)


Theo thử nghiệm của tôi, GNU grep 3.0 không xuất ra hơn 132 dòng trong bối cảnh sau (bất kể giá trị được chỉ định).
ruvim

22

Trong thực tế, có lẽ tôi thường sử dụng câu trả lời của Aet3miirahcâu trả lời của alexey thật tuyệt vời khi muốn điều hướng qua các dòng (ngoài ra, nó cũng hoạt động với less). OTOH, tôi thực sự thích một cách tiếp cận khác (đó là loại câu trả lời của Gilles ngược :

sed -n '/dog 123 4335/,$p'

Khi được gọi bằng -ncờ, sedkhông in theo mặc định các dòng nó xử lý nữa. Sau đó, chúng tôi sử dụng một hình thức 2 địa chỉ nói để áp dụng một lệnh từ khớp dòng /dog 123 4335/cho đến khi kết thúc tập tin (đại diện bởi $). Lệnh trong câu hỏi là p, in dòng hiện tại. Vì vậy, điều này có nghĩa là "in tất cả các dòng từ một khớp /dog 123 4335/cho đến hết."


3
Điều đó in dogdòng mặc dù không muốn ở đây.
Stéphane Chazelas

1
Đây có vẻ là câu trả lời tốt nhất (và hoạt động cho trường hợp của riêng tôi) nhưng cũng cần phải được điều chỉnh để bỏ qua dòng phù hợp.
Pavel imerda 14/03/2016

1
sed -n '/ con chó 123 4335 /, $ p' | sed '1d' sẽ xóa dòng chó
Kemin Zhou

1
sed -n '/dog 123 4335/,$p' | tail -n +2cũng sẽ xóa trận đấu
gilad mayani

15
sed -e '1,/dog 123 4335/d' file1

Nếu bạn cần đọc mẫu từ một tệp, thay thế nó vào lệnh sed. Nếu tệp chứa mẫu sed:

sed -e "1,/$(cat file2)/d" file1

Nếu tệp chứa một chuỗi ký tự cần tìm, hãy trích dẫn tất cả các ký tự đặc biệt. Tôi giả sử tập tin chứa một dòng duy nhất.

sed -e "1,/$(sed 's/[][\\\/^$.*]/\\&/g' file2)/d" file1

Nếu bạn muốn trận đấu là toàn bộ dòng, không chỉ là một chuỗi con, hãy bọc mẫu ^…$.

sed -e "1,/^$(sed 's/[][\\\/^$.*]/\\&/g' file2)\$/d" file1

6
Điều đó sẽ không hoạt động nếu mô hình nằm trên dòng đầu tiên. GNU sed0,/dog.../dcho điều đó.
Stéphane Chazelas

14

$ more +/"dog 123 4335" file1


4
Nó cũng hoạt động với less.
brandizzi

3
thông minh trên thiết bị đầu cuối, nhưng nó không thực sự hoạt động nếu bạn đặt nó vào một cái gì đó khác tac.
jcomeau_ictx

tôi đang sử dụng nó như thế này, $ more + / "khớp với từ của tôi" file1 >> file2
AMB

1
Có thể +đã được thay thế bằng -pPOSIX 7: pubs.opengroup.org/onlinepub/9699919799/utilities/more.html nhưng chưa được triển khai trong produc-linux 2.20.1. Và điều này cũng in skipping..và một số dòng mới bổ sung (theo stderr tôi mong đợi, vì vậy có thể sẽ ổn).
Ciro Santilli 新疆 心 心 事件

có lẽ mọi thứ đã thay đổi kể từ đó? bình luận của tôi đã nhận được 3 lượt upvote vì vậy nó có thể có liên quan vào thời điểm đó ...
jcomeau_ictx

11

Với awk:

awk 'BEGIN {getline pattern < "other file"}
   NR == 1, $0 ~ pattern {next}; {print}' < "input file"

5

Một cách sử dụng awk:

awk 'NR==FNR{a[$0];next}f;($0 in a){f=1}'  file2 file1

trong đó file2 chứa các mẫu tìm kiếm của bạn. Đầu tiên, tất cả nội dung của tệp2 được lưu trữ trong mảng "a". Khi tệp1 được xử lý, mọi dòng được kiểm tra đối với mảng và chỉ được in nếu không có.


Tôi nghĩ OP muốn xuất ra mọi dòng theo mẫu.
Thor

@Thor: cảm ơn bạn đã chỉ ra, cập nhật ngay bây giờ ...

Hoàn thành tốt :).
Thor

5

Nếu đầu vào là một tệp thông thường lseekable :

Với GNU grep:

{ grep  -xFm1 'dog 123 4335' >&2
  cat; } <infile 2>/dev/null >outfile

Với sed:

{ sed -n '/^dog 123 4335$/q'
  cat; } <infile >outfile

Một GNU grepđược gọi là w / -mtùy chọn sẽ thoát đầu vào tại trận đấu - và nó sẽ rời khỏi fd đầu vào (có thể phát hiện được) ngay sau điểm mà nó tìm thấy khớp cuối cùng. Vì vậy, việc gọi grepw / -m1tìm sự xuất hiện đầu tiên của một mẫu trong một tệp và để phần bù đầu vào ở vị trí chính xác catđể ghi mọi thứ theo sau khớp đầu tiên của mẫu trong một tệp vào thiết bị xuất chuẩn.

Ngay cả khi không có GNU, grepbạn vẫn có thể thực hiện chính xác điều tương tự với tương thích POSIX sed- khi sed quits, nó được chỉ định để bù đầu vào ngay tại nơi nó thực hiện. GNU sedkhông tuân thủ tiêu chuẩn theo cách này, và do đó, ở trên có thể sẽ không hoạt động với GNU sedtrừ khi bạn gọi nó bằng công -utắc của nó .


lưu ý, việc sedchia sẻ luồng được trình bày ở đây không đặc biệt (mặc dù, vâng, tiêu chuẩn được tham chiếu cụ thể sedlà ví dụ như một tiện ích có khả năng) của quy trình làm việc ở dạng tự do và hợp tác có điều kiện được hiển thị. Đáng chú ý, tất cả các tiện ích tiêu chuẩn đều có nghĩa và được chỉ định để hợp tác và chia sẻ vị trí con trỏ của các luồng đầu vào mà không làm cho trình đọc tiếp theo bất kỳ xử lý nào cả. grep -qnên làm điều này; lặng lẽ grepnên quay lại ngay khi tìm thấy bất kỳ kết quả khớp nào trong đầu vào và theo tiêu chuẩn, mọi tiêu chuẩn còn lại sẽ không được sử dụng theo mặc định.
mikeerv

4

Câu trả lời của tôi cho câu hỏi trong chủ đề, mà không lưu mẫu trong tệp thứ hai. Đây là tập tin thử nghiệm của tôi:

$ cat animals.txt 
cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

GNU sed:

 $ sed '0,/^dog 123 4335$/d' animals.txt 
 cat 13123 23424 
 deer 2131 213132
 bear 2313 21313

Perl:

$ perl -ne 'print unless 1.../^dog 123 4335$/' animals.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Biến thể Perl với mẫu trong một tệp:

$ cat pattern.txt 
dog 123 4335
$ perl -ne 'BEGIN{chomp($p=(<STDIN>)[0])};print unless 1../$p/;' animals.txt < pattern.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313

2

Thứ ed:

ed -s file1 <<< '/dog 123 4335/+1,$p'

Điều này sẽ gửi một plệnh rint để ed trong chuỗi ở đây; lệnh in bị giới hạn trong phạm vi chỉ sau một ( +1) dog 123 4335khớp cho đến khi kết thúc tệp ( $).


1

Nếu bạn không quan tâm đến việc tạo một tệp tạm thời và có csplitsẵn, thì điều này hoạt động:

sh -c 'csplit -sf"$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

Lưu ý file1là tệp đầu vào và file2là tệp mẫu (như đã nêu trong câu hỏi).

Dạng dài của lệnh trên là:

sh -c 'csplit --quiet --prefix="$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

I E,

csplit --quiet --prefix="file1_" "file1" "%^$(cat "file2")%+1" && cat "file1_00"

csplitkhông có prefixcờ ở trên sẽ tạo tệp xx00(tiền tố là xxvà hậu tố 00). Với cờ ở trên, nó tạo ra các tập tin file1_00. Không có quietcờ, nó sẽ in kích thước tệp đầu ra (kích thước của tệp kết quả).


0

Vì awk không được phép rõ ràng, nên đây là lời đề nghị của tôi với giả định 'con mèo' là trận đấu.

awk '$0 ~ /cat/ { vart = NR }{ arr[NR]=$0 } END { for (i = vart; i<=NR ; i++) print arr[i]  }' animals.txt

0

Làm thế nào để in tất cả các dòng sau khi khớp đến cuối tập tin?

Một cách khác để đặt nó là "cách xóa tất cả các dòng từ số 1 cho đến khi khớp (bao gồm)" và điều này có thể được sedviết là:

sed -e '1,/MATCH PATTERN/d'

1
Vấn đề duy nhất là khi mẫu nằm trên dòng đầu tiên ...
don_crissti

1
Điều này có khác với unix.stackexchange.com/a/56517/32558 không?
Ciro Santilli 心 心 事件

Tôi đoán chúng ta cần một ủy ban ở đây để quyết định.
poige

1
@poige: nah, bạn cung cấp cùng một câu trả lời ít toàn diện hơn
Thor

@don_crissti, vậy sed -e '0,/MATCH PATTERN/d'thì sao?
Velkan
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.