Xóa các dòng khỏi một tệp tùy thuộc vào các dòng được tìm thấy trong một tệp khác


11

Tệp file1.txt chứa các dòng như:

/api/purchase/<hash>/index.html

Ví dụ:

/api/purchase/12ab09f46/index.html

Tệp file2.csv chứa các dòng như:

<hash>,timestamp,ip_address

Ví dụ:

12ab09f46,20150812235200,22.231.113.64 
a77b3ff22,20150812235959,194.66.82.11

Tôi muốn lọc file2.csv loại bỏ tất cả các dòng trong đó giá trị của hàm băm cũng có trong file1.txt. Đó là những gì để nói:

cat file1.txt | extract <hash> | sed '/<hash>/d' file2.csv

hoặc thứ gì đó giống thế này.

Nó nên đơn giản, nhưng tôi dường như không thể làm cho nó hoạt động.

Bất cứ ai có thể xin vui lòng cung cấp một đường ống làm việc cho nhiệm vụ này?

Câu trả lời:


13

cut -d / -f 4 file1.txt | paste -sd '|' | xargs -I{} grep -v -E {} file2.csv

Giải trình:

cut -d / -f 4 file1.txt sẽ chọn băm từ tệp đầu tiên

paste -sd '|' sẽ tham gia tất cả các giá trị băm vào một biểu thức chính quy. H1|H2|H3

xargs -I{} grep -v -E {} file2.csvsẽ gọi grep với mẫu trước đó làm đối số, xargs sẽ thay thế {}bằng nội dung củaSTDIN

Nếu bạn không có, pastebạn có thể thay thế nó bằngtr "\\n" "|" | sed 's/|$//'


3
+1 nhưng không cần cat, chỉ cần cut -d / -f 4 file1.txt. Hoặc nếu bạn thích giao diện tuần tự,<file1.txt cut -d / -f 4
Sparhawk

@Sparhawk cảm ơn! Tôi không biết ;-) giải pháp được cập nhật :-)
Gabriele Lana

11

awkGiải pháp có thể :

awk 'NR == FNR { x[$4] = 1; next; } { if (!($1 in x)) print $0; }' FS="/" file1.txt FS="," file2.txt

Đầu tiên chúng ta đọc file1.txtbằng cách sử dụng FS(dấu tách trường) "/" và tạo mảng x với các giá trị khóa từ trường $4là hàm băm bạn muốn. Tiếp theo chúng ta đọc tập tin thứ hai file2.txtthiết lập FSđược ,và kiểm tra xem giá trị của trường $1không tồn tại như chìa khóa trong mảng xvà nếu nó không chúng ta in nó.
Thành ngữ tương tự như đề xuất trong các ý kiến ​​có thể là:

awk 'NR == FNR { x[$4] = 1; next; } !($1 in x)' FS="/" file1.txt FS="," file2.txt

Tôi đánh giá cao nỗ lực của bạn, nhưng tôi sợ điều này bay lên trên đầu tôi. Tôi tiếp tục hy vọng một giải pháp dựa trên một số hỗn hợp sed / grep / cat sẽ có thể.
Marco Faustinelli 17/08/2015

1
Tôi sẽ thêm một lời giải thích, nó đơn giản. Và có thể ai đó sẽ đề xuất một giải pháp với các công cụ bạn muốn.
Taliezin 17/08/2015

Tại sao không chỉ !($1 in x)thay vì{ if (!($1 in x)) print $0; }
iruvar 17/08/2015

@ 1_CR đó là thói quen xấu của tôi, tôi biết nó có thể là thành ngữ hơn nhưng tôi luôn nghĩ rằng nó sẽ đơn giản hơn để giải thích cho OP.
Taliezin

@Muzietto vẫn còn, tôi nghĩ rằng không có hại khi bắt đầu tìm hiểu các công cụ khác như awkgiải pháp dựa trên cơ sở này ... về lâu dài, bạn sẽ học được cách hấp dẫn đối với các giải pháp có thể đạt được bằng cách sử dụng các ống nhỏ hơn để đơn giản ... :)
hjk

5

Đối với GNU sed

sed -z 's%.*/\([^/]*\)/index.html\n%\1\\|%g;s%^%/%;s%\\|$%/d%' file1.csv |
sed -f - file2.csv

trong đó sed đầu tiên tạo ra danh sách các giá trị băm theo định dạng sed-Command like và chuyển nó sang sed -script tiếp theo đọc lệnh trên từ tùy chọn đầu vào . Tương tự với grep/12ab09f46\|a77b3ff22\|..../d -f -

grep -oP '[^/]*(?=/index.html$)' file1.csv | grep -Fvf - file2.csv

hoặc không có perl-expresions:

grep -o '[^/]*/index.html$' file1.csv | 
grep -o '^[^/]*' | 
grep -Fvf - file2.csv

hoặc thậm chí tốt hơn với cắt :

cut -d/ -f4 file1.csv | grep -Fvf - file2.csv

Điều này cho tôi những gì tôi đang tìm kiếm. Bạn có thể vui lòng minh họa nó một chút? Tôi không thể thấy lệnh thứ hai sẽ xóa các dòng khỏi file2.csv như thế nào.
Marco Faustinelli 17/08/2015

@Muzietto Xem cập nhật
Costas

2
#!/bin/bash
cut -d, -f1 file2 | while read key ; do 
   #check for appearance in file1 with successful grep:
   #exit status is 0 if pattern is found, only search for at least 1
   #appearance -> to speed it up
   if [[ $(grep -m 1 "/$key/" file1) ]] ; then
      sed "/^$key,/d" -i file2
      #note that we are gradually overwriting file2 (-i option),
      #so make a backup!
   fi
done

Lưu ý rằng các đoạn tìm kiếm là /$key/^$key,để giảm kết quả ở giữa hai dấu gạch chéo (tệp 1) hoặc là mục nhập đầu tiên của một dòng và theo sau là dấu phẩy (tệp 2). Điều này sẽ làm cho nó an toàn nếu các phím trông như

a,values
a1,values

trong tập tin 2, hoặc thích

/api/../a1/../
/api/../a/../

trong tập tin 1


2

Tôi vừa thử một lớp lót sau, và nó dường như thực hiện công việc:

 for i in `cat file1.txt  | awk -F"/" '{print $4}'`; do echo "\n $i" ; sed -ri "/^$i,/d" file2.csv ; done

Vui lòng thay thế đầu tiên -ri bằng -re để kiểm tra nó. -re có chạy khô không, và nếu tất cả đều ổn, bạn có thể chạy nó với -ri


mmmh, tôi đã chuyển hướng đầu ra mã của bạn sang một tệp tạm thời và nó chứa khoảng 30k dòng, trong khi file2.csv ban đầu có 240 và được cho là sẽ được lọc.
Marco Faustinelli 17/08/2015

Chà, tôi nghĩ đó là bởi vì tôi in mọi hàm băm trong tệp đầu tiên, khi tôi thực hiện thay thế (phần echo "\ n" $ i). Dù sao đi nữa nếu bạn chạy nó với -ri bạn không phải chuyển hướng, bởi vì nó thay thế tại chỗ
primero

Ngoài ra Nếu bạn chạy với -re và chuyển hướng, bạn sẽ lặp lại file2 với số lần băm bạn có trong tệp đầu tiên. Về cơ bản cho mỗi hàm băm trong tệp đầu tiên, nó thay thế nó trong tệp thứ hai và in kết quả, vì vậy đó là lý do tại sao bạn có quá nhiều dòng.
Primero

1

Ngoài câu trả lời của Gabriele Lana, xin lưu ý rằng lệnh dán BSD cần được chỉ định dấu gạch ngang để đọc nội dung từ đầu vào tiêu chuẩn.

hướng dẫn sử dụng lệnh dán

Nếu '-' được chỉ định cho một hoặc nhiều tệp đầu vào, đầu vào tiêu chuẩn được sử dụng; đầu vào tiêu chuẩn được đọc từng dòng một, theo vòng tròn, cho mỗi trường hợp của '-'.

Vì vậy, cuối cùng cần phải được thay đổi như dưới đây

cut -d / -f 4 file1.txt | paste -sd '|' - | xargs -I{} grep -v -E {} file2.csv
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.