csvファイルから日付範囲を取得する方法はありますか?

csvファイルから日付範囲を取得する方法はありますか?

ディレクトリ内の4つのCSVファイルのそれぞれの日付範囲を見つけたいです。日付列は各csvの最後の列で、形式はです1/25/2012 7:20:55 PM

日付フィールドは、常に最後の列である24列です。各CSVのレコードのみが変更されます。そして日付が決まりました。

各ファイルに対してこれを行い、最終範囲を取得する方法はありますか(日付がソートされていると仮定)?

したがって、最初の記録が2012年1月25日午後7時20分55秒である場合、最後の記録は2016年11月7日午後2時36分20秒です。

出力日付範囲を2012年1月25日から2016年11月7日までに設定したいと思います。しかし、4つのファイルの日付範囲をすべてマージしたいと思います。

入力例(簡潔にするために一部の列は省略):

  • 最初のファイル:

    123,...,コラム23,col24_time
    123,...,23、2012年1月25日午後7時20分
    123,...,23、2012/1/26午前10:57
    123,...,23、2012年1月26日午後2時20分
    123,...,23、2012年1月30日午前11時55分
    123,...,23、2012年1月30日午後3時17分
    123,...,23、2012年1月30日午後5時36分
    123,...,23、2012年1月30日午後8時16分
    ...
    123,...,23、2012年4月11日午前11時45分
    123,...,23、2012年4月11日午後2時23分
  • 中間ファイル

  • 最後のファイル:

    123,...,23、2015年3月11日午前4時45分
    123,...,23、2015年3月11日午前8時40分
    ...
    123,...,23、2016年11月7日午後2時36分

各ファイルにはほぼ5〜10Kレコードがあります。日付はファイル内で順次ソートされます。各ファイルの各列にはヘッダーがあります。

このコマンドの出力は次のhead -n7 Files/file1.csv | cut -d, -f24とおりです。

"col24_time"
"2012-01-01 00:30:26"
"0"
"2012-01-01 02:00:37"
"0"
"0"
"https://external.xx.fbcdn.net/safe_image.php?" <<-- previous column record?

答え1

私はまだその質問を理解しているかどうかわかりません。しかし、指定された入力に基づいて目的の出力を生成するコードは次のようになり、他の答えよりはるかに短いです。

datetime1=$(head -n1 file1.csv | cut -d, -f24)
datetime4=$(tail -n1 file4.csv | cut -d, -f24)
printf '%s - %s\n' "${datetime1%% *}" "${datetime4%% *}"

その後、最初のファイルから最初の行を取得し、4番目と最後のファイルから最後の行を取得し、24番目のフィールドを抽出します(基準:,それぞれの区切り記号)。具体的には1/25/2012 7:20 AM日付/時刻文字列です11/7/2016 2:36 PM。次に、最初のスペースとその後のすべての内容を削除して、各単語の最初の「単語」を印刷します。必須の日付です。

これは準行のようなものです。読みやすくするために3行に分けましたが、論理的には長いコマンドです。

printf '%s - %s\n' \
        "$(head -n1 file1.csv | cut -d, -f24 | cut -d' ' -f1)" \
        "$(tail -n1 file4.csv | cut -d, -f24 | cut -d' ' -f1)"

ここでは変数を使用しないため、パラメータ拡張は使用できないため、2番目を使用して24番目のフィールドの最初の「単語」を抽出しました  cut

答え2

データがすでに日付でソートされていて一貫した構造を持っている場合は、それを使用してsed特定の行を処理できます。

sed -E -n "2 {s/.*,([^ ]*).*/\1 - /;h}; $ {s/.*,([^ ]*).*/\1/;H;x;s/\n//;p}" file

最初のファイルの出力は次のとおりです。

1/26/2012 - 4/11/2012

すべてのファイルを一緒に入れますcat(日付順に名前が付けられ、正しい順序でパイプされると仮定します)。

cat file* | sed ...
1/26/2012 - 11/7/2016

デフォルト値を印刷しないsedに設定-n

sed -E -n "

2行目をつかみ、キャプチャグループで目的の行2部分を収集([^ ]+)し、出力をパターンスペースのキャプチャと区切り文字に結合します。\1 -

           2 {s/.*,([^ ]+).*/\1 - /;

h前のスペースに押し込みます(h以前に存在していたすべての内容を消去します)。

                                h}; 

上の$導線でパターン空間で再度希望の線部分をつかんでください

                                    $ {s/.*,([^ ]+).*/\1/;

既存のスペースに新しいパターンスペースを追加し、既存のコンテンツと新しいコンテンツの間にHewlineを追加(ewlineを追加)してから、パターンスペースを使用して予約済みスペースの内容全体を変更します。\nH\nx

                                                H;x;

これで、結合された出力がパターン空間にあります。望ましくない\n改行とpリントを削除するだけです。

                                                    s/\n//;p}" file

答え3

Unixパイプを好む場合は、次のことができます。

# standalone example: this converts from a 2-colum, 1-line "csv" to unixtime, 
#  and converts back to readable date
echo "2,1/25/2012  7:20:55 PM" \
| perl -aF, -MDate::Parse -E "say Date::Parse::str2time(\$F[1])" - \
| xargs -i date "+%D " -d@{}

# result
01/25/12

これは古いがコアではないPerlモジュールに依存しDate::Parse、まだない場合は最初にインストールする必要があります。設定クライアントインストールを
有効にするか、0に設定することもできます。cpan Date::Parsecpanm Date::Parse

したがって、あなたの例では、2つの単一行から最も若いデータと最も古いデータを取得しようとすることがあります。

perl -aF, -MDate::Parse -E "say Date::Parse::str2time(\$F[5])" *.csv \
| sort \
| sed -e 1b -e '$!d'  \
| xargs -i date "+%D " -d@{}

# result
01/25/12 
11/07/16

ラインsedこの投稿このウェブサイトから。

答え4

次のawkプログラムが実行されます(と呼びますtimerange.awk)。特定の順序でファイルを提供することなく簡単に使用できるように設計されています*.csv。それ以外の場合は、タイムスタンプが順番に指定されているため、ディレクトリの最初のファイルと最後のファイルを指定するだけです。

#!/usr/bin/awk -f

# For every line of the files (after the first, which contains headers)
FNR>1{
    # Break the time stamp field into its individual components and reassemble
    # in a way that 'mktime' understands, to generate an epoch-based timestamp
    # for "later/earlier than"-type comparisons.
    split($NF,a,/[ /:]/);
    if (a[6]=="AM" && a[4]==12) a[4]=0;
    if (a[6]=="PM") a[4]=a[4]+12;
    tst=a[3]" " a[1] " " a[2] " " a[4] " " a[5] " 00";
    curr_ts=mktime(tst);

    # If we are on the first "data" row of the first file, initialize start and end
    # date
    if (NR==2)
    {
        end=start=$NF;
        end_ts=start_ts=curr_ts;
    }
    # On all later lines, check if the timestamps associated with the "start"
    # and "end" time specifications are later resp. earlier than that of the
    # current line. If so, update "start" and "end" specifications.
    else
    {
        if (curr_ts>end_ts) {end_ts=curr_ts; end=$NF};
        if (curr_ts<start_ts) {start_ts=curr_ts; start=$NF};
    }
}


# After the last file was processed: Output the human-readable range
END{print start " - " end}

あなたはそれを呼び出すことができます

awk -F, -f timerange.awk file1.csv file2.csv ...

または単に

awk -F, -f timerange.awk *.csv

ファイルの順序は重要ではないため、グローバル「最初」と「最後」の項目を自動的に検索します。

サンプル入力の出力(現在の形式 - 最初の説明とは反対であると仮定します。タイムスタンプはいいえ秒を含む):

1/25/2012 10:57 AM - 11/7/2016 2:36 PM

修正する

一日の時間を完全に無視するには、プログラムを中断するだけです。

#!/usr/bin/awk -f

# For every line of the files (after the first, which contains headers)
FNR>1{
    split($NF,a,/[ /:]/);
    tst=a[3]" " a[1] " " a[2] " 00 00 00"
    curr_ts=mktime(tst);

    sub(/[[:space:]]+.* [AP]M$/,"",$NF);

    if (NR==2)
    {
        end=start=$NF;
        end_ts=start_ts=curr_ts;
    }
    else
    {
        if (curr_ts>end_ts) {end_ts=curr_ts; end=$NF};
        if (curr_ts<start_ts) {start_ts=curr_ts; start=$NF};
    }
}

END{print start " - " end}

関連情報