在 bash 中使用 bash 循环迭代日期 - for (( ... )) 无法正常工作

Iterating over dates with a bash loop in bash -- for (( ... )) not working correctly

我正在尝试创建一个日期列表。下面的脚本适用于没有空格的简单日期。

datestart=20130601
dateend=20130705

for (( date1="$datestart"; date1 != dateend; )); do
    date1="$(date --date="$date1 + 1 days" +'%Y%m%d')";
    echo $date1;
done

当我使用像(包含 WHITE SPACE)datestart="2013-06-01 00:00:00" 这样的数据字符串和像 +'Y-%m-%d %H:%M:%S'[=15 这样的日期格式时=]

datestart="2013-06-01 00:00:00"
dateend="2013-07-05 00:00:00"
for (( date1="$datestart"; date1 != "$dateend"; )); do
    date1="$(date --date=""$date1" + 1 days" +'%Y-%d-%m %H:%M:%S')";        
    echo "$date1";
done

我收到以下错误:

-bash: ((: date1=2013-06-01 00:00:00: syntax error in expression (error token is "00:00:00")

我想我没有正确引用我的变量。我胡思乱想,现在我在这里。如何在 for 循环中正确引用变量?

不可能,bash 三表达式 for 循环需要算术表达式。看到这个 link:

http://wiki.bash-hackers.org/syntax/ccmd/c_for

改用经典的 "while" 循环。

附录

您可以使用纪元中的秒数来允许算术,例如:

datestart=$(date --date="2013-06-01 00:00:00" +%s)
dateend=$(date --date="2013-07-05 00:00:00" +%s)
for (( date1=$datestart; date1 != $dateend; date1+=86400 )); do
    date --date=@$date1
done

但要注意没有 86400 秒的日子。

这应该适合你:

datestart="2013-06-01 00:00:00"
dateend="2013-07-05 00:00:00"

date1="$datestart"
while [[ "$date1" != "$dateend" ]]; do
   date1="$(date -u --date="$date1 tomorrow" '+%Y-%m-%d %H:%M:%S')"
   echo "$date1"
done

Working Demo

  1. ((...)) 仅用于算术运算。请改用 while 循环。
  2. 无需为 $date
  3. 使用嵌套引号
  4. 使用tomorrow获取下一个日期
  5. 在将下一个日期分配给 date1
  6. 时使用正确的 year-month-date 格式

关于你的报价被关闭,你是对的。你有:

date1="$(date --date=""$date1" + 1 days" +'%Y-%d-%m %H:%M:%S')";        

但是你正在以非常奇怪的方式使用双引号。一个快速的解决方案可能是切换到单引号,如果变量在双引号内,它不会隔离变量:

date1="$(date --date="'$date1' + 1 days" +'%Y-%d-%m %H:%M:%S')";        

至于其他部分...让我们回顾一下 bash 手册页上的内容:

for (( expr1 ; expr2 ; expr3 )) ; do list ; done
      First, the arithmetic expression expr1 is evaluated according to
      the  rules  described  below  under  ARITHMETIC EVALUATION. ...

如果您查看手册页的 ARITHMETIC EVALUATION 部分,您会发现它 包含 /bin/test 的各种测试或者 [[ ... ]] 可以 运行。这些内容在手册页的下一部分 CONDITIONAL EXPRESSIONS.

中介绍

如果你想使用 for 循环,那么我也会使用 @pasaba 的建议使用纪元秒,以便坚持算术。像这样:

#!/bin/bash

datestart="2013-06-01 00:00:00"
dateend="2013-07-05 00:00:00"

e_start=$(date -d "$datestart" '+%s')
e_end=$(date -d "$dateend" '+%s')

for (( date1=$e_start; date1 < $e_end; date1+=86400 )); do
    echo -n "$date1 "; date -d "@$date1" '+%Y-%m-%d'
done

考虑到闰年和闰秒等,你可以相信 Linux date 命令,并像你原来那样在循环中计算 $date1 :

for (( date1=$e_start; date1 < $e_end; )); do
    date1=$(date -d "$(date -d "@$date1") + 1 day" '+%s')
    echo -n "$date1 "; date -d "@$date1" '+%Y-%m-%d'
done

嵌套的 date 命令是必需的,因为 Linux 的 date 命令不允许您使用相对日期(“+1day”或 "tomorrow")当起始日期指定为 @ 的纪元时。 (我很想知道我是否错了。)


我知道你的问题被标记为 "Linux",但我会在以后的搜索中注意到这是 date 的不可移植(Linux-only)使用命令,因此如果您希望此脚本在 FreeBSD、NetBSD、OSX 等中 运行,您需要查看它们的用法。以下适用于 FreeBSD:

#!/usr/bin/env bash

datestart="2015-06-01 00:00:00"
dateend="2015-07-05 00:00:00"

e_start="$(date -jf '%Y-%m-%d %T' "$datestart" '+%s')"
e_end="$(date -jf '%Y-%m-%d %T' "$dateend" '+%s')"

for (( date1 = $e_start; date1 < $e_end; )); do
    date1=$(date -j -v+1d -f '%s' "$date1" '+%s')
    echo -n "$date1 "; date -jf '%s' "$date1" '+%Y-%m-%d'
done