
ディレクトリ内の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つのファイルの日付範囲をすべてマージしたいと思います。
入力例(簡潔にするために一部の列は省略):
最初のファイル:
列1、2列、3列,...,コラム23,col24_time 値1、値2、値3,...,値23、2012年1月25日午後7時20分 値1、値2、値3,...,値23、2012/1/26午前10:57 値1、値2、値3,...,値23、2012年1月26日午後2時20分 値1、値2、値3,...,値23、2012年1月30日午前11時55分 値1、値2、値3,...,値23、2012年1月30日午後3時17分 値1、値2、値3,...,値23、2012年1月30日午後5時36分 値1、値2、値3,...,値23、2012年1月30日午後8時16分 ... 値1、値2、値3,...,値23、2012年4月11日午前11時45分 値1、値2、値3,...,値23、2012年4月11日午後2時23分
中間ファイル
最後のファイル:
値1、値2、値3,...,値23、2015年3月11日午前4時45分 値1、値2、値3,...,値23、2015年3月11日午前8時40分 ... 値1、値2、値3,...,値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/;
既存のスペースに新しいパターンスペースを追加し、既存のコンテンツと新しいコンテンツの間にH
ewlineを追加(ewlineを追加)してから、パターンスペースを使用して予約済みスペースの内容全体を変更します。\n
H
\n
x
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::Parse
cpanm 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}