Bash:IF条件下的日期比较未按预期工作

Bash : Date comparison in IF condition not working as expected

提问人:Luqman 提问时间:10/12/2023 最后编辑:JL00001Luqman 更新时间:10/13/2023 访问量:63

问:

echo "Input start date and time (yyyy-mm-dd hh:mm) :"
read start_date_time
echo "Input end date and time(yyyy-mm-dd hh:mm) :" 
read end_date_time
  
while [[ $(date -d "$start_date_time" +%Y-%m-%d\ %H:%M 2>/dev/null) != $start_date_time || $(date -d "$end_date_time" +%Y-%m-%d\ %H:%M 2>/dev/null) != $end_date_time ]]

do
    echo "Invalid input! Please reinsert in the correct format."
    echo "Start date and time :"
    read start_date_time
    echo "End date and time :"
    read end_date_time

    if [[ $(date -d "$start_date_time" +%s) -gt $(date -d "$end_date_time" +%s) ]]
    then
        echo "Start datetime cannot be greater than end datetime."
    continue
    fi

done

此脚本中的条件不起作用,即使 when 大于 .我什至尝试了以下方法,但无济于事:ifecho$start_date_time$end_date_time

if [[ $start_date_time > $end_date_time ]]

关于如何解决这个问题,我已经没有想法了。

Linux Bash Shell IF-语句 UNIX

评论

3赞 Todd A. Jacobs 10/12/2023
将您的日期转换为自纪元以来的秒数。Bash 本身并不真正了解如何解析日期,并且您的 while 条件存在语法错误。你对它运行了shellcheck吗?
0赞 Luqman 10/12/2023
@ToddA.Jacobs 正如你所看到的,我确实把它转换成了秒。我还对它进行了 shellcheck,但条件本身没有错误。奇怪的是,如果我将条件放在循环之外,它就会起作用。ifif
1赞 John Bollinger 10/12/2023
专业提示:对于调试,请向脚本添加 a,使其打印正在执行的(展开)命令。set -x
1赞 John Bollinger 10/12/2023
这样做可以帮助您认识到,当您输入有效日期时,甚至没有达到您所询问的比较。它位于循环的主体内部,仅当一个或两个日期的格式不正确时才会执行循环。
0赞 Luqman 10/12/2023
@JohnBollinger 啊,是的,谢谢。这是完全有道理的。这就是为什么当它被放置在循环之外时会触发条件的原因。if

答:

1赞 Paul Hodges 10/12/2023 #1

正如 John Bollinger 在他的评论中指出的那样,如果你在顶部输入有效的日期时间值,它永远不会进入循环,因此永远不会检查结束大于开始。

如果在开始时输入无效值,它会进入循环并要求输入新值,然后检查 start > end 和 s,但唯一测试的是两个日期是否有效,因此循环会退出,而不会让您重新输入它们。continuewhile

此外,您的代码目前允许开始和结束相同而没有抱怨,这可能不是您想要的。

考虑使用函数进行重构以消除冗余代码:

take_datetime() {
  local dt= prompt="${1:-Please input a valid date and time(yyyy-mm-dd hh:mm) :}"
  read -p "$prompt" dt
  until date -d "$dt" "+%Y-%m-%d %H:%M"
  do dt=$(take_datetime "$prompt")
  done
}

declare -i stst=0 etst=0
until (( etst > stst ))
do ((stst)) && printf "\n *** Ending datetime must be greater than starting datetime ***\n"
   start=$( take_datetime "Input START date and time (yyyy-mm-dd hh:mm) : " )
   end=$(   take_datetime "Input END   date and time (yyyy-mm-dd hh:mm) : " )
   stst=$(  date -d "$start" "+%s" )
   etst=$(  date -d "$end"   "+%s" )
done

echo "START: $start"
echo "END  : $end"

这目前允许使用任何有效的日期/时间格式,并将其转换为所需的格式。

$: ./tst
Input START date and time (yyyy-mm-dd hh:mm) : 1234-11-12 11:13
Input END   date and time (yyyy-mm-dd hh:mm) : 11/12/1234 11:14
START: 1234-11-12 11:13
END  : 1234-11-12 11:14

这确实留下了混淆月份和日期的可能性,例如欧洲与美国的规范。要像以前一样强制输入格式,请更新函数:

take_datetime() {
  local dt= prompt="${1:-Please input a valid date and time(yyyy-mm-dd hh:mm) :}"
  read -p "$prompt" dt
  until date -d "$dt" >/dev/null && [[ $( date -d "$dt" "+%Y-%m-%d %H:%M" ) == "$dt" ]]
  do dt=$( take_datetime "Please check your format; $prompt" )
  done
  echo "$dt"
}

这应该可以做到:

$: ./tst
Input START date and time (yyyy-mm-dd hh:mm) : foo
date: invalid date ‘foo’
Please check your format; Input START date and time (yyyy-mm-dd hh:mm) : 1234-11-12 11:13
Input END   date and time (yyyy-mm-dd hh:mm) : 11/12/1234 11:14
Please check your format; Input END   date and time (yyyy-mm-dd hh:mm) : 1234-11-12 11:14
START: 1234-11-12 11:13
END  : 1234-11-12 11:14

Namerefs 可能会让它更漂亮......