【问题标题】:Date arithmetic in Unix shell scriptsUnix shell 脚本中的日期运算
【发布时间】:2010-09-05 14:36:30
【问题描述】:

我需要在用于控制第三方程序执行的 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 && 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

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

最好且更兼容的方法是什么?

【问题讨论】:

    标签: unix shell date math scripting


    【解决方案1】:

    假设你有GNU date,像这样:

    date --date='1 days ago' '+%a'
    

    还有similar phrases

    【讨论】:

    • 似乎可以在 Linux 上运行。如果它是 GNU 扩展,我不会感到惊讶。它甚至可以用于从任意日期抵消任意金额,例如“date --date='2012-01-27 - 30 days'”
    • 绝对不适用于 BSD 的 date,它是随 OS X 一起安装的。使用 homebrew 之类的东西安装 coreutils 使得添加这些扩展的 GNU 版本可以作为 gdate 使用。
    • 很高兴发现有用的功能可以节省一天的时间,在其他平凡的命令中。永远的 Unix 命令行。
    【解决方案2】:
    date --date='1 days ago' '+%a'
    

    这不是一个非常兼容的解决方案。它仅适用于 Linux。至少,它在 Aix 和 Solaris 中不起作用。

    它适用于 RHEL:

    date --date='1 days ago' '+%Y%m%d'
    20080807
    

    【讨论】:

      【解决方案3】:

      为什么不用像 perl 或 python 这样更自然地支持复杂日期处理的语言来编写脚本呢?当然,您可以在 bash 中完成所有这些操作,但我认为您还可以在使用 python 的平台之间获得更多的一致性,只要您可以确保安装了 perl 或 python。

      我应该补充一点,将 python 和 perl 脚本连接到包含 shell 脚本中是很容易的。

      【讨论】:

      • 我同意贾斯汀的观点,并建议为您的脚本使用更好的工具/语言。我可能会选择 perl,因为它安装在大多数 Unix 系统上,开箱即用。 Python 虽然更现代,但需要您先安装它,这可能会很痛苦,甚至在政治上是不可能的。
      【解决方案4】:

      进一步研究,我认为您可以简单地使用日期。 我在 OpenBSD 上尝试了以下操作:我选择了 2008 年 2 月 29 日的日期和一个随机时间(以 080229301535 的形式),并在日期部分添加了 +1,如下所示:

      $ date -j 0802301535
      Sat Mar  1 15:35:00 EST 2008
      

      如您所见,日期正确地格式化了时间...

      HTH

      【讨论】:

        【解决方案5】:

        如果 GNU 版本的 date 适合您,为什么不获取源代码并在 AIX 和 Solaris 上编译它?

        http://www.gnu.org/software/coreutils/

        无论如何,如果您要编写自己的代码,源代码应该可以帮助您正确计算日期。

        顺便说一句,cmets 喜欢“那个解决方案很好,但你肯定会注意到它没有那么好。似乎没有人想到在构建 Unix 时修改日期。”不要真的让我们到任何地方。到目前为止,我发现每一个建议都非常有用且目标明确。

        【讨论】:

          【解决方案6】:

          要在 UNIX 上对日期进行算术运算,您可以将日期作为自 UNIX 纪元以来的秒数,进行一些计算,然后转换回可打印的日期格式。 date 命令应该能够为您提供自纪元以来的秒数,并将该数字转换回可打印的日期。我的本地日期命令就是这样做的,

          % date -n
          1219371462
          % date 1219371462
          Thu Aug 21 22:17:42 EDT 2008
          % 
          

          请参阅您当地的 date(1) 手册页。 要增加一天,请添加 86400 秒。

          【讨论】:

            【解决方案7】:

            我已经碰到过几次了。我的想法是:

            1. 日期算术总是很痛苦
            2. 使用 EPOCH 日期格式会更容易一些
            3. Linux 上的日期转换为 EPOCH,但在 Solaris 上没有
            4. 对于便携式解决方案,您需要执行以下操作之一:
              1. 在 solaris 上安装 gnu date(已经 提到,需要人机交互 完成)
              2. 对日期部分使用 perl(大多数 unix 安装包括 perl,所以我通常会假设 此操作不会 需要额外的工作)。

            一个示例脚本(检查某些用户文件的年龄以查看是否可以删除该帐户):

            #!/usr/local/bin/perl
            
            $today = time();
            
            $user = $ARGV[0];
            
            $command="awk -F: '/$user/ {print \$6}' /etc/passwd";
            
            chomp ($user_dir = `$command`);
            
            if ( -f "$user_dir/.sh_history" ) {
                @file_dates   = stat("$user_dir/.sh_history");
                $sh_file_date = $file_dates[8];
            } else {
                $sh_file_date = 0;
            }
            if ( -f "$user_dir/.bash_history" ) {
                @file_dates     = stat("$user_dir/.bash_history");
                $bash_file_date = $file_dates[8];
            } else {
                $bash_file_date = 0;
            }
            if ( $sh_file_date > $bash_file_date ) {
                $file_date = $sh_file_date;
            } else {
                $file_date = $bash_file_date;
            }
            $difference = $today - $file_date;
            
            if ( $difference >= 3888000 ) {
                print "User needs to be disabled, 45 days old or older!\n";
                exit (1);
            } else {
                print "OK\n";
                exit (0);
            }
            

            【讨论】:

              【解决方案8】:

              如果你想继续使用 awk,那么 mktime 和 strftime 函数很有用:

              
              BEGIN { dateinit }
                    { newdate=daysadd(OldDate,DaysToAdd)}
              
               # daynum: convert DD-MON-YYYY to day count
               #-----------------------------------------
              function daynum(date,  d,m,y,i,n)
              {
                   y=substr(date,8,4)
                   m=gmonths[toupper(substr(date,4,3))]
                   d=substr(date,1,2)
                   return mktime(y" "m" "d" 12 00 00")
              }
              
               #numday: convert day count to DD-MON-YYYY
               #-------------------------------------------
              function numday(n,  y,m,d)
              {
                  m=toupper(substr(strftime("%B",n),1,3))
                  return strftime("%d-"m"-%Y",n)
              }
              
               # daysadd: add (or subtract) days from date (DD-MON-YYYY), return new date (DD-MON-YYYY)
               #------------------------------------------
              function daysadd(date, days)
              {
                  return numday(daynum(date)+(days*86400))
              }
              
               #init variables for date calcs
               #-----------------------------------------
              function dateinit(   x,y,z)
              {
                   # Stuff for date calcs
                   split("JAN:1,FEB:2,MAR:3,APR:4,MAY:5,JUN:6,JUL:7,AUG:8,SEP:9,OCT:10,NOV:11,DEC:12", z)
                   for (x in z)
                   {
                      split(z[x],y,":")
                      gmonths[y[1]]=y[2]
                   }
              }
              

              【讨论】:

              • 但是,正如我刚刚发现的那样,mktime 是 gawk,并且在 solaris awk/nawk 上不存在 :(
              【解决方案9】:

              Chris F.A. Johnson 所著的“Shell Script Recipes: A Problem Solution Approach”一书(ISBN:978-1-59059-471-1)有一个可能有用的日期函数库。源代码在http://apress.com/book/downloadfile/2146 提供(日期函数在tar 文件的Chapter08/data-funcs-sh 中)。

              【讨论】:

                【解决方案10】:

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

                以下是一些使用示例。为简洁起见,我放置了每个调用的输出 与调用在同一行,用冒号 (:) 分隔。在运行 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
                

                我已经包含这个脚本作为这个问题的答案,因为它说明了如何 通过一组 bash 函数进行日期运算,这些函数可能很有用 为他人。它正确处理闰年和闰世纪:

                #! /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 $*
                

                这个脚本大量使用了我编写的另一个脚本(称为 col ...对于那些使用 Linux 提供的标准 col 的人,我深表歉意)。这个版本的 col 简化了从标准输入中提取列的过程。因此,

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

                打印

                e c b
                

                这里是 col 脚本:

                #!/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}"
                

                当脚本调用不正确时,它还使用 printdirections 打印出方向:

                #!/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.$$
                

                要使用此功能,请将三个脚本分别放在 ComputeDatecolprintdirections 文件中。将文件放在 PATH 命名的目录中,通常是 ~/bin。然后使它们可执行:

                $ chmod a+x ComputeDate col printdirections
                

                问题?给我发一些电子邮件:morell AT cs.atu.edu 将 ComputeDate 放在主题中。

                【讨论】:

                  【解决方案11】:

                  这是在 shell 脚本中进行日期计算的简单方法。

                  meetingDate='12/31/2011' # MM/DD/YYYY Format
                  reminderDate=`date --date=$meetingDate'-1 day' +'%m/%d/%Y'`
                  echo $reminderDate
                  

                  以下是可以使用date 实用程序实现的日期计算的更多变体。 http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/

                  这在 RHEL 上对我有用。

                  【讨论】:

                  • +1 这是我做算术所需要的。非常简单直接。我更正了月/日/年订单。
                  【解决方案12】:

                  这是我的两分钱 - 一个使用 dategrep 的脚本包装器。

                  示例用法

                  > sh ./datecalc.sh "2012-08-04 19:43:00" + 1s
                  2012-08-04 19:43:00 + 0d0h0m1s
                  2012-08-04 19:43:01
                  
                  > sh ./datecalc.sh "2012-08-04 19:43:00" - 1s1m1h1d
                  2012-08-04 19:43:00 - 1d1h1m1s
                  2012-08-03 18:41:59
                  
                  > sh ./datecalc.sh "2012-08-04 19:43:00" - 1d2d1h2h1m2m1s2sblahblah
                  2012-08-04 19:43:00 - 1d1h1m1s
                  2012-08-03 18:41:59
                  
                  > sh ./datecalc.sh "2012-08-04 19:43:00" x 1d
                  Bad operator :-(
                  
                  > sh ./datecalc.sh "2012-08-04 19:43:00"
                  Missing arguments :-(
                  
                  > sh ./datecalc.sh gibberish + 1h
                  date: invalid date `gibberish'
                  Invalid date :-(
                  

                  脚本

                  #!/bin/sh
                  
                  # Usage:
                  #
                  # datecalc "<date>" <operator> <period>
                  #
                  # <date> ::= see "man date", section "DATE STRING"
                  # <operator> ::= + | -
                  # <period> ::= INTEGER<unit> | INTEGER<unit><period>
                  # <unit> ::= s | m | h | d
                  
                  if [ $# -lt 3 ]; then
                  echo "Missing arguments :-("
                  exit; fi
                  
                  date=`eval "date -d \"$1\" +%s"`
                  if [ -z $date ]; then
                  echo "Invalid date :-("
                  exit; fi
                  
                  if ! ([ $2 == "-" ] || [ $2 == "+" ]); then
                  echo "Bad operator :-("
                  exit; fi
                  op=$2
                  
                  minute=$[60]
                  hour=$[$minute*$minute]
                  day=$[24*$hour]
                  
                  s=`echo $3 | grep -oe '[0-9]*s' | grep -m 1 -oe '[0-9]*'`
                  m=`echo $3 | grep -oe '[0-9]*m' | grep -m 1 -oe '[0-9]*'`
                  h=`echo $3 | grep -oe '[0-9]*h' | grep -m 1 -oe '[0-9]*'`
                  d=`echo $3 | grep -oe '[0-9]*d' | grep -m 1 -oe '[0-9]*'`
                  if [ -z $s ]; then s=0; fi
                  if [ -z $m ]; then m=0; fi
                  if [ -z $h ]; then h=0; fi
                  if [ -z $d ]; then d=0; fi
                  
                  ms=$[$m*$minute]
                  hs=$[$h*$hour]
                  ds=$[$d*$day]
                  
                  sum=$[$s+$ms+$hs+$ds]
                  out=$[$date$op$sum]
                  formattedout=`eval "date -d @$out +\"%Y-%m-%d %H:%M:%S\""`
                  
                  echo $1 $2 $d"d"$h"h"$m"m"$s"s"
                  echo $formattedout
                  

                  【讨论】:

                    【解决方案13】:

                    为了兼容 BSD / OS X,您还可以使用带有 -j-v 的日期实用程序来进行日期数学运算。请参阅FreeBSD manpage for date。您可以将以前的 Linux 答案与此答案结合起来,这可能会为您提供足够的兼容性。

                    在 BSD 上,如 Linux,运行 date 将为您提供当前日期:

                    $ date
                    Wed 12 Nov 2014 13:36:00 AEDT
                    

                    现在有了 BSD 的日期,您可以使用 -v 进行数学运算,例如列出明天的日期(+1d加一天):

                    $ date -v +1d
                    Thu 13 Nov 2014 13:36:34 AEDT
                    

                    您可以使用现有日期作为基准,并且可以选择使用 strftime 指定解析格式,并确保使用 -j,这样您就不会更改系统日期:

                    $ date -j -f "%a %b %d %H:%M:%S %Y %z" "Sat Aug 09 13:37:14 2014 +1100"
                    Sat  9 Aug 2014 12:37:14 AEST
                    

                    您可以将其用作日期计算的基础:

                    $ date -v +1d -f "%a %b %d %H:%M:%S %Y %z" "Sat Aug 09 13:37:14 2014 +1100"
                    Sun 10 Aug 2014 12:37:14 AEST
                    

                    注意-v 暗示-j

                    可以依次提供多个调整:

                    $ date -v +1m -v -1w
                    Fri  5 Dec 2014 13:40:07 AEDT
                    

                    有关详细信息,请参阅手册页。

                    【讨论】:

                      【解决方案14】:

                      这对我有用:

                      TZ=GMT+6;
                      export TZ
                      mes=`date --date='2 days ago' '+%m'`
                      dia=`date --date='2 days ago' '+%d'`
                      anio=`date --date='2 days ago' '+%Y'`
                      hora=`date --date='2 days ago' '+%H'`
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2012-12-22
                        • 2011-07-21
                        • 2023-03-14
                        • 2023-04-09
                        相关资源
                        最近更新 更多