如何使用 bash 获取当年每个月的最后一天,而不使用 if else 或 switch 或 while 循环?

问题描述 投票:0回答:13

众所周知,每年每个月都有以下最大天数,如下:

Jan - 31 days
Feb - 28 days / 29 days (leap year)
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days

如何让 bash 在不使用

if else
switch
while
循环的情况下返回当年的值(每月的最后一天)?

bash unix
13个回答
53
投票

我的看法:

for m in {1..12}; do
  date -d "$m/1 + 1 month - 1 day" "+%b - %d days"; 
done

解释一下:对于第一次迭代,当 m=1 时,

-d
参数为“1/1 + 1 个月 - 1 天”,“1/1”被解释为 1 月 1 日。因此,1 月 1 + 1 个月 - 1 天是 1 月 31 日。下一次迭代“2/1”是 2 月 1 日,加上一个月减去一天即可得到 2 月 28 日或 29 日。依此类推。


20
投票
cat <<EOF
Jan - 31 days
Feb - `date -d "yesterday 3/1" +"%d"` days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
EOF

8
投票
cal $(date +"%m %Y") |
awk 'NF {DAYS = $NF}; END {print DAYS}'

这使用标准的

cal
实用程序来显示指定的月份,然后运行一个简单的 Awk 脚本来提取最后一天的数字。


7
投票

假设您允许“for”,那么 bash 中将出现以下内容

for m in {1..12}; do
    echo $(date -d $m/1/1 +%b) - $(date -d "$(($m%12+1))/1 - 1 days" +%d) days
done

产生这个

 Jan - 31 days
 Feb - 29 days
 Mar - 31 days
 Apr - 30 days
 May - 31 days
 Jun - 30 days
 Jul - 31 days
 Aug - 31 days
 Sep - 30 days
 Oct - 31 days
 Nov - 30 days
 Dec - 31 days

注意:我删除了

cal

的需要

对于那些喜欢琐事的人:

Number months from 1 to 12 and look at the binary representation in four
bits {b3,b2,b1,b0}.  A month has 31 days if and only if b3 differs from b0.
All other months have 30 days except for February.

因此,除了二月以外,这都有效:

for m in {1..12}; do
    echo $(date -d $m/1/1 +%b) - $((30+($m>>3^$m&1))) days
done

结果:

Jan - 31 days
Feb - 30 days (wrong)
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days

5
投票

尝试使用此代码

date -d "-$(date +%d) days  month" +%Y-%m-%d

5
投票

使用 if 语句或分叉为二进制文件,返回月份中的天数,以补偿闰年中二月的变化,而无需循环。

此代码测试日期以查看所请求年份的 2 月 29 日是否有效,如果有效,则更新日期偏移字符串中的第二个字符。月份参数选择相应的子字符串并将月份差异添加到 28。

function daysin()
{
   s=303232332323                                        # normal year
   ((!($2%4)&&($2%100||!($2%400)))) && s=313232332323      # leap year
   echo $[ ${s:$[$1-1]:1} + 28 ]
}

daysin $1 $2                                               #daysin [1-12] [YYYY]

4
投票

在具有 BSD date 功能的 Mac 上,您可以执行以下操作:

for i in {2..12}; do date -v1d -v"$i"m -v-1d "+%d"; done

快速解释

-v
代表调整。我们正在将日期调整为:

-v1d
代表该月的第一天

-v"$i"m
定义月份,例如(2 月
-v2m

-v-1d
减去一天(所以我们得到上个月的最后一天)

"+%d"
打印该月的日期

for i in {2..12}; do date -v1d -v"$i"m -v-1d "+%d"; done
31
28
31
30
31
30
31
31
30
31
30

您当然可以添加年份。请参阅联机帮助页中的示例(上面的链接)。


1
投票

script.sh
的内容:

#!/bin/bash
begin="-$(date +'%-m') + 2"
end="10+$begin"

for ((i=$begin; i<=$end; i++)); do
    echo $(date -d "$i month -$(date +%d) days" | awk '{ printf "%s - %s days", $2, $3 }')
done

结果:

Jan - 31 days
Feb - 29 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days

1
投票

基于 patm 的答案,使用 BSD

date
for macOS(patm 的答案在 12 月被遗漏):

for i in {1..12}; do date -v1m -v1d -v+"$i"m -v-1d "+%b - %d days"; done
说明:

-v
,当使用BSD
date
时,表示将日期调整为:

-v1m
表示转到第一个月(当年的一月)。

-v1d
表示转到第一天(所以现在是 1 月 1 日)。

-v+"$i"m
表示转到下个月。

-v-1d
表示减去一天。这获取上个月的最后一天。

"+%b - %d days"
是您想要输出的任何格式。

这将输出当年的所有月份以及每个月的天数。以下输出适用于 2022 年截至目前的情况:

Jan - 31 days
Feb - 28 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days

0
投票
for m in $(seq 1 12); do cal  $(date +"$m %Y") | grep -v "^$" |tail -1|grep -o "..$"; done
  • 从 1 迭代到 12(对于...)
  • 打印每个月的日历表(cal...)
  • 从输出中删除空行(grep -v ...)
  • 打印表格中的最后一个数字(尾部...)

避免使用 cal 是没有意义的,因为 POSIX 需要它,所以应该在那里


0
投票

已接受答案的变体,以显示“昨天”的使用

$ for m in {1..12}; do date -d "yesterday $m/1 + 1 month" "+%b - %d days"; done
Jan - 31 days
Feb - 28 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days

如何运作?

日期“month/1”添加 1 个月后显示昨天的日期


0
投票

我需要这几次,所以当 PHP 中带有 easy 时,bash 中则不是, 所以我用这个直到抛出错误“无效算术运算符” 即使在拼写检查中存在冲突(“mt”代表月份,“yr”代表年份)

last=$(echo $(cal ${mt} ${yr}) | awk '{print $NF}')

所以这很好用...

### get last day of month
#
# implement from PHP
# src: https://www.php.net/manual/en/function.cal-days-in-month.php
#
if [ $mt -eq 2 ];then
    if [[ $(bc <<< "${yr} % 4") -gt 0 ]];then
        last=28
    else
        if [[ $(bc <<< "${yr} % 100") -gt 0 ]];then
            last=29
        else
            [[ $(bc <<< "${yr} % 400") -gt 0 ]] && last=28 || last=29
        fi
    fi
else
    [[ $(bc <<< "(${mt}-1) % 7 % 2") -gt 0 ]] && last=30 || last=31
fi

0
投票

遵循格伦·杰克曼的相同答案,但有更新;他的答案最初使用“+ 1个月 - 1天”配置,但是我尝试了他的代码,但它无法正常工作 - 由于某种未知的原因,我的日期命令返回了日期+ 29天(在我的测试事件中,这是 12 月的日期,但返回的是 12 月 30 日,而不是 12 月 31 日)。

我的建议是您将“1个月”切换为“下个月”,这样会正确返回正确的结果。

就我而言,我必须从另一个变量收集年份和月份,所以结果是这样的:

date -d "$AnalysisYear-$AnalysisMonth-01 00:00:00 + next month - 1 second" "+%d"
© www.soinside.com 2019 - 2024. All rights reserved.