2

このようにフォーマットされた .csv ファイルがあります。

myfile.csv

**Date,Timestamp,Data1,Data2,Data3,Data4,Data5,Data6**  
20130730,22:08:51.244,APPLES,Spain,67p,blah,blah  
20130730,22:08:51.244,PEARS,Spain,32p,blah,blah  
20130730,22:08:51.708,APPLES,France,102p,blah,blah  
20130730,22:10:62.108,APPLES,Spain,67p,blah,blah  
20130730,22:10:68.244,APPLES,Spain,67p,blah,blah  

ファイル内のタイムスタンプとミリ秒単位で完全に一致しない可能性が最も高いタイムスタンプをフィードし、特定の grep 検索に一致する前の行を見つけたいと考えています。

たとえば、次のようなものです。

cat myfile.csv | grep 'Spain' | grep 'APPLES' | grep -B1 "22:09"

戻るべき

20130730,22:08:51.244,APPLES,Spain,67p,blah,blah

しかし、これまでのところ、grep で正確なタイムスタンプを使用して動作させることしかできません。これらを時系列として扱う方法はありますか?(それがここでの問題だと推測しています - 純粋なパターンマッチングを試みており、不当に見つけられないわけではありません)

4

3 に答える 3

2

awkを使用した素晴らしいソリューションもあります:

awk -F ',' -v mytime="2013 07 30 22 09 00" '
  BEGIN {tlimit=mktime(mytime); lastline=""}
  {
    l_y=substr($1,0,4); l_m=substr($1,4,2); l_d=substr($1,6,2);
    split($2,l_hms,":"); l_hms[3]=int(l_hms[3]);
    line_time=mktime(sprintf("%d %d %d %d %d %d", l_y, l_m, l_d, l_hms[1], l_hms[2], l_hms[3]));
    if (line_time>tlimit) exit; lastline=$0;
  }
  END{if lastline=="" print $0; else print lastline;}' myfile.csv

awk の time function を使用して各行からタイムスタンプを作成することに基づいて動作していますmktime。また、それが日付であると仮定し$1ます。

最初の行では、希望する制限時間のタイムスタンプを指定する必要があります (ここでは を選択します2013 07 30 22 09 00)。で使用される形式に従って記述する必要がありますmktime: YYYY MM DD hh mm ss。制限時間のタイムスタンプを作成して awk ステートメントを開始します。次に、各行について、$1(4 行目) から年、月、日を取得し、(5 行目) から正確な時間を取得し$2ます。1 秒しかかからないのでmktime、秒を切り捨てます ( で切り上げることができますint(l_hms[3]+0.5))。ここでは、秒を破棄するなど、タイムスタンプを概算するために必要なすべてのことを行うことができます。6 行目では、抽出した 6 つの日付フィールドからタイム スタンプを作成します。最後に、7 行目で、タイムスタンプを比較し、制限時間に達した場合に終了します。前の行が必要なので、その行を変数に格納しますlastline. 終了時に、私は印刷しlastlineます; 最初の行で制限時間に達した場合は、最初の行を印刷します。

このソリューションは、サンプル ファイルでうまく機能し、指定した任意の日付で機能します。日付制限を正しい形式で指定するだけです。

編集

mktimeその必要がないことは承知しています。YYYYMMDDとして書かれた日付であると仮定する$1と、日付を数値として比較してから時間を比較できます( で抽出しsplit、他の回答のように数値として再構築します)。その場合、必要な形式で時間制限を指定し、BEGINブロックで適切な日付と時間制限を回復できます。

于 2013-08-23T15:52:58.893 に答える
2

フィードしたタイムスタンプよりも低いタイムスタンプを持つ最後の行をメモリに保持し、最後の一致を最後に出力する awk を作成できます (それらが昇順であることを考慮して)

元:

awk  -v FS=',' -v thetime="22:09" '($2 < thetime) { before=$0 ; }  END { print before ; }' myfile.csv

これは、辞書編集上、比較するために完全なサイズ (つまり 22:09:00.000) を必要としない文字列をフィードするときに機能します。

同じですが、読みやすくするためにいくつかの行があります。

awk  -v FS=',' -v thetime="22:09" '
   ($2 < thetime) { before=$0 ; }  
   END            { print before ; }' myfile.csv

あなたの完全な要件を理解できたら、国と製品の種類に一致する行の中から、タイムスタンプの前の最後の行を見つける必要がありますか? それから:

awk -v FS=',' -v thetime="${timestamp}" -v country="${thecountry}" -v product="${theproduct}" '
   ( $4 == country ) && ( $3 == product ) && ( $2 < thetime ) { before=$0 ; }
   END             { print before ; }'  myfile.csv

あなたのために働くはずです...(10:07、SpainとAPPLESをフィードすると、予想される「20130730,22:08:51.244,APPLES,Spain,67p,blah,blah」行が返されます)

また、ファイルが数日にわたる場合 (Bentoy13 の懸念に対処するため)、

awk -v FS=',' -v theday="${theday}" -v thetime="${timestamp}" -v thecountry="${thecountry}" -v theproduct="${theproduct}" '
   ( $4 == thecountry ) && ( $3 == theproduct ) && (($1<theday)||(($1==theday)&&($2<thetime))) { before=$0 ; }
   END             { print before ; }'  myfile.csv

最後の列は、最初の列が変更された場合 (つまり、数日にまたがる場合) にも機能しますが、その日にもフィードする必要があります。

于 2013-08-23T14:54:27.180 に答える
1

次のようawkに代わりに使用できます。grep

 awk -v FS=',' -v Hour=22 -v Min=9 '{split($2, a, "[:]"); if ((3600*a[1] + 60*a[2] + a[3] - 3600*Hour - 60*Min)^2 < 100) print $0}' file

基本的に、100必要な許容範囲に変更します。

于 2013-08-23T14:10:33.353 に答える