2017-07-25 5 views
2

私は約10MMレコードのファイルを持っています。unix awk月の日付フィールドから整数フィールドを減算する

0000000566 2017/01/01 
0000000055 2017/01/01 
0000000109 2016/12/01 
0000000940 2017/01/01 
0000000566 2016/12/01 
0000000055 2016/12/01 
0000000109 2016/11/01 

私は」:私は基本的に日付の月のオフ最後の整数値を引くので、整数ことなく、新しい価値を印刷する必要が

0000000566 2017/01/01 0 
0000000055 2017/01/01 0 
0000000109 2017/01/01 1 
0000000940 2017/01/01 0 
0000000566 2017/01/01 1 
0000000055 2017/01/01 1 
0000000109 2017/01/01 2 

:ここに私のdateSampleのsrcファイルです(またはテスト中にmacOS上でgdateして)実際の問題を抱えていて、最後の数日間は無駄に探していました。
それはゼロで接頭辞とMとDの値を落としています次のいずれか

ゼロと
awk '{ print (gdate -d $2 +"%Y/%m/%d") }' <$src 

または接尾辞年から整数を引い:

awk '{ print (gdate -d $2 +-$3 months +"%Y/%m/%d") }' <$src 

またはまだ一緒に全部をマッシュ正しくありません:

awk '{ print gdate -d (gdate -d $2 +"%Y/%m/%d") +-$3 months +"%Y/%m/%d" }' <$src 

私は、次のような優れた応答が見つかりました: Increment date with AWK for few days and months 私は望んでいるものを正確にやっているが、コマンド内のコマンドのために非常に遅いと思っている。

awk '{ cmd=" gdate -d \"$(gdate -d \""$2"\")+\"-"$3"\"months\" \"+%Y/%m/%d\" "; 
     cmd | getline fmtDate; close(cmd); 
     print $1, fmtDate 
    }' <$src 

だから私は基本的にパフォーマンスの方法で、その出力を必要とする:ここで

は、現在のawkは(私は今のMacOSのBSD上で実行していますので、私はgdateを使用しています)です。
事前にガイダンス/リライトをお寄せいただきありがとうございます。
乾杯

答えて

2

あなたawkは(GNUの拡張である)time functionsmktimestrftimeをサポートしている場合、あなたは、単に行うことができますそれは次のようなものです:

awk -F'[ /]' '{print $1 " " strftime("%Y/%m/%d", mktime($2" "($3-$5)" "$4" 0 0 0"))}' file 

まず、日付をUnixのタイムスタンプに変換します。 mktimeは、"YYYY MM DD HH MM SS"形式の日付しか受け付けないため、手動で構築する必要があります。しかし、それは自動的に正規化を行い、うれしく"2017 -1 1 0 0 0""2016 11 1 0 0 0"と同じタイムスタンプに変換します。

その後、タイムスタンプを「y/m/d」形式に変換して印刷するだけで済みます。


または、日付の正規化を必要としない簡単な場合には「手で」日付の計算を行うことができます - 月の日は常に<= 28ある場合。

#!/usr/bin/awk -f 

BEGIN { 
    FS = "[ /]"; 
} 

{ 
    mm = $2 * 12 + ($3 - 1) - $5; 
    y = int(mm/12); 
    m = mm % 12 + 1; 
    d = $4; 
    printf("%s %04d/%02d/%02d\n", $1, y, m, d); 
} 

ので(28は、31のように、あなたはまた、クリッピング/クランプまたは下記のスクリプトにあふれを追加する必要がありますが、その後、あなたがうるう年の世話をしなければならないこと大きい日、などの場合)、アイデアは簡単です。スペースとスラッシュを分割して、年/月を合計月数(12 * y + m)に変換することができます。次に、最後の列から月を減算し、divmod操作を介して月の合計数を年/月に変換します。

出力:

$ ./script.awk file 
0000000566 2017/01/01 
0000000055 2017/01/01 
0000000109 2016/12/01 
0000000940 2017/01/01 
0000000566 2016/12/01 
0000000055 2016/12/01 
0000000109 2016/11/01 
+1

すごい反応...詳細な説明をいただきありがとうございます。 strftime-mktimeコンボはとても素敵で、超高速動作しています。 完全版のために「手作り」版もありがとう。 ありがとうございます。乾杯。 – sigmazen

+0

歓迎します、あなたの問題を解決してうれしいです。 – randomir

1

あなたが日付を操作しているので、シェル自身でこれを行う方が良いです:

while read -r str date n; do 
    echo "$str $(date -d "$(date -d $date) -$n months" '+%Y/%m/%d')" 
done < file 

0000000566 2017/01/01 
0000000055 2017/01/01 
0000000109 2016/12/01 
0000000940 2017/01/01 
0000000566 2016/12/01 
0000000055 2016/12/01 
0000000109 2016/11/01 
+1

各行で外部( '' date'')を呼び出しているので、遅くなることはありませんか? – randomir

+1

また、 'date'への内部呼び出しは' $(date -d "$ date" '+%Y /%m /%d')です。 – randomir

+0

ありがとう@randomirこれで修正されました。 – anubhava

1

この問題を解決するためのもう一つの簡単な方法を試してみてください。

awk 'BEGIN{ 
    split("01,02,03,04,05,06,07,08,09,10,11,12", month,",") 
} 
{ 
    split($2, array,"/"); 
     if(array[2]<=$3){ 
     array[2]=array[2]+12-$3; 
     array[1]=array[1]-1 
     } 
     else{ 
     array[2]-$3 
     }; 
    print $1,array[1]"/"array[2]"/"array[3] 
} 
' Input_file 
+0

こんにちは...上記のおかげで、それは2017-05-01の日付のために働いていなかった。ここで調整されたバージョンです: awk '{ \t split($ 2、array、 "/"); \t IF($ 3>の0){ \t(配列[2] == "01")であれば{ \t配列[1] =配列[1] - 1 \t配列[2] =配列[2] + 12 - $ 3; \t IF(配列[2] <10){アレイ[2] = "0" 配列[2]} \t}他{ \t \t配列[2] =配列[2] - $ 3 \t IF(配列[ 2] <10){array [2] = "0" array [2]} \t}; \t} \t印刷$ 1、配列[1] "/" 配列[2] "/" 配列[3] \t}」<$ SRC 乾杯 – sigmazen

関連する問題