web-dev-qa-db-ja.com

別のファイルで見つかった行に応じてファイルから行を削除する

ファイル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

またはこのようなもの。

簡単なはずですが、うまく動かないようです。

誰もがこのタスクのための作業パイプラインを提供できますか?

11

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/|$//'に置き換えることができます。

13
Gabriele Lana

可能なawkソリューション:

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

最初にFS(フィールドセパレータ) "/"を使用してfile1.txtを読み取り、必要なハッシュであるフィールド$4からのキー値で配列xを作成します。次に、2番目のファイルfile2.txtを設定してFS,に設定し、フィールド$1の値が配列xのキーとして存在しないかどうかと、印刷しません。
コメントで提案されているのと同じより慣用的なものは次のようになります:

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

GNU sedの場合

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

ここでfirstsed/12ab09f46\|a77b3ff22\|..../dのようなsed-command-formatでハッシュのリストを生成し、それをnextsed-script入力から上記のコマンドを読み取るため、-f -オプション。
grepと同じ

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

cut -d/ -f4 file1.csv | grep -Fvf - file2.csv
5
Costas
#!/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,であり、結果が2つのスラッシュの間にある(ファイル1)か、行の最初のエントリになり、その後にコンマが続く(ファイル2)ことに注意してください。これは、キーが次のように見える場合に安全になるはずです

a,values
a1,values

ファイル2など

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

ファイル1

2
Fiximan

私は次のライナーを試してみました、そしてそれは仕事をするようです:

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

最初に-ri-reに置き換えてテストしてください。 -re予行演習を行い、問題がなければ-riで実行できます。

2
primero

Gabriele Lanaの回答 に加えて、標準入力からコンテンツを読み取るには、BSD pasteコマンドでダッシュを指定する必要があることに注意してください。

貼り付けコマンドのマニュアル

1つ以上の入力ファイルに「-」が指定されている場合、標準入力が使用されます。標準入力は、「-」のインスタンスごとに1行ずつ循環的に読み取られます。

最終的には以下のように変更する必要があります

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