ลบบรรทัดออกจากไฟล์โดยขึ้นอยู่กับบรรทัดที่พบในไฟล์อื่น


11

ไฟล์ file1.txt มีบรรทัดเช่น:

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

ตัวอย่างเช่น:

/api/purchase/12ab09f46/index.html

ไฟล์ file2.csv มีบรรทัดเช่น:

<hash>,timestamp,ip_address

ตัวอย่างเช่น:

12ab09f46,20150812235200,22.231.113.64 
a77b3ff22,20150812235959,194.66.82.11

ฉันต้องการกรอง file2.csv ลบบรรทัดทั้งหมดที่มีค่าแฮชอยู่ใน file1.txt กล่าวได้ว่า:

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

หรืออะไรทำนองนี้

มันควรจะตรงไปตรงมา แต่ดูเหมือนว่าฉันจะไม่สามารถใช้งานได้

ใครช่วยกรุณาให้ขั้นตอนการทำงานสำหรับงานนี้ได้ไหม

คำตอบ:


13

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

คำอธิบาย:

cut -d / -f 4 file1.txt จะเลือกแฮชจากไฟล์แรก

paste -sd '|' จะเข้าร่วมแฮชทั้งหมดในการแสดงออกปกติ H1|H2|H3

xargs -I{} grep -v -E {} file2.csvจะเรียกใช้ grep ที่มีรูปแบบก่อนหน้านี้เป็นอาร์กิวเมนต์ xargs จะแทนที่{}ด้วยเนื้อหาของSTDIN

หากคุณไม่มีpasteคุณสามารถแทนที่ด้วยtr "\\n" "|" | sed 's/|$//'


3
+1 แต่จำเป็นที่จะต้องไม่เพียงcat cut -d / -f 4 file1.txtหรือถ้าคุณชอบลุคแบบต่อเนื่อง<file1.txt cut -d / -f 4
ปาร์ฮอว์

@Sparhawk ขอบคุณ! ฉันไม่รู้ ;-) อัปเดตวิธีแก้ปัญหา :-)
Gabriele Lana

11

awkทางออกที่เป็นไปได้:

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

ก่อนอื่นเราอ่านfile1.txtโดยใช้FS(ตัวคั่นฟิลด์) "/" และสร้างอาร์เรย์ x พร้อมค่าคีย์จากฟิลด์$4ซึ่งเป็นแฮชที่คุณต้องการ ต่อไปเราอ่านfile2.txtการตั้งค่าไฟล์ที่สองที่FSจะเป็น,และตรวจสอบว่าค่าของเขตข้อมูล$1ไม่ได้เป็นกุญแจสำคัญในอาร์เรย์xและถ้ามันไม่ได้เราพิมพ์มัน
สำนวนที่เหมือนกันมากขึ้นตามที่เสนอในความคิดเห็นอาจเป็น:

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

ฉันซาบซึ้งในความพยายามของคุณ แต่ฉันเกรงว่าสิ่งนี้จะบินเหนือหัวฉัน ฉันหวังว่าจะมีทางออกจากส่วนผสมของ sed / grep / cat บางอย่างที่เป็นไปได้
Marco Faustinelli

1
ฉันจะเพิ่มคำอธิบายมันเป็นเรื่องง่าย และอาจเป็นคนที่จะเสนอวิธีการแก้ปัญหาด้วยเครื่องมือที่คุณต้องการ
taliezin

ทำไมไม่เพียง แต่!($1 in x)แทนที่จะเป็น{ if (!($1 in x)) print $0; }
iruvar

@ 1_CR มันเป็นนิสัยที่ไม่ดีของฉันฉันรู้ว่ามันอาจจะเป็นไปได้มากกว่า แต่ฉันคิดเสมอว่ามันจะง่ายกว่าสำหรับคำอธิบายเกี่ยวกับ OP
taliezin

@Muzietto ฉันคิดว่าคงไม่มีอันตรายใด ๆ เมื่อเริ่มเรียนรู้เครื่องมืออื่น ๆ เช่นawkโซลูชั่นที่ใช้ ... ในระยะยาวคุณจะเรียนรู้ที่จะหาทางแก้ปัญหาที่สามารถทำได้โดยใช้ท่อที่น้อยลงเพื่อความเรียบง่าย ... :)
hjk

5

สำหรับGNU sed

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

โดยที่sed แรก สร้างรายการของ hash ใน sed-command-format like และถ่ายโอนไปยังsed -script ถัดไปซึ่งอ่านคำสั่งด้านบนจากอินพุตดังนั้นตัวเลือก เช่นเดียวกันกับgrep/12ab09f46\|a77b3ff22\|..../d -f -

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

หรือไม่มี perl-expresions:

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

หรือดีกว่าด้วยการตัด :

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

นี่คือสิ่งที่ฉันกำลังมองหา คุณช่วยอธิบายให้หน่อยได้ไหม? ฉันไม่เห็นว่าคำสั่งที่สองจะลบบรรทัดออกจาก file2.csv
Marco Faustinelli

@Muzietto ดูอัปเดต
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

โปรดทราบว่า stings การค้นหาคือ/$key/และ^$key,เพื่อลดผลลัพธ์ที่อยู่ระหว่างเครื่องหมายทับสองอัน (ไฟล์ 1) หรือเป็นรายการแรกของบรรทัดและตามด้วยเครื่องหมายจุลภาค (ไฟล์ 2) สิ่งนี้จะทำให้ปลอดภัยหากปุ่มมีลักษณะ

a,values
a1,values

ในไฟล์ 2 หรือชอบ

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

ในไฟล์ 1


2

ฉันเพิ่งลองใช้สายการบินหนึ่งต่อไปนี้และดูเหมือนว่าจะทำงาน:

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

โปรดแทนที่-riก่อนด้วย-reเพื่อทดสอบ -จะทำงานแบบแห้งและถ้าทั้งหมดก็โอเคคุณสามารถรันด้วย-ri


mmmh ฉันได้เปลี่ยนเส้นทางการส่งออกรหัสของคุณไปยังไฟล์ชั่วคราวและมันมีประมาณ 30k บรรทัดในขณะที่ file2.csv ได้เริ่ม 240 และมันควรจะได้รับการกรอง
Marco Faustinelli

ฉันคิดว่าเป็นเพราะฉันพิมพ์แฮชทุกไฟล์ในไฟล์แรกเมื่อฉันทำการทดแทน (ส่วน echo "\ n" $ i) อย่างไรก็ตามถ้าคุณรันด้วย -ri คุณไม่จำเป็นต้องเปลี่ยนเส้นทางเพราะมันใช้แทนตัวเองได้
Primero

นอกจากนี้หากคุณรันด้วย -re และ redirect คุณจะมีการทำซ้ำ file2 สำหรับแฮชจำนวนมากที่คุณมีในไฟล์แรก โดยพื้นฐานแล้วสำหรับแฮชแต่ละไฟล์ในไฟล์แรกมันจะแทนที่มันในไฟล์ที่สองและพิมพ์ผลลัพธ์ดังนั้นนั่นคือสาเหตุที่คุณมีหลายบรรทัด
primero

1

นอกจากคำตอบของ Gabriele Lana แล้วโปรดทราบว่าคำสั่งวาง BSD ต้องมีการระบุขีดกลางเพื่ออ่านเนื้อหาจากอินพุตมาตรฐาน

คู่มือการวางคำสั่ง

หากระบุ '-' สำหรับไฟล์อินพุตหนึ่งไฟล์ขึ้นไปจะใช้อินพุตมาตรฐาน อินพุตมาตรฐานจะอ่านทีละหนึ่งบรรทัดเป็นวงกลมสำหรับแต่ละอินสแตนซ์ของ '-'

ดังนั้นสุดท้ายต้องมีการเปลี่ยนแปลงเช่นด้านล่าง

cut -d / -f 4 file1.txt | paste -sd '|' - | xargs -I{} grep -v -E {} file2.csv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.