shell - 如何在shell脚本中设置日期范围

标签 shell

我正在 shell 脚本中编写代码来加载特定范围内的数据,但它不会停止在我想要的数据处,而是超出了该范围。下面是我的shell脚本代码。

j=20180329
while [ $j -le 20180404]
do

我有一个问题,我的循环在日期 20180331 之后运行到 20180399,然后转到 20180401。 我希望它从 20180331 到 20180401。而不是 20180332 等等

最佳答案

一个简单的问题,3 个以上不那么简短的答案...

正如您的请求代表

1。首先兼容的答案

j=20180329
while [ "$j" != "20180405" ] ;do
    echo $j
    j=`date -d "$j +1 day" +%Y%m%d`
done

注意我使用一天后作为while条件基于平等!当然,将 YYYYMMDD 日期解释为整数也可以:

注释 2 关心时区设置 TZ=UTC 进一步查看问题...

j=20180329
while [ $j -le 20180404 ] ;do
    echo $j
    j=`TZ=UTC date -d "$j +1 day" +%Y%m%d`
done

但我不喜欢这样,因为如果时间格式发生变化,这可能会成为一个问题。

下测试和 dashbusybox
(使用日期(GNU coreutils)8.26

1.2 POSIX shell下最小化fork

使用之前 bashisms,这是在任何 POSIX shell 下执行此操作的一种方法:

POSIX shell 的强大之处在于我们可以使用非常简单的转换器,例如日期,并对结果执行条件:

#!/usr/bin/env sh

tempdir=$(mktemp -d)
datein="$tempdir/datein"
dateout="$tempdir/dateout"
mkfifo "$datein" "$dateout"

exec 5<>"$datein"
exec 6<>"$dateout"
stdbuf -i0 -o0 date -f - <"$datein" >"$dateout" +'%Y%m%d' &
datepid=$!

echo "$2" >&5
read -r end <&6
echo "$1" >&5
read -r crt <&6

while [ "$crt" -le "$end" ];do
    echo $crt
    echo "$crt +1 day" >&5
    read -r crt <&6
done

exec 5>&-
exec 6<&-
kill "$datepid"
rm -fR "$tempdir"

然后

daterange.sh 20180329 20180404
20180329
20180330
20180331
20180401
20180402
20180403
20180404

2。 通过 printf

获取日期

,你可以使用所谓的bashisms:

将日期转换为整数 Epoch (Unix time) ,但是通过一个fork获得两个日期:

{
    read start;
    read end
} < <(date -f - +%s <<eof
20180329
20180404
eof
)

start=20180329
end=20180404
{ read start;read end;} < <(date -f - +%s <<<$start$'\n'$end)

然后使用 内置 printf 命令(注意:每天有 $[24*60*60] -> 86400 秒)

for (( i=start ; i<=end ; i+=86400 )) ;do
    printf "%(%Y%m%d)T\n" $i
done

3。时区问题!!

警告夏季冬季时间存在问题:

作为函数

dayRange() { 
    local dR_Start dR_End dR_Crt
    { 
        read dR_Start
        read dR_End
    } < <(date -f - +%s <<<${1:-yesterday}$'\n'${2:-tomorrow})
    for ((dR_Crt=dR_Start ; dR_Crt<=dR_End ; dR_Crt+=86400 )) ;do
        printf "%(%Y%m%d)T\n" $dR_Crt
    done
}

显示问题:

TZ=CET dayRange 20181026 20181030
20181026
20181027
20181028
20181028
20181029

printf "%(%Y%m%d)T\n"$dR_Crt 替换为 printf "%(%Y%m%dT%H%M)T\n “$dR_Crt 可以帮助:

20181026T0000
20181027T0000
20181028T0000
20181028T2300
20181029T2300

为了避免此问题,您只需在函数开始时本地化 TZ=UTC:

    local dR_Start dR_End dR_Crt TZ=UTC

函数的最后一步:避免无用的 fork

为了提高性能,我尝试减少 fork ,避免使用如下语法:

    for day in $(dayRange 20180329 20180404);do ...
    # or
    mapfile range < <(dayRange 20180329 20180404)

我使用函数的能力来直接设置提交的变量:

这就是我的目的:

dayRange() { # <start> <end> <result varname>
    local dR_Start dR_End dR_Crt dR_Day TZ=UTC
    declare -a dR_Var='()'
    { 
        read dR_Start
        read dR_End
    } < <(date -f - +%s <<<${1:-yesterday}$'\n'${2:-tomorrow})
    for ((dR_Crt=dR_Start ; dR_Crt<=dR_End ; dR_Crt+=86400 )) ;do
        printf -v dR_Day "%(%Y%m%d)T\n" $dR_Crt
        dR_Var+=($dR_Day)
    done
    printf -v ${3:-dRange} "%s" "${dR_Var[*]}"
}

然后快速进行小错误测试:

TZ=CET dayRange 20181026 20181030 bugTest
printf "%s\n" $bugTest 
20181026
20181027
20181028
20181029
20181030

看起来不错。这可以像这样使用:

dayRange 20180329 20180405 myrange
for day in $myrange ;do
    echo "Doing something with string: '$day'."
done

2.2 使用 shell-connector 的替代方案

有一个shell函数可以添加后台命令以减少fork。

wget https://f-hauri.ch/vrac/shell_connector.sh
. shell_connector.sh

启动后台日期+%Y%m%d并测试:@0必须回答19700101

newConnector /bin/date '-f - +%Y%m%d' @0 19700101

然后

j=20190329
while [ $j -le 20190404 ] ;do
    echo $j; myDate "$j +1 day" j
done

3.++小板凳

让我们尝试一下 3 年的范围:

j=20160329
time while [ $j -le 20190328 ] ;do
    echo $j;j=`TZ=UTC date -d "$j +1 day" +%Y%m%d`
done | wc
1095    1095    9855

real    0m1.887s
user    0m0.076s
sys     0m0.208s

在我的系统上超过 1 秒...当然,有 1095 个 fork !

time { dayRange 20160329 20190328 foo && printf "%s\n" $foo | wc ;}
1095    1095    9855

real    0m0.061s
user    0m0.024s
sys     0m0.012s

只有 1 个 fork,然后是 bash 内置函数 -> 不到 0.1 秒...

并使用newConnector函数:

j=20160329
time while [ $j -le 20190328 ] ;do echo $j
    myDate "$j +1 day" j
  done | wc
   1095    1095    9855

real    0m0.109s
user    0m0.084s
sys     0m0.008s

虽然不如使用内置整数快,但还是很快。

关于shell - 如何在shell脚本中设置日期范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55952605/

相关文章:

php - 将 tiff(带路径)转换为 png 并删除背景(透明)- 在 PHP 中使用 Imagemagick

linux - 逐行读取文件并在每行中打印第一个匹配项,或者在没有匹配项时打印 "no_data"

linux - 时间通过领先的 sudo 调用产生奇怪的结果

regex - 如何在 awk 中使用带空格的 Sed

linux - 通过 linux 命令检查主内存使用情况

bash - Jenkins - 将参数传递给 groovy 函数

linux - 使用 bash 访问 getopts 选项中的第二个参数

macos - 从 OS X Dock 执行 Shell 脚本?

linux - PERL sh : script_init: command not found

linux - 如何在给定 pid 的远程机器上恢复停止的作业?