问题:对于最小和最大日期的参数(例如:script.sh 20190801、20201005),在该日期范围内的每个月执行一个函数。 (例如:20190801-20190831、20190901-20190931……)
背景故事:我有一个函数可以根据日期范围内的数据生成 CSV,但是对于较大的日期范围,运行时间太长。考虑到这一点,我尝试每月同时运行一次,然后将 CSV 重新拼接在一起。我会在 Python 或 Javascript 中尝试类似的操作并使用日期时间库,但某些公司限制要求我专门使用 bash/shell。
以下是我目前的想法,但大多数情况下都失败
我通过传递得到了预期的结果,例如: 20190101 20201231 到下面的代码,但我不确定如何修改它以支持类似 20190105 20200820 的内容
DateMin=$1
DateMax=$2
if [ $DateMin -ge $DateMax ]; then
echo "ERROR: Minimum Date Cannot be above Maximum Date"
exit
fi
let DateDelta=DateMax-DateMin
for YearDelta in `seq $DateDelta -10000 0`
do
let TempDelta=$YearDelta-1130
for MonthDelta in `seq $YearDelta -100 $TempDelta`
do
let TempMin=$DateMax-$MonthDelta
let TempMax=$DateMax-$MonthDelta+30
call_to_function $TempMin $TempMax
done
done
我在这里创建了一个 repl.it 以便更容易查看:https://repl.it/repls/CanineAdmirableSeahorse#main.sh 如果您运行“bash main.sh 20190101 20201231”,这就是预期的输出。
答案1
怎么样使用date
程序,它就在那里,可以用日期计算很多东西。这是一个例子:
DateMinStr="${1:0:4}-${1:4:2}-01"
DateMaxStr="${2:0:4}-${2:4:2}-${2:6:2}"
DateMin=$(date -d $DateMinStr +%s)
DateMax=$(date -d $DateMaxStr +%s)
if [ $DateMin -ge $DateMax ]; then
echo "ERROR: Minimum Date Cannot be above Maximum Date"
exit
fi
DateCurrentStr=$DateMinStr
DateCurrent=$DateMin
while [ $DateCurrent -lt $DateMax ]
do
TempMin=$(date -d $DateCurrentStr +%Y%m%d)
TempMinStr=$(date -d $DateCurrentStr +%Y-%m-%d)
TempMax=$(date -d "$TempMinStr + 1 month - 1 day" +%Y%m%d)
TempMaxStr=$(date -d "$TempMinStr + 1 month - 1 day" +%Y-%m-%d)
echo $TempMin $TempMax
DateCurrent=$(date -d "$TempMaxStr + 1 day" +%s)
DateCurrentStr=$(date -d "$TempMaxStr + 1 day" +%Y-%m-%d)
done
我认为这个示例并没有完全满足您的要求,并且远未优化,但可以让您了解如何做到这一点。
答案2
类似的工具日期工具通常更适合此类工作,但可以使用 shell 原语来完成,为简洁起见,这里使用与您不同的参数集。
#!/bin/sh
# For a date range print start and end date per calendar month.
# Gregorian calendar: number of days in month.
# $1 year (>= 1600)
# $2 month (1..12), one leading '0' allowed for 1..9
# exitval: day count (28..31)
daysinmonth() {
case "$2" in
(2|02)
test 0 -eq $(($1 % 4 || !($1 % 100) && $1 % 400))
return $((28 + ($?<1))) ;;
(4|6|9|11|04|06|09)
return 30 ;;
(*) return 31 ;;
esac
}
# Print year,month,day in ISO 8601 basic format
isobasic() {
printf '%d%02d%02d' "$1" "$2" "$3"
}
ylo="$1" mlo="${2#0}" dlo="${3#0}" # range start
yhi="$4" mhi="${5#0}" dhi="${6#0}" # range end
isohi=$(isobasic "${yhi}" "${mhi}" "${dhi}")
until
isolo=$(isobasic "${ylo}" "${mlo}" "${dlo}")
test $(( isolo - isohi )) -gt 0
# test "${isolo}" \> "${isohi}" # dash, bash
do
# compute end of month
daysinmonth "${ylo}" "${mlo}"
isoeom=$(isobasic "${ylo}" "${mlo}" "${?}")
# print start, end date for partial/whole calendar month
test "${isoeom%??}" != "${isohi%??}" || isoeom="${isohi}"
printf '%s%s %s\n' "${PREFIX:-}" "${isolo}" "${isoeom}"
# increase *lo values to first of next month
dlo=1
: $(( mlo = (mlo == 12 ? 1 : mlo + 1) ))
: $(( ylo = (mlo > 1 ? ylo : ylo + 1) ))
done
# fail if start later than end date
return $(( isoeom == isohi ? 0 : 1 ))
给定命令
PREFIX='cmd ' ./script 2016 11 09 2020 3 5
输出是:
cmd 20161109 20161130
cmd 20161201 20161231
cmd 20170101 20170131
cmd 20170201 20170228
...
cmd 20191201 20191231
cmd 20200101 20200131
cmd 20200201 20200229
cmd 20200301 20200305