unix 列出以一周中的同一天结束的月份

shyt4zoc  于 2022-12-12  发布在  Unix
关注(0)|答案(8)|浏览(158)

这是一个任务。我有以下代码:

#! /bin/bash

y=$1
if [ -z $1 ] # if year is not specified use the current year
    then y=(`date +%Y`)
fi

for m in {1..12}; do
    if [ $m -eq 12 ] # december exception
    then echo $(date -d $m/1/$y +%b) - $(date -d "$(($m%12+1))/1/$y" +%A)
          break 
    fi  
   echo $(date -d $m/1/$y +%b) - $(date -d "$(($m%12+1))/1/$y - 1 days" +%A) # print the last day of the week for the month
done

它列出每月每周的最后一天:

Jan - Monday
Feb - Monday
Mar - Thursday
Apr - Saturday
May - Tuesday
Jun - Thursday
Jul - Sunday
Aug - Wednesday
Sep - Friday
Oct - Monday
Nov - Wednesday
Dec - Saturday

现在我需要反转它,这样它就可以列出以一周中每一天结束的月份,如下所示:

Sunday - Jul
Monday - Jan Feb Oct
Tuesday - May
Wednesday - Aug Nov
Thursday - Mar Jun
Friday - Sep
Saturday - Apr Dec

我想到了一个嵌套循环,

for d in {1..7};

在数组中存储月份?

nhjlsmyf

nhjlsmyf1#

#! /usr/bin/env bash

# if year is not specified use the current year
declare -r year="${1:-$(date +%Y)}"

# associative array (aka hash table)
declare -A months_per_day=()
for m in {01..12}; do
    day_month=$(LANG=C date -d "${year}-${m}-01 +1 month -1 day" +"%A %b")
    months_per_day[${day_month% *}]+=" ${day_month#* }"
done

for day in Sunday Monday Tuesday Wednesday Thursday Friday Saturday; do
    echo "${day} -${months_per_day[${day}]:-}"
done

输出量:

Sunday - Jul
Monday - Jan Feb Oct
Tuesday - May
Wednesday - Aug Nov
Thursday - Mar Jun
Friday - Sep
Saturday - Apr Dec
o2rvlv0m

o2rvlv0m2#

使用GNU awk for time函数,使用大量中间变量和描述性命名的变量,使其易于理解:

$ cat tst.sh
#!/usr/bin/env bash

awk -v year="$1" '
    BEGIN {
        OFS = " - "

        year = (year == "" ? strftime("%Y") : year)
        secsInDay = 24*60*60

        for ( mthNr=1; mthNr<=12; mthNr++ ) {
            lastDayEpochSecs = mktime(year " " (mthNr+1) " 1 12 0 0") - secsInDay
            mthAbbrDayName = strftime("%b %A", lastDayEpochSecs)
            split(mthAbbrDayName,m)
            mthAbbr = m[1]
            dayName = m[2]
            mthNr2mthAbbr[mthNr] = mthAbbr
            mthAbbr2dayName[mthAbbr] = dayName
            dayName2mthAbbrs[dayName] = \
                (dayName in dayName2mthAbbrs ? dayName2mthAbbrs[dayName] " " : "" ) mthAbbr
        }

        for ( mthNr=1; mthNr<=12; mthNr++ ) {
            mthAbbr = mthNr2mthAbbr[mthNr]
            dayName = mthAbbr2dayName[mthAbbr]
            print mthAbbr, dayName
        }

        print "\n--------\n"

        for ( dayName in dayName2mthAbbrs ) {
            mthAbbrs = dayName2mthAbbrs[dayName]
            print dayName, mthAbbrs
        }
    }
'
$ ./tst.sh
Jan - Monday
Feb - Monday
Mar - Thursday
Apr - Saturday
May - Tuesday
Jun - Thursday
Jul - Sunday
Aug - Wednesday
Sep - Friday
Oct - Monday
Nov - Wednesday
Dec - Saturday

--------

Tuesday - May
Friday - Sep
Sunday - Jul
Thursday - Mar Jun
Saturday - Apr Dec
Monday - Jan Feb Oct
Wednesday - Aug Nov

上面的代码比在shell循环中多次调用date要快得多,而且修改它来做任何其他需要的事情都很简单。

tyg4sfes

tyg4sfes3#

这个答案将你的实现重构为一个可重用的getlastday函数。然后,我们将getlastday与匹配的months进行7次循环匹配,并将其输出:

#!/bin/bash

getlastday() {
    if [ $m -eq 12 ]; then
        echo $(date -d "$(($m%12+1))/1/$y" +%A)
        return
    fi
    echo $(date -d "$(($m%12+1))/1/$y - 1 days" +%A) # print the last day of the week for the month
}

y=$1
if [ -z $1 ] # if year is not specified use the current year
    then y=(`date +%Y`)
fi

for d in Monday Tuesday Wednesday Thursday Friday Saturday Sunday; do
    months=()
    for m in {1..12}; do
        lastday=$(getlastday)
        if [ $lastday != $d ]; then continue; fi
        months+=($(date -d $m/1/$y +%b))
    done
    echo $d - ${months[@]}
done

[EDIT:版本2]
正如下面@markp-fuso的评论所说,上面的方法效率低下,因为需要84次迭代来构建列表。
下面是一个改进,我们构造了一个res结果数组,其中有7个条目。一个条目代表一周中的每一天。然后我们使用+%u来获取日期编号,而不是日期字符串。这将为我们提供res结果附加的索引,在那里附加月份:

#!/bin/bash

y=$1
if [ -z $1 ] # if year is not specified use the current year
    then y=(`date +%Y`)
fi

res=("Sunday    -" "Monday    -" "Tuesday   -" "Wednesday -" "Thursday  -" "Friday    -" "Saturday  -")
for m in {1..12}; do
    mstr=$(date -d $m/1/$y +%b)
    if [ $m -eq 12 ]; then
        d=$(date -d "$(($m%12+1))/1/$y" +%u)
    else
        d=$(date -d "$(($m%12+1))/1/$y - 1 days" +%u)
    fi
    res[$d]="${res[$d]} $mstr"
done
for d in {0..6}; do
    echo "${res[$d]}"
done
xiozqbni

xiozqbni4#

使用jq可以解决您的任务:

INPUT='
Jan - Monday
Feb - Monday
Mar - Thursday
Apr - Saturday
May - Tuesday
Jun - Thursday
Jul - Sunday
Aug - Wednesday
Sep - Friday
Oct - Monday
Nov - Wednesday
Dec - Saturday
'

jq -Rrs '
  (split("\n") | map(split(" - "))) as $input |   # split lines and split each line by "-"
  reduce ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") as $day   # iterate over all weekdays
      ([]; . + [ ($input | map(select(.[1] == $day)) |        # select entries for $day
                 .[0][1] + " - " + (map(.[0]) | join(" ")))   # generate output for $day
               ]
      ) | .[]
' <<< "$INPUT"

输出

Sunday - Jul
Monday - Jan Feb Oct
Tuesday - May
Wednesday - Aug Nov
Thursday - Mar Jun
Friday - Sep
Saturday - Apr Dec
ohtdti5x

ohtdti5x5#

尝试遵循此算法以获得预期结果:

# Initialize an array to hold the months that end on each day of the week

# Loop over the months from the starting month/year to the ending month/year

    # get the last day of the month

    # get the day of the week for the last day of the month

    # Append the month to the array for the corresponding day of the week

 
# Loop over the days of the week and print the list of months that end on each day

我希望它能帮助你

mlnl4t2r

mlnl4t2r6#

尝试以下Shellcheck-clean代码:

#! /bin/bash -p

year=${1-$(date +%Y)}

months=( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec )
day_months=(    'Sunday    -'   'Monday    -'   'Tuesday   -'   'Wednesday -'
                'Thursday  -'   'Friday    -'   'Saturday  -'                )

for m in {1..12}; do
    last_day=$(date -d "$year-$m-1 +1 month -1 day" +%w)
    day_months[last_day]+=" ${months[m-1]}"
done

printf '%s\n' "${day_months[@]}"
dzjeubhm

dzjeubhm7#

declare -A a
for i in '1/1 + 1 year' {2..12}/1; do
    d=($(LC_ALL=C date '+%A %B' -d "$i - 1 second"))
    a["$d"]+=${a["$d"]:+ }${d[1]}
done

for i in {Sun,Mon,Tues,Wednes,Thurs,Fri,Satur}day; do
    echo "$i - ${a["$i"]}"
done

这将打印当前年份的列表:

Sunday - July
Monday - January February October
Tuesday - May
Wednesday - August November
Thursday - March June
Friday - September
Saturday - December April
j91ykkif

j91ykkif8#

从技术上讲,您需要的唯一信息是一月的结束日期,以及(a)一年是否是闰年,或者(B)二月是否与一月在同一周的同一天结束。
其余的不用运行date 12次就可以很容易地推导出来

相关问题