Unix shell脚本中的日期算术 [英] Date arithmetic in Unix shell scripts

查看:156
本文介绍了Unix shell脚本中的日期算术的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在Unix shell脚本中进行日期算术,用于控制第三方程序的执行。



我正在使用一个函数来增加一天,另一个减少:

  IncrementaDia(){
echo $ 1 | awk'
BEGIN {
diasDelMes [1] = 31
diasDelMes [2] = 28
diasDelMes [3] = 31
diasDelMes [4] = 30
diasDelMes [5] = 31
diasDelMes [6] = 30
diasDelMes [7] = 31
diasDelMes [8] = 31
diasDelMes [9] = 30
diasDelMes [10] = 31
diasDelMes [11] = 30
diasDelMes [12] = 31
}
{
anio = substr($ 1,1, 4)
mes = substr($ 1,5,2)
dia = substr($ 1,7,2)

if((anio%4 == 0&&& amp ; anio%100!= 0)|| anio%400 == 0)
{
diasDelMes [2] = 29;
}

if(dia == diasDelMes [int(mes)]){
if(int(mes)== 12){
anio = anio + 1
mes = 1
dia = 1
} else {
mes = mes + 1
dia = 1
}
} else {
dia = dia + 1
}
}
END {
printf(%04d%02d%02d,anio,mes,dia)
}
'
}

if [$#-eq 1];那么
tomorrow = $ 1
else
today = $(date +%Y%m%d)
tomorrow = $(IncrementaDia $ hoy)
fi

但现在我需要做更复杂的算术。



这是最好的和更兼容的方式吗?

解决方案

p>我已经写了一个bash脚本,用于将用英语表达的日期转换为常规
mm / dd / yyyy日期。它被称为 ComputeDate



以下是其使用的一些示例。为简洁起见,我将每个调用
的输出放在与冒号(:)分开的调用相同的行上。运行 ComputeDate 时,下面显示的引号是

  $ ComputeDate'昨天':03/19/2010 
$ ComputeDate'yes':03/19/2010
$ ComputeDate'today':03/20/2010
$ ComputeDate'tod' 03/20/2010
$ ComputeDate'now':03/20/2010
$ ComputeDate'tomorrow':03/21/2010
$ ComputeDate'tom':03/21/2010
$ ComputeDate '10 / 29/32':10/29/2032
$ ComputeDate'October 29':10/1/2029
$ ComputeDate'2010年10月29日':10 / 29/2010
$ ComputeDate'this monday':'this monday'has passed。你的意思是下一个星期?
$ ComputeDate'今后一周':03/27/2010
$ ComputeDate'this satu':03/20/2010
$ ComputeDate'next星期一:03/22/2010
$ ComputeDate'next thur':03/25/2010
$ ComputeDate'mon in 2 weeks':03/28/2010
$ ComputeDate'最后一天的日子:03/31/2010
$ ComputeDate'feb'的最后一天:2/28/2010
$ ComputeDate'feb 2000'的最后一天:2/29 / 2000
$ ComputeDate'从昨天起1周':03/26/2010
$ ComputeDate'今天1周':03/27/2010
$ ComputeDate'从明天起1周': 03/28/2010
$ ComputeDate'从昨天起的2周':4/2/2010
$ ComputeDate'今天2周':4/3/2010
$ ComputeDate'2周从明天起:4/4/2010
$ ComputeDate'3月的最后一天后1周:4/7/2010
$ ComputeDate'下周四之后1周':4/1/2010
$ ComputeDate'3月的最后一个星期之后':4 / 14/2010
$ ComputeDate'3月的最后一天1周后的2周内:4/15/2010
$ ComputeDate'3月的最后一天后1天:4/1/2010
$ ComputeDate'今天之后1天后1天后1天之后':03/24/2010

我已经将这个脚本作为这个问题的答案,因为它说明了如何通过一组bash函数对
进行日期算术,而这些函数可能对其他人来说可能是有用的
。它正确地处理闰年和闰年:

 #! / bin / bash 
#ConvertDate - 将可读取的日期转换为MM / DD / YY日期

#Date :: =月/日/年
# |月/日
#| DayOfWeek
#| [this | next] DayOfWeek
#| DayofWeek [in | in] [Number | next] weeks [s]
#|日期中的[日|周] [s]日期
#|本月最后一天
#|月的最后一天

#Month :: = January |二月三月四月|五月... |十二月
#January :: = jan |一月| 1
#February :: = feb |一月| 2
#...
#十二月:: = dec |十二月| 12
#Day :: = 1 | 2 | ... | 31
#DayOfWeek :: = today |星期日|星期一|星期二| ... |星期六
#星期日:: =太阳*
#...
#星期六:: = sat *

#Number :: = Day | a

#作者:Larry Morell

如果[$#= 0];然后
printdirections $ 0
退出
fi



#请求变量的值
GetVar(){
Var = $ 1
echo -n$ Var = [$ {!Var}]:
local X
读取X
如果[! -z $ X];那么
eval $ Var =$ X
fi
}

IsLeapYear(){
local Year = $ 1
if [$ [20 $ Year%4] -eq 0];那么
echo yes
else
echo no
fi
}

#AddToDate - 计算同一年内的另一个日期

DayNames =(mon tue wed thu fri sat sun)#对应于'date'输出

Day2Int(){
ErrorFlag =
case $ 1 in
-e)
ErrorFlag = -e; shift
;;
esac
local dow = $ 1
n = 0
while [$ n -lt 7 -a $ dow!=$ {DayNames [n]}]; do
let n ++
done
if [-z$ ErrorFlag-a $ n -eq 7];那么
echo无法将$ dow转换为一个数字的天数
exit
fi
echo $ [n + 1]

}

Months =(31 28 31 30 31 30 31 31 30 31 30 31)
MonthNames =(jan feb mar apr may jun jul aug sep oct nov dec)
#返回月份(1 -12)从日期或月份名称
Month2Int(){
ErrorFlag =
case $ 1
-e)
ErrorFlag = -e; shift
;;
esac
M = $ 1
月份= $ {M %% / *}#删除/ ...
案例$ month
[az] *)
月份= $ {月:0:3}
M = 0
同时[$ M -lt 12 -a $ {MonthNames [M]}!= $ Month];做
让M ++
完成
让M ++
esac
如果[-z$ ErrorFlag-a $ M -gt 12];那么
echo'$ Month'不是一个有效的月份。
exit
fi
echo $ M
}

#从合法日期检索月,日,年
GetMonth(){
echo $ {1 %% / *}
}

GetDay(){
echo $ 1 | col / 2
}

GetYear(){
echo $ {1 ## * /}
}


AddToDate(){

本地日期= $ 1
本地日= $ 2
本地月=`GetMonth $ Date`
本地日=`echo $ Date | col / 2`#日期日期
local Year =`echo $ Date | col / 3`#年份
local LeapYear =`IsLeapYear $ Year`

if [$ LeapYear =yes];然后
让月份[1] ++
fi
Day = $ [Day + days]
while [$ Day -gt $ {Months [$ Month-1]}] ; do
Day = $ [Day - $ {Months [$ Month-1]}]
let Month ++
done
echo$ Month / $ Day / $ Year
}

#将日期转换为正常形式
NormalizeDate(){
Date =`echo$ *| sed'sX * X / Xg'`
local Day =`date +%d`
local Month =`date +%m`
local年=`date +%Y`
#echo规范化日期= $ Date> / dev / tty
case $日期
* / * / *)
Month =`echo $ Date | col / 1`
Month =`Month2Int $ Month`
Day =`echo $ Date | col / 2`
Year =`echo $ Date | col / 3`
;;
* / *)
Month =`echo $ Date | col / 1`
Month =`Month2Int $ Month`
Day = 1
Year =`echo $ Date | col / 2`
;;
[az] *)#更好的是一个月或一周的一天
Exp = $ {Date:0:3}
case $ Exp in
jan | feb | mar | apr | may | june | jul | aug | sep | oct | nov | dec)
Month = $ Exp
Month =`Month2Int $ Month`
Day = 1
#Year保持不变
;;
mon | tue | wed | thu | Fri | sat | sun)
#计算下一个这样的一天
本地DayOfWeek =`date +%u`
D =`Day2Int $ Exp`
if [$ DayOfWeek -le $ D];那么
Date =`AddToDate $ Month / $ Day / $ Year $ [D-DayOfWeek]`
else
Date =`AddToDate $ Month / $ Day / $ Year $ [7 + D -DayOfWeek]`
fi

#重置月/日/年
月=`echo $ Date | col / 1`
Day =`echo $ Date | col / 2`
Year =`echo $ Date | col / 3`
;;
*)echo$ Exp不是有效的一个月或者一天
exit
;;
esac
;;
*)echo$ Date不是有效的日期
exit
;;
esac
case $ Day in
[0-9] *);; #天必须是数字
*)echo$ Date不是有效的日期
exit
;;
esac
[0-9] [0-9] [0-9] [0-9]);; #年必须是4位数
[0-9] [0-9])
年= 20 $年
;;
esac
Date = $ Month / $ Day / $ Year
echo $ Date
}
#NormalizeDate jan
#NormalizeDate january
# NormalizeDate jan 2009
#NormalizeDate jan 22 1983
#NormalizeDate 1/22
#NormalizeDate 1 22
#NormalizeDate sat
#NormalizeDate sun
#NormalizeDate mon

ComputeExtension(){

本地日期= $ 1; shift
local Month =`GetMonth $ Date`
local Day =`echo $ Date | col / 2`
local Year =`echo $ Date | col / 3`
本地ExtensionExp =$ *
case $
* w * d *中的ExtensionExp#如5周3天甚至5w2d
ExtensionExp =`echo $ ExtensionExp | sed's / [a-z] / / g'`
weeks =`echo $ ExtensionExp | col 1`
days =`echo $ ExtensionExp | col 2`
days = $ [7 * week + days]
Due =`AddToDate $ Month / $ Day / $ Year $ days`
;;
* d)#喜欢5天或5d
ExtensionExp =`echo $ ExtensionExp | sed's / [a-z] / / g'`
days = $ ExtensionExp
Due =`AddToDate $ Month / $ Day / $ Year $ days`
;;
*)
到期= $ ExtensionExp
;;
esac
echo $ Due

}


#Pop - 从数组中移除第一个元素并向左移动
Pop(){
Var = $ 1
evalunset $ Var [0]
eval$ Var =(\ $ {$ Var [*]})
}

ComputeDate(){
local Date =`NormalizeDate $ 1`; shift
local Expression =`echo $ * | sed的/ ^ * a / 1 /; s /,/ /'| tr AZ az`
local Exp =(`echo $ Expression`)
local Token = $ Exp#first one
local Ans =
#echo$ {Exp [*]}> / dev / tty
case $令牌
* / *)#正常日期
M =`GetMonth $ Token`
D =`GetDay $ Token`
Y =`GetYear $ Token`
if [-z$ Y];那么
Y = $ Year
elif [$ {#Y} -eq 2];那么
Y = 20 $ Y
fi
Ans =$ M / $ D / $ Y
;;
yes *)
Ans =`AddToDate $ Date -1`
;;
tod * | now)
Ans = $ Date
;;
tom *)
Ans =`AddToDate $ Date 1`
;;

case $表达式
* day * after *)#日期后的日期
Pop Exp; #跳过
Pop Exp; #跳过天
Pop Exp; #跳过
#echo调用ComputeDate $ Date $ {Exp [*]}> / dev / tty
Date =`ComputeDate $ Date $ {Exp [*]}`#Recursive call
#echo新的日期是$ Date> / dev / tty
Ans =`AddToDate $ Date 1`
;;
*最后*日* * *月* *结束* * *月)
M =`date +%m`
Day = $ {Months [M-1] }
if [$ M -eq 2 -a`IsLeapYear $ Year` = yes];那么
let Day ++
fi
Ans = $ Month / $ Day / $ Year
;;
* last * day * of *)
D = $ {Expression ## * of}
D =`NormalizeDate $ D`
M =`GetMonth $ D`
Y =`GetYear $ D`
#echo M是$ M> / dev / tty
Day = $ {Months [M-1]}
如果[$ M -eq 2 -a`IsLeapYear $ Y` = yes];然后
let Day ++
fi
Ans = $ [M] / $ Day / $ Y
;;
*)
echo未知表达式:$ expression
exit
;;
esac
;;
next *)#next DayOfWeek
Pop Exp
dow =`Day2Int $ DayOfWeek`#前3个字符
tdow =`Day2Int $ {Exp:0:3}## 3个字符
n = $ [7-dow + tdow]
Ans =`AddToDate $ Date $ n`
;;
this *)
Pop Exp
dow =`Day2Int $ DayOfWeek`
tdow =`Day2Int $ {Exp:0:3}`#前3个字符
if [$ dow -gt $ tdow];那么
echo'这个$ Exp'已经过去了,你的意思是'next $ Exp?'
exit
fi
n = $ [tdow-dow]
Ans =`AddToDate $ Date $ n`
;;
[az] *)#DayOfWeek ...

M = $ {Exp:0:3}
case $ M
jan | feb | mar | apr | may | june | jul | aug | sep | oct | nov | dec)
ND =`NormalizeDate $ {Exp [*]}`
Ans = $ ND
;;
mon | tue | wed | thu | Fri | sat | sun)
dow =`Day2Int $ DayOfWeek`
Ans =`NormalizeDate $ Exp`

if [ $ {#Exp [*]} -gt 1];然后#Just a DayOfWeek
#tdow =`GetDay $ Exp`#前3个字符
#if [$ dow -gt $ tdow];那么
#echo'这个$ Exp'已经过去了,你的意思是'下一个$ Exp'?
#exit
#fi
#n = $ [tdow-dow]
#else#DayOfWeek在未来的一周
Pop Exp#toss monday
Pop Exp#toss in / off
if [$ Exp = next];那么
Exp = 2
fi
n = $ [7 *(Exp-1)]#周数
n = $ [n + 7-dow + tdow]
Ans =`AddToDate $ Date $ n`
fi
;;
esac
;;
[0-9] *)#Number星期[from | after]日期
n = $ Exp
Pop Exp;
case $ Exp in
w *)let n = 7 * n ;;
esac

Pop Exp; Pop Exp
#echo调用ComputeDate $ Date $ {Exp [*]}> / dev / tty
Date =`ComputeDate $ Date $ {Exp [*]}`#Recursive call
#echo新的日期是$ Date> / dev / tty
Ans =`AddToDate $ Date $ n`
;;
esac
echo $ Ans
}

年份=`日期+%Y`
月份=日期+%m`
日=`date +%d`
DayOfWeek =`date +%a | tr AZ az`

Date =$ Month / $ Day / $ Year
ComputeDate $ Date $ *

此脚本广泛使用我写的另一个脚本(称为 col col 的用户,许多人表示歉意)。此版本的
col 简化了从stdin提取列。因此,

  $ echo a b c d e | col 5 3 2 

打印

  ecb 

这里是 col 脚本:

 #!/ bin / sh 
#col - 从文件中提取列
#用法:
#col [-r] [c] col-1 col-2 ...
#其中[c]如果提供定义字段分隔符
#其中每个col-i表示列根据-r的存在如下解释:
#-r present:从行的右端开始计数
#-r缺席:从行左边开始计数
Separator =
Reverse = false

-r中的$ 1)Reverse = true;转移;
;;
[0-9] *)
;;
*)Separator =$ 1;转移;
;;
esac

案例$ 1in
-r)Reverse = true;转移;
;;
[0-9] *)
;;
*)Separator =$ 1;转移;
;;
esac

#用$ i
替换每个col-i cols =
for $ in $ *
do
if [ $ Reverse = true];那么
Cols =$ Cols \ $(NF- $ f + 1)
else
Cols =$ Cols \ $$ f
fi

done

Cols =`echo$ Cols| sed's /,$ //'`
#echo使用$ Cols的列规格
awk -F$ Separator{print $ Cols}

当脚本调用不正确时,还会使用 printdirections 打印出路线:

 #!/ bin / sh 

#printdirections - 打印shell脚本的标题行

#用法:
#printdirections路径
#其中
#路径是有问题的shell脚本的一个* full *路径
#以'/'开头

#要使用printdirections,您必须包含(作为您的shell脚本的顶部
#上的注释)运行shell脚本的文档。

if [$#-eq 0 -o$ *=-h];然后
printdirections $ 0
exit
fi
#删除文件顶部的命令调用,如果有
#从打印方向发生到最后的位置删除的文件
#删除#comments
#这里有一个奇怪的奇怪。
sed'/#!/d;/.*printdirections/,$d;/ *#/!d; s /#//; s /#//'$ 1> /tmp/printdirections.$$

#计算行数
numlines =`wc -l /tmp/printdirections.$$ | awk'{print $ 1}'`

#删除最后一行
numlines =`expr $ numlines - 1`


head -n $ numlines /tmp/printdirections.$$
rm /tmp/printdirections.$$

请将这三个脚本分别放在文件 ComputeDate col printdirections 中。将文件放在由PATH命名的目录中,通常为〜/ bin。然后使它们执行:

  $ chmod a + x ComputeDate col printdirections 
/ pre>

问题?给我一些emaiL:morell AT cs.atu.edu在该主题中的 ComputeDate


I need to do date arithmetic in Unix shell scripts that I use to control the execution of third party programs.

I'm using a function to increment a day and another to decrement:

IncrementaDia(){
echo $1 | awk '
BEGIN {
        diasDelMes[1] = 31
        diasDelMes[2] = 28
        diasDelMes[3] = 31
        diasDelMes[4] = 30
        diasDelMes[5] = 31
        diasDelMes[6] = 30
        diasDelMes[7] = 31
        diasDelMes[8] = 31
        diasDelMes[9] = 30
        diasDelMes[10] = 31
        diasDelMes[11] = 30
        diasDelMes[12] = 31
}
{
        anio=substr($1,1,4)
        mes=substr($1,5,2)
        dia=substr($1,7,2)

        if((anio % 4 == 0 && anio % 100 != 0) || anio % 400 == 0)
        {
                diasDelMes[2] = 29;
        }

        if( dia == diasDelMes[int(mes)] ) {
                if( int(mes) == 12 ) {
                        anio = anio + 1
                        mes = 1
                        dia = 1
                } else {
                        mes = mes + 1
                        dia = 1
                }
        } else {
                dia = dia + 1
        }
}
END {
        printf("%04d%02d%02d", anio, mes, dia)
}
'
}

if [ $# -eq 1 ]; then
        tomorrow=$1
else
        today=$(date +"%Y%m%d")
        tomorrow=$(IncrementaDia $hoy)
fi

but now I need to do more complex arithmetic.

What it's the best and more compatible way to do this?

解决方案

I have written a bash script for converting dates expressed in English into conventional mm/dd/yyyy dates. It is called ComputeDate.

Here are some examples of its use. For brevity I have placed the output of each invocation on the same line as the invocation, separarted by a colon (:). The quotes shown below are not necessary when running ComputeDate:

$ ComputeDate 'yesterday': 03/19/2010
$ ComputeDate 'yes': 03/19/2010
$ ComputeDate 'today': 03/20/2010
$ ComputeDate 'tod': 03/20/2010
$ ComputeDate 'now': 03/20/2010
$ ComputeDate 'tomorrow': 03/21/2010
$ ComputeDate 'tom': 03/21/2010
$ ComputeDate '10/29/32': 10/29/2032
$ ComputeDate 'October 29': 10/1/2029
$ ComputeDate 'October 29, 2010': 10/29/2010
$ ComputeDate 'this monday': 'this monday' has passed.  Did you mean 'next monday?'
$ ComputeDate 'a week after today': 03/27/2010
$ ComputeDate 'this satu': 03/20/2010
$ ComputeDate 'next monday': 03/22/2010
$ ComputeDate 'next thur': 03/25/2010
$ ComputeDate 'mon in 2 weeks': 03/28/2010
$ ComputeDate 'the last day of the month': 03/31/2010
$ ComputeDate 'the last day of feb': 2/28/2010
$ ComputeDate 'the last day of feb 2000': 2/29/2000
$ ComputeDate '1 week from yesterday': 03/26/2010
$ ComputeDate '1 week from today': 03/27/2010
$ ComputeDate '1 week from tomorrow': 03/28/2010
$ ComputeDate '2 weeks from yesterday': 4/2/2010
$ ComputeDate '2 weeks from today': 4/3/2010
$ ComputeDate '2 weeks from tomorrow': 4/4/2010
$ ComputeDate '1 week after the last day of march': 4/7/2010
$ ComputeDate '1 week after next Thursday': 4/1/2010
$ ComputeDate '2 weeks after the last day of march': 4/14/2010
$ ComputeDate '2 weeks after 1 day after the last day of march': 4/15/2010
$ ComputeDate '1 day after the last day of march': 4/1/2010
$ ComputeDate '1 day after 1 day after 1 day after 1 day after today': 03/24/2010

I have included this script as an answer to this problem because it illustrates how to do date arithmetic via a set of bash functions and these functions may prove useful for others. It handles leap years and leap centuries correctly:

#! /bin/bash
#  ConvertDate -- convert a human-readable date to a MM/DD/YY date
#
#  Date ::= Month/Day/Year
#        |  Month/Day
#        |  DayOfWeek
#        |  [this|next] DayOfWeek
#        |  DayofWeek [of|in] [Number|next] weeks[s]
#        |  Number [day|week][s] from Date
#        |  the last day of the month
#        |  the last day of Month
#
#  Month ::= January | February | March | April | May | ...  | December
#  January  ::= jan | january | 1
#  February  ::= feb | january | 2
#  ...
#  December ::=  dec | december | 12
#  Day   ::= 1 | 2 | ... | 31
#  DayOfWeek ::= today | Sunday | Monday | Tuesday | ...  | Saturday
#  Sunday    ::= sun*
#  ...
#  Saturday  ::= sat*
#
#  Number ::= Day | a
#
#  Author: Larry Morell

if [ $# = 0 ]; then
   printdirections $0
   exit
fi



# Request the value of a variable
GetVar () {
   Var=$1
   echo -n "$Var= [${!Var}]: "
   local X
   read X
   if [ ! -z $X ]; then
      eval $Var="$X"
   fi
}

IsLeapYear () {
   local Year=$1
   if [ $[20$Year % 4]  -eq  0 ]; then
      echo yes
   else
      echo no
   fi
}

# AddToDate -- compute another date within the same year

DayNames=(mon tue wed thu fri sat sun )  # To correspond with 'date' output

Day2Int () {
   ErrorFlag=
   case $1 in
      -e )
         ErrorFlag=-e; shift
         ;;
   esac
   local dow=$1
   n=0
   while  [ $n -lt 7 -a $dow != "${DayNames[n]}" ]; do
      let n++
   done
   if [ -z "$ErrorFlag" -a $n -eq 7 ]; then
      echo Cannot convert $dow to a numeric day of wee
      exit
   fi
   echo $[n+1]

}

Months=(31 28 31 30 31 30 31 31 30 31 30 31)
MonthNames=(jan feb mar apr may jun jul aug sep oct nov dec)
# Returns the month (1-12) from a date, or a month name
Month2Int () {
   ErrorFlag=
   case $1 in
      -e )
         ErrorFlag=-e; shift
         ;;
   esac
   M=$1
   Month=${M%%/*}  # Remove /...
   case $Month in
      [a-z]* )
         Month=${Month:0:3}
         M=0
         while [ $M -lt 12 -a ${MonthNames[M]} != $Month ]; do
            let M++
         done
         let M++
   esac
   if [  -z "$ErrorFlag" -a $M -gt 12 ]; then
      echo "'$Month' Is not a valid month."
      exit
   fi
   echo $M
}

# Retrieve month,day,year from a legal date
GetMonth() {
   echo ${1%%/*}
}

GetDay() {
   echo $1 | col / 2
}

GetYear() {
   echo ${1##*/}
}


AddToDate() {

   local Date=$1
   local days=$2
   local Month=`GetMonth $Date`
   local Day=`echo $Date | col / 2`   # Day of Date
   local Year=`echo $Date | col / 3`  # Year of Date
   local LeapYear=`IsLeapYear $Year`

   if [ $LeapYear = "yes" ]; then
      let Months[1]++
   fi
   Day=$[Day+days]
   while [ $Day -gt ${Months[$Month-1]} ]; do
       Day=$[Day -  ${Months[$Month-1]}]
       let Month++
   done
   echo "$Month/$Day/$Year"
}

# Convert a date to normal form
NormalizeDate () {
   Date=`echo "$*" | sed 'sX  *X/Xg'`
   local Day=`date +%d`
   local Month=`date +%m`
   local Year=`date +%Y`
   #echo Normalizing Date=$Date > /dev/tty
   case $Date in
      */*/* )
         Month=`echo $Date | col / 1 `
         Month=`Month2Int $Month`
         Day=`echo $Date | col / 2`
         Year=`echo $Date | col / 3`
         ;;
      */* )
         Month=`echo $Date | col / 1 `
         Month=`Month2Int $Month`
         Day=1
         Year=`echo $Date | col / 2 `
         ;;
      [a-z]* ) # Better be a month or day of week
         Exp=${Date:0:3}
         case $Exp in
            jan|feb|mar|apr|may|june|jul|aug|sep|oct|nov|dec )
               Month=$Exp
               Month=`Month2Int $Month`
               Day=1
               #Year stays the same
               ;;
            mon|tue|wed|thu|fri|sat|sun )
               # Compute the next such day
               local DayOfWeek=`date +%u`
               D=`Day2Int $Exp`
               if [ $DayOfWeek -le $D ]; then
                  Date=`AddToDate $Month/$Day/$Year $[D-DayOfWeek]`
               else
                  Date=`AddToDate $Month/$Day/$Year $[7+D-DayOfWeek]`
               fi

               # Reset Month/Day/Year
               Month=`echo $Date | col / 1 `
               Day=`echo $Date | col / 2`
               Year=`echo $Date | col / 3`
               ;;
            * ) echo "$Exp is not a valid month or day"
                exit
               ;;
            esac
         ;;
      * ) echo "$Date is not a valid date"
          exit
         ;;
   esac
   case $Day in
      [0-9]* );;  # Day must be numeric
      * ) echo "$Date is not a valid date"
          exit
         ;;
   esac
      [0-9][0-9][0-9][0-9] );;  # Year must be 4 digits
      [0-9][0-9] )
          Year=20$Year
      ;;
   esac
   Date=$Month/$Day/$Year
   echo $Date
}
# NormalizeDate jan
# NormalizeDate january
# NormalizeDate jan 2009
# NormalizeDate jan 22 1983
# NormalizeDate 1/22
# NormalizeDate 1 22
# NormalizeDate sat
# NormalizeDate sun
# NormalizeDate mon

ComputeExtension () {

   local Date=$1; shift
   local Month=`GetMonth $Date`
   local Day=`echo $Date | col / 2`
   local Year=`echo $Date | col / 3`
   local ExtensionExp="$*"
   case $ExtensionExp in
      *w*d* )  # like 5 weeks 3 days or even 5w2d
            ExtensionExp=`echo $ExtensionExp | sed 's/[a-z]/ /g'`
            weeks=`echo $ExtensionExp | col  1`
            days=`echo $ExtensionExp | col 2`
            days=$[7*weeks+days]
            Due=`AddToDate $Month/$Day/$Year $days`
      ;;
      *d )    # Like 5 days or 5d
            ExtensionExp=`echo $ExtensionExp | sed 's/[a-z]/ /g'`
            days=$ExtensionExp
            Due=`AddToDate $Month/$Day/$Year $days`
      ;;
      * )
            Due=$ExtensionExp
      ;;
   esac
   echo $Due

}


# Pop -- remove the first element from an array and shift left
Pop () {
   Var=$1
   eval "unset $Var[0]"
   eval "$Var=(\${$Var[*]})"
}

ComputeDate () {
   local Date=`NormalizeDate $1`; shift
   local Expression=`echo $* | sed 's/^ *a /1 /;s/,/ /' | tr A-Z a-z `
   local Exp=(`echo $Expression `)
   local Token=$Exp  # first one
   local Ans=
   #echo "Computing date for ${Exp[*]}" > /dev/tty
   case $Token in
      */* ) # Regular date
         M=`GetMonth $Token`
         D=`GetDay $Token`
         Y=`GetYear $Token`
         if [ -z "$Y" ]; then
            Y=$Year
         elif [ ${#Y} -eq 2 ]; then
            Y=20$Y
         fi
         Ans="$M/$D/$Y"
         ;;
      yes* )
         Ans=`AddToDate $Date -1`
         ;;
      tod*|now )
         Ans=$Date
         ;;
      tom* )
         Ans=`AddToDate $Date 1`
         ;;
      the )
         case $Expression in
            *day*after* )  #the day after Date
               Pop Exp;   # Skip the
               Pop Exp;   # Skip day
               Pop Exp;   # Skip after
               #echo Calling ComputeDate $Date ${Exp[*]} > /dev/tty
               Date=`ComputeDate $Date ${Exp[*]}` #Recursive call
               #echo "New date is " $Date > /dev/tty
               Ans=`AddToDate $Date 1`
               ;;
            *last*day*of*th*month|*end*of*th*month )
               M=`date +%m`
               Day=${Months[M-1]}
               if [ $M -eq 2 -a `IsLeapYear $Year` = yes ]; then
                  let Day++
               fi
               Ans=$Month/$Day/$Year
               ;;
            *last*day*of* )
               D=${Expression##*of }
               D=`NormalizeDate $D`
               M=`GetMonth $D`
               Y=`GetYear $D`
               # echo M is $M > /dev/tty
               Day=${Months[M-1]}
               if [ $M -eq 2 -a `IsLeapYear $Y` = yes ]; then
                  let Day++
               fi
               Ans=$[M]/$Day/$Y
               ;;
            * )
               echo "Unknown expression: " $Expression
               exit
               ;;
         esac
         ;;
      next* ) # next DayOfWeek
         Pop Exp
         dow=`Day2Int $DayOfWeek` # First 3 chars
         tdow=`Day2Int ${Exp:0:3}` # First 3 chars
         n=$[7-dow+tdow]
         Ans=`AddToDate $Date $n`
         ;;
      this* )
         Pop Exp
         dow=`Day2Int $DayOfWeek`
         tdow=`Day2Int ${Exp:0:3}` # First 3 chars
         if [ $dow -gt $tdow ]; then
            echo "'this $Exp' has passed.  Did you mean 'next $Exp?'"
            exit
         fi
         n=$[tdow-dow]
         Ans=`AddToDate $Date $n`
         ;;
      [a-z]* ) # DayOfWeek ...

         M=${Exp:0:3}
         case $M in
            jan|feb|mar|apr|may|june|jul|aug|sep|oct|nov|dec )
               ND=`NormalizeDate ${Exp[*]}`
               Ans=$ND
               ;;
            mon|tue|wed|thu|fri|sat|sun )
               dow=`Day2Int $DayOfWeek`
               Ans=`NormalizeDate $Exp`

               if [ ${#Exp[*]} -gt 1 ]; then # Just a DayOfWeek
                  #tdow=`GetDay $Exp` # First 3 chars
                  #if [ $dow -gt $tdow ]; then
                     #echo "'this $Exp' has passed.  Did you mean 'next $Exp'?"
                     #exit
                  #fi
                  #n=$[tdow-dow]
               #else  # DayOfWeek in a future week
                  Pop Exp  # toss monday
                  Pop Exp  # toss in/off
                  if [ $Exp = next ]; then
                     Exp=2
                  fi
                  n=$[7*(Exp-1)]   # number of weeks
                  n=$[n+7-dow+tdow]
                  Ans=`AddToDate $Date $n`
               fi
               ;;
         esac
         ;;
      [0-9]* ) # Number  weeks [from|after] Date
         n=$Exp
         Pop Exp;
         case $Exp in
            w* ) let n=7*n;;
         esac

         Pop Exp; Pop Exp
         #echo Calling ComputeDate $Date ${Exp[*]} > /dev/tty
         Date=`ComputeDate $Date ${Exp[*]}` #Recursive call
         #echo "New date is " $Date > /dev/tty
         Ans=`AddToDate $Date $n`
         ;;
   esac
   echo $Ans
}

Year=`date +%Y`
Month=`date +%m`
Day=`date +%d`
DayOfWeek=`date +%a |tr A-Z a-z`

Date="$Month/$Day/$Year"
ComputeDate $Date $*

This script makes extensive use of another script I wrote (called col ... many apologies to those who use the standard col supplied with Linux). This version of col simplifies extracting columns from the stdin. Thus,

$ echo a b c d e | col 5 3 2

prints

e c b

Here it the col script:

#!/bin/sh
# col -- extract columns from a file
# Usage:
#    col [-r] [c] col-1 col-2 ...
#   where [c] if supplied defines the field separator
#   where each col-i represents a column interpreted according to  the presence of -r as follows:
#        -r present : counting starts from the right end of the line
#        -r absent  : counting starts from the left side of the line
Separator=" "
Reverse=false
case "$1" in
 -r )  Reverse=true; shift;
 ;;
 [0-9]* )
 ;;
 * )Separator="$1"; shift;
 ;;
esac

case "$1" in
 -r )  Reverse=true; shift;
 ;;
 [0-9]* )
 ;;
 * )Separator="$1"; shift;
 ;;
esac

#  Replace each col-i with $i
Cols=""
for  f in $*
do
  if [ $Reverse = true ]; then
     Cols="$Cols \$(NF-$f+1),"
  else
     Cols="$Cols \$$f,"
  fi

done

Cols=`echo "$Cols" | sed 's/,$//'`
#echo "Using column specifications of $Cols"
awk -F "$Separator"  "{print $Cols}"

It also uses printdirections for printing out directions when the script is invoked improperly:

#!/bin/sh
#
#  printdirections -- print header lines of a shell script
#
#  Usage:
#      printdirections path
#  where
#      path is a *full* path to the shell script in question
#      beginning with '/'
#
#  To use printdirections, you must include (as comments at the top
#  of your shell script) documentation for running the shell script.

if [ $# -eq 0 -o "$*" = "-h" ]; then
   printdirections $0
   exit
fi
#  Delete the command invocation at the top of the file, if any
#  Delete from the place where printdirections occurs to the end of the file
#  Remove the # comments
#  There is a bizarre oddity here.
   sed '/#!/d;/.*printdirections/,$d;/ *#/!d;s/# //;s/#//' $1 > /tmp/printdirections.$$

#  Count the number of lines
numlines=`wc -l /tmp/printdirections.$$ | awk '{print $1}'`

#  Remove the last   line
numlines=`expr $numlines - 1`


head -n $numlines /tmp/printdirections.$$
rm /tmp/printdirections.$$

To use this place the three scripts in the files ComputeDate, col, and printdirections, respectively. Place the file in directory named by your PATH, typically, ~/bin. Then make them executable with:

$ chmod a+x ComputeDate col printdirections

Problems? Send me some emaiL: morell AT cs.atu.edu Place ComputeDate in the subject.

这篇关于Unix shell脚本中的日期算术的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆