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


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の混合に基づくソリューションが可能になることを期待し続けます。
Marco Faustinelli、2015

1
説明を追加します、それは簡単です。そして、誰かがあなたが望むツールで解決策を提案するかもしれません。
taliezin

なぜだけではなく、!($1 in x)代わりに{ if (!($1 in x)) print $0; }
iruvar

@ 1_CRそれは私の悪い癖です、もっと慣用的かもしれないことは知っていますが、OPへの説明にはそれがより簡単になるといつも思います。
taliezin

@Muziettoはまだ、私はこのawkベースのソリューションなどの他のツールを学習し始めても害はないと思います...長い目で見れば、簡素化のために少ないパイプを使用して達成できるソリューションに引き寄せられることを学びます... :)
hjk 2015

5

以下のためのGNU SED

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

ここで、第一 sedの農産物などのsedコマンドフォーマットでハッシュのリスト/12ab09f46\|a77b3ff22\|..../dとに転送 SED入力従ってからのコマンド上に読み出し-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 -d/ -f4 file1.csv | grep -Fvf - file2.csv

これは私が探していたものを私に見えます。少し説明していただけますか?2番目のコマンドがfile2.csvから行を削除する方法がわかりません。
Marco Faustinelli、2015

@Muzietto See更新
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

検索刺さがあることに注意してください/$key/^$key,2個のスラッシュ(ファイル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に置き換えてテストしてください。-re予行演習行います。問題がなければ、-riを使用して実行できます。


mmmh、私はあなたのコードの出力を一時ファイルにリダイレクトしました、そしてそれは約30k行を含んでいます、一方file2.csvは最初に240を持っていて、それはフィルタリングされることになっています。
Marco Faustinelli、2015

まあ、それは置換を行うときに最初のファイルのすべてのハッシュを出力するためだと思います(エコー "\ n" $ iの部分)。あなたは-riでそれを実行した場合、それが所定の位置に置換を行いので、とにかくあなたは、リダイレクトする必要はありません
プリメロ

また、-reを指定してリダイレクトすると、最初のファイルにあるハッシュの数だけfile2が繰り返されます。基本的に、最初のファイルのハッシュごとに、2番目のファイルのハッシュを置き換えて結果を出力するため、非常に多くの行があります。
Primeo 2015

1

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

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

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

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

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.