次の形式のデータセットがあります
1番目と2番目のフィールドは、調査の開始日と終了日(M/D/YYYY)を示します。
AWKまたはBASHスクリプトを使用したうるう年を考慮して、データを目的の出力形式に拡張するにはどうすればよいですか?
あなたの助けは非常にありがたいです。
入力
7/2/2009 7/7/2009
2/28/1996 3/3/1996
12/30/2001 1/4/2002
必要な出力
7/7/2009
7/6/2009
7/5/2009
7/4/2009
7/3/2009
7/2/2009
3/3/1996
3/2/1996
3/1/1996
2/29/1996
2/28/1996
1/4/2002
1/3/2002
1/2/2002
1/1/2002
12/31/2001
12/30/2001
gawk
がある場合:
#!/usr/bin/gawk -f
{
split($1,s,"/")
split($2,e,"/")
st=mktime(s[3] " " s[1] " " s[2] " 0 0 0")
et=mktime(e[3] " " e[1] " " e[2] " 0 0 0")
for (i=et;i>=st;i-=60*60*24) print strftime("%m/%d/%Y",i)
}
デモンストレーション:
./daterange.awk inputfile
出力:
07/07/2009
07/06/2009
07/05/2009
07/04/2009
07/03/2009
07/02/2009
03/03/1996
03/02/1996
03/01/1996
02/29/1996
02/28/1996
01/04/2002
01/03/2002
01/02/2002
01/01/2002
12/31/2001
12/30/2001
編集:
上記のスクリプトは、日数についての素朴な仮定に苦しんでいます。マイナーな問題ですが、状況によっては予期しない結果が生じる可能性があります。ここで少なくとも1つの他の答えにもその問題があります。おそらく、日数を減算(または加算)するdate
コマンドには、この問題はありません。
一部の回答では、事前に日数を知っておく必要があります。
これらの懸念にうまく対処する別の方法を次に示します。
while read -r d1 d2
do
t1=$(date -d "$d1 12:00 PM" +%s)
t2=$(date -d "$d2 12:00 PM" +%s)
if ((t2 > t1)) # swap times/dates if needed
then
temp_t=$t1; temp_d=$d1
t1=$t2; d1=$d2
t2=$temp_t; d2=$temp_d
fi
t3=$t1
days=0
while ((t3 > t2))
do
read -r -u 3 d3 t3 3<<< "$(date -d "$d1 12:00 PM - $days days" '+%m/%d/%Y %s')"
((++days))
echo "$d3"
done
done < inputfile
それはbashだけでうまく行うことができます:
for i in `seq 1 5`;
do
date -d "2017-12-01 $i days" +%Y-%m-%d;
done;
またはパイプ付き:
seq 1 5 | xargs -I {} date -d "2017-12-01 {} days" +%Y-%m-%d
GNU日付(date -d @nnn
フォームに必要であり、おそらく1桁の日に先行ゼロを削除する機能)があり、月):
while read start end ; do
for d in $(seq $(date +%s -d $end) -86400 $(date +%s -d $start)) ; do
date +%-m/%-d/%Y -d @$d
done
done
夏時間を採用しているロケールにいる場合、夏時間の切り替えが間に発生する日付シーケンスを要求すると、これが混乱する可能性があります。 -uを使用してUTCに強制します。これも、1日あたり86400秒を厳密に監視します。このような:
while read start end ; do
for d in $(seq $(date -u +%s -d $end) -86400 $(date -u +%s -d $start)) ; do
date -u +%-m/%-d/%Y -d @$d
done
done
これにstdinで入力をフィードするだけです。
データの出力は次のとおりです。
7/7/2009
7/6/2009
7/5/2009
7/4/2009
7/3/2009
7/2/2009
3/3/1996
3/2/1996
3/1/1996
2/29/1996
2/28/1996
1/4/2002
1/3/2002
1/2/2002
1/1/2002
12/31/2001
12/30/2001
私はISO8601形式の日付を好みます-これはそれらを使用する解決策です。必要に応じて、アメリカのフォーマットに簡単に適合させることができます。
BEGIN {
days[ 1] = 31; days[ 2] = 28; days[ 3] = 31;
days[ 4] = 30; days[ 5] = 31; days[ 6] = 30;
days[ 7] = 31; days[ 8] = 31; days[ 9] = 30;
days[10] = 31; days[11] = 30; days[12] = 31;
}
function leap(y){
return ((y %4) == 0 && (y % 100 != 0 || y % 400 == 0));
}
function last(m, l, d){
d = days[m] + (m == 2) * l;
return d;
}
function prev_day(date, y, m, d){
y = substr(date, 1, 4)
m = substr(date, 6, 2)
d = substr(date, 9, 2)
#print d "/" m "/" y
if (d+0 == 1 && m+0 == 1){
d = 31; m = 12; y--;
}
else if (d+0 == 1){
m--; d = last(m, leap(y));
}
else
d--
return sprintf("%04d-%02d-%02d", y, m, d);
}
{
d1 = $1; d2 = $2;
print d2;
while (d2 != d1){
d2 = prev_day(d2);
print d2;
}
}
このファイルを呼び出します:dates.awk
2009-07-02 2009-07-07
1996-02-28 1996-03-03
2001-12-30 2002-01-04
このファイルを呼び出します:dates.txt
実行されたコマンド:
awk -f dates.awk dates.txt
出力:
2009-07-07
2009-07-06
2009-07-05
2009-07-04
2009-07-03
2009-07-02
1996-03-03
1996-03-02
1996-03-01
1996-02-29
1996-02-28
2002-01-04
2002-01-03
2002-01-02
2002-01-01
2001-12-31
2001-12-30
もう1つのオプションは、dateutilsのdateseqを使用することです( http://www.fresse.org/dateutils/#dateseq )。 -i
入力フォーマットを変更し-f
出力フォーマットを変更します。 -1
は、最初の日付が2番目の日付より後の場合の増分として指定する必要があります。
$ dateseq -i %m/%d/%Y -f %m/%d/%Y 7/7/2009 -1 7/2/2009
07/07/2009
07/06/2009
07/05/2009
07/04/2009
07/03/2009
07/02/2009
$ dateseq 2017-04-01 2017-04-05
2017-04-01
2017-04-02
2017-04-03
2017-04-04
2017-04-05