Видаліть рядки з файлу залежно від рядків, знайдених в іншому файлі


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 '|' приєднає всі хеші до регулярного виразу ex. 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

@Sparhawk дякую! Я не знав ;-) рішення оновлено :-)
Габриела Лана

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, буде можливим.
Марко Фаустінеллі

1
Додам пояснення, це просто. І може хтось запропонує рішення з потрібними інструментами.
taliezin

Чому б не !($1 in x)замість цього{ if (!($1 in x)) print $0; }
iruvar

@ 1_CR це моя погана звичка, я знаю, що це може бути ідіоматичнішим, але я завжди думаю, що це буде простіше для пояснення ОП.
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 видає список хешів у sed-command-форматі, як /12ab09f46\|a77b3ff22\|..../dі переносить його на наступний sed -script, який читає вище команду з введення, тому -f -варіант.
Те саме з grep

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

або без perl-виразів:

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

а ще краще з вирізом :

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

Це виглядає на мені, що я шукав. Чи можете ви, будь ласка, трохи проілюструвати це? Я не бачу, як друга команда видалить рядки з file2.csv.
Марко Фаустінеллі

@Muzietto Дивіться оновлене
Костас

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

Зверніть увагу , що пошукові укуси /$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


мммм, я перенаправив вихід вашого коду до тимчасового файлу, і він містить близько 30 к рядків, тоді як файл2.csv спочатку 240, і його слід фільтрувати.
Марко Фаустінеллі

Ну, я думаю, це тому, що я друкую кожен хеш у першому файлі, коли роблю підміну (ехо "\ n" $ i частина). У будь-якому випадку, якщо ви запускаєте його з -рі, вам не доведеться переадресовувати, оскільки це робить заміну на місці
primero

Крім того, якщо ви працюєте з -re та перенаправленням, у вас буде file2 повторений стільки ж хешей у першому файлі. В основному для кожного хешу в першому файлі він замінює його у другому файлі та друкує результат, тому у вас так багато рядків.
primero

1

На додаток до відповіді Габріеле Лани, будь ласка, зверніть увагу, що команда BSD paste повинна задавати тире, щоб прочитати вміст зі стандартного введення.

посібник з команди вставки

Якщо "-" вказано для одного або декількох вхідних файлів, використовується стандартний вхід; стандартне введення читається по одному рядку по черзі, для кожного примірника '-'.

Отже, остаточну необхідність потрібно змінити, як нижче

cut -d / -f 4 file1.txt | paste -sd '|' - | xargs -I{} grep -v -E {} file2.csv
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.