如何使用BASH脚本的AWK生成给定开始日期和结束日期的日期序列?

如何使用BASH脚本的AWK生成给定开始日期和结束日期的日期序列?,bash,unix,awk,Bash,Unix,Awk,我有一个以下格式的数据集 第一个和第二个字段表示研究开始和结束的日期(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/199

我有一个以下格式的数据集

第一个和第二个字段表示研究开始和结束的日期(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

我更喜欢ISO8601格式的日期-这里有一个使用它们的解决方案。 如果你愿意,你可以很容易地把它改编成美国的格式

AWK脚本 调用此文件:dates.awk

资料 调用此文件: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
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

如果您有
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
输出:

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
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
编辑:

上面的脚本有一个关于天数的天真假设。这是一个小错误,但在某些情况下可能会产生意想不到的结果。这里至少还有一个答案也有这个问题。大概,减去(或添加)天数的
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
读取时-r d1 d2
做
t1=$(日期-d“$d1下午12:00”+%s)
t2=$(日期-d“$d2下午12:00”+%s)
如果((t2>t1))#交换时间/日期(如果需要)
然后
温度=1美元;温度=1美元
t1=t2美元;d1=d2美元
t2=临时费用;d2=临时费用
fi
t3=t1美元
天数=0
而((t3>t2))
做

read-r-u 3 d3 t3 3假设您有GNU date(这是
date-d@nnn
表单所需要的,并且可能能够在一位数的天数和月份中去掉前导零),您可以在shell中不使用awk来执行此操作:

如果您所在的区域设置是夏令时,那么如果请求一个日期序列,而夏令时开关发生在这两者之间,那么这可能会出错。使用-u强制UTC,UTC每天也严格遵守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

仅使用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
另一个选项是使用dateutils()中的dateseq
-i
更改输入格式,
-f
更改输出格式<当第一个日期晚于第二个日期时,必须将代码>-1
指定为增量

$dateseq-i%m/%d/%Y-f%m/%d/%Y 2009年7月7日-1年7月2日
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

您可以将日期转换为unix时间戳,然后对其进行排序,如果需要,您甚至可以拥有纳秒的粒度(日期中有
'%N'

以下示例以
5分钟的间隔打印从
2020-11-07 00:00:00
2020-11-07 01:00:00
的时间

# total seconds past 1970-01-01 00:00:00 as observed on UTC timestamp in UTC
# you change TZ to represent time in your timezone like TZ="Asia/Kolkata"

start_time=$(date -u -d 'TZ="UTC" 2020-11-07 00:00:00' '+%s')   
end_time=$(date -u -d 'TZ="UTC" 2020-11-07 01:00:00' '+%s')


# 60 seconds * 5 times (i.e. 5 minutes)
# you change interval according your needs or leave it to show every second

interval=$((60 * 5))


# generate sequence with intervals and convert back to timestamp in UTC
# again change TZ to represent timein your timezone

seq ${start_time} ${interval} ${end_time} | 
xargs -I{} date -u -d 'TZ="UTC" @'{} '+%F %T'

更简洁地说:
daylist='31 28。。。30 31'; split(daylist,days)
@Tony:这是一个awk脚本,不是bash脚本。放一个
#/usr/bin/awk-f
shebang在顶部,或者用
awk-f
yourself调用它../date_script.sh 2011-01-01 2011-01-02日期:无效日期@1.29396e+09'日期:无效日期@1.29387e+09'为什么会发生这种情况?@Necronet:出于某种原因,你的日期(从纪元算起的秒数)是科学记数法。我不知道为什么,因为我不知道你的
date\u script.sh
脚本中有什么内容。@Necronet你是在Mac还是其他BSD机器上?BSD的日期与GNU coreutil的日期不同。尝试使用自制(
brew install coreutils
)安装,并运行gdate而不是date。@Necronet:在seq中添加浮点数格式选项修复此问题
seq-f'%.f'
为了完成:过去5天=
seq 15 | xargs-I{}date-d{}天之前+%Y-%m-%d
seq
date-d
都是GNUISM。在*BSD(包括Mac OSX)上,您可能需要
jot
date-j
。为了可移植性,我可能会跳到Awk或Perl。从现在起也可以很好地向后工作:
seq 15 | xargs-I{}date-d“now-{}days”+%Y-%m-%d
如果您想迭代几个小时,您可以使用
seq 5-10 | xargs-I{date d-d“now-{days”+%Y-%m-%d>获得从5天前到现在的所有时间,您必须将时间部分放在第一位:
date-d“13:00:00 2017-12-01$i hours”+%Y-%m-%d-%H:%m:%S
这是我一直在寻找的答案:-d OP要求awk或bash,但如果您可以安装dateutils,这就是您应该使用的。
# total seconds past 1970-01-01 00:00:00 as observed on UTC timestamp in UTC
# you change TZ to represent time in your timezone like TZ="Asia/Kolkata"

start_time=$(date -u -d 'TZ="UTC" 2020-11-07 00:00:00' '+%s')   
end_time=$(date -u -d 'TZ="UTC" 2020-11-07 01:00:00' '+%s')


# 60 seconds * 5 times (i.e. 5 minutes)
# you change interval according your needs or leave it to show every second

interval=$((60 * 5))


# generate sequence with intervals and convert back to timestamp in UTC
# again change TZ to represent timein your timezone

seq ${start_time} ${interval} ${end_time} | 
xargs -I{} date -u -d 'TZ="UTC" @'{} '+%F %T'