2008-08-08 13 views
17

Ich muss Datenarithmetik in Unix-Shell-Skripten, die ich verwende, um die Ausführung von Programmen von Drittanbietern zu steuern.Datum arithmetisch in Unix-Shell-Skripten

Ich verwende eine Funktion einen Tag und eine andere zu Abnahme zu erhöhen:

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 

aber jetzt brauche ich mehr komplexe Arithmetik zu tun.

Was ist der beste und kompatiblere Weg, dies zu tun?

Antwort

15

Ich habe ein Bash-Skript geschrieben, um in Englisch ausgedrückte Daten in konventionelle mm/dd/yyyy Daten zu konvertieren. Es heißt ComputeDate.

Hier sind einige Beispiele für seine Verwendung. Der Kürze halber habe ich die Ausgabe jedes Aufrufs in der gleichen Zeile wie der Aufruf platziert, getrennt durch einen Doppelpunkt (:). Die Zitate unten sind nicht notwendig beim Laufen 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 

ich dieses Skript für dieses Problem als Antwort enthalten sind, weil es zeigt, wie Datumsberechnungen über einen Satz von bash-Funktionen zu tun, und diese Funktionen können sich als nützlich erweisen für andere. Es behandelt Schaltjahr und Sprung Jahrhunderte richtig:

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

Dieses Skript umfangreiche Verwendung von einem anderen Skript macht I (genannt col ... viele Entschuldigungen für diejenigen, die den Standard col mit Linux geliefert wird) schrieb.Diese Version col vereinfacht das Extrahieren von Spalten aus der Standardeingabe. So

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

druckt

e c b 

Hier ist es das col Skript:

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

Es nutzt auch printdirections für Richtungen ausdrucken, wenn das Skript nicht ordnungsgemäß aufgerufen wird:

an diesem Ort die drei Skripte in den Dateien zu verwenden ComputeDate, col und printdirections ist. Legen Sie die Datei in das Verzeichnis, das in Ihrem PATH angegeben ist, normalerweise ~/bin. Dann mache sie ausführbar mit:

$ chmod a+x ComputeDate col printdirections 

Probleme? Senden Sie mir eine E-Mail: morell AT cs.atu.edu Platz ComputeDate im Betreff.

+0

Siehe meine Antwort für die einfachste Lösung. –

64

Angenommen, Sie GNU date haben, etwa so:

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

Und similar phrases.

+2

eine doppelte bis geben wollte !! :-) hat so viel Zeit damit verschwendet! :-(Danke an dich! :-) –

+1

Auf welchen Unixes (Unices?) funktioniert das? – Johnsyweb

+2

Scheint unter Linux zu arbeiten. Ich wäre nicht überrascht, wenn es eine GNU-Erweiterung wäre. Es funktioniert sogar für die Verrechnung beliebiger Beträge aus beliebigen Daten, zB "date --date = '2012-01-27 - 30 days'" –

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

Es ist keine sehr kompatible Lösung. Es funktioniert nur unter Linux. Zumindest hat es in Aix und Solaris nicht funktioniert.

Es funktioniert in RHEL:

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

Warum Ihre Skripte nicht schreiben, anstatt mit einer Sprache wie Perl oder Python, die mehr unterstützt natürlich komplexe Datum Verarbeitung? Sicher, Sie können tun alles in Bash, aber ich denke, Sie werden auch mehr Konsistenz über Plattformen mit Python zum Beispiel, solange Sie sicherstellen können, dass Perl oder Python installiert ist.

Ich sollte hinzufügen, dass es sehr einfach ist, in Python und Perl-Skripten in ein enthaltenes Shell-Skript zu verbinden.

+0

Ich stimme Justin zu und empfehle, ein besseres Tool/Sprache für Ihr Skript zu verwenden. Ich würde wahrscheinlich mit Perl gehen, da es auf den meisten Unix-Systemen standardmäßig installiert ist. Python, obwohl moderner, erfordert, dass Sie es zuerst installieren, was ein Schmerz oder sogar politisch unmöglich sein kann. –

3

Wenn ich weiter darüber nachdenke, kann ich einfach das Datum verwenden. Ich habe folgendes auf OpenBSD versucht: Ich habe das Datum des 29. Februar 2008 und eine zufällige Stunde (in Form von 080229301535) aufgelöst und +1 an den Tag Teil, etwa so:

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

Wie Sie können, das Datum sehen die Zeit richtig ...

HTH für Sie

0

Wenn die GNU-Version von Datum Werke formatiert ist, warum Sie nicht die Quelle greifen und sie auf AIX und Solaris kompilieren?

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

In jedem Fall sollte die Quelle, die Sie das Datum Arithmetik richtig helfen, wenn Sie Sie Code besitzen, schreiben werden.

Nebenbei bemerkt, Kommentare wie "diese Lösung ist gut, aber sicher können Sie feststellen, es ist nicht so gut wie es sein kann. Es scheint niemand dachte an Basteln mit Daten bei der Konstruktion von Unix." Bring uns nirgendwo hin. Ich fand jeden der Vorschläge bisher sehr nützlich und zielgerichtet.

6

Um Arithmetik mit Datumsangaben unter UNIX zu berechnen, erhalten Sie das Datum als die Anzahl Sekunden seit der UNIX-Epoche, führen Sie eine Berechnung durch und konvertieren Sie dann wieder in Ihr druckbares Datumsformat. Der Datumsbefehl sollte Ihnen die Sekunden seit der Epoche geben und von dieser Zahl wieder in ein druckbares Datum konvertieren können.Mein lokaler Befehl date tut dies,

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

Ihre lokalen date(1) -Handbuchseite. Um einen Tag zu erhöhen, fügen Sie 86400 Sekunden hinzu.

4

Ich habe ein paar Mal getroffen. Meine Gedanken sind:

  1. Datum Arithmetik ist immer ein Schmerz
  2. es etwas einfacher ist, wenn EPOCH Datumsformat
  3. Datum auf Linux konvertiert zu EPOCH, aber nicht auf Solaris
  4. Für eine tragbare Lösung , müssen Sie eine der folgenden Funktionen ausführen:
    1. Install Gnu Datum auf solaris (bereits erwähnt, abzuschließen menschliche Interaktion braucht)
    2. Verwenden Perl für das Datum Teil (meist Unix installiert Perl umfassen, so würde ich in der Regel davon ausgehen, dass diese Aktion macht nicht erfordern zusätzliche Arbeit).

Ein Beispielskript (überprüft das Alter bestimmter Benutzerdateien, um zu sehen, ob das Konto gelöscht werden kann):

#!/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); 
} 
3

Wenn Sie mit awk fortsetzen wollen, dann ist die mktime und strftime Funktionen sind nützlich:


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] 
    } 
} 
+0

aber, wie ich gerade entdeckt habe, mktime ist gawk, und nicht dort auf Solaris awk/nawk :( –

3

Das Buch "Shell-Skript Rezepte: Ein Ansatz Problem Solution" (ISBN: 978-1-59059-471-1) von Chris FA Johnson ein Datum Funktionen Bibliothek, die hilfreich sein könnte. Der Quellcode ist verfügbar unter http://apress.com/book/downloadfile/2146 (die Datumsfunktionen sind in Kapitel08/Daten-Funktionen-sh innerhalb der TAR-Datei).

16

Hier ist eine einfache Möglichkeit, Datumsberechnungen in Shell-Skripten durchzuführen.

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

Im Folgenden sind weitere Variationen der Datumsberechnung, die date Dienstprogramm erreicht werden kann. http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/

Dies funktionierte für mich auf RHEL.

+0

+1 Das ist, was ich arithmetische tun musste. Sehr einfach und unkompliziert. Ich habe die Bestellung m/d/Y korrigiert –

0

Hier sind meine zwei Cent wert - ein Skript Wrapper mit date und grep.

Beispiel Verwendung

> 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 :-(

Script

#!/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 
5

Für BSD/OS X-Kompatibilität, können Sie auch das Datum Dienstprogramm mit -j und -v verwenden Datum Mathematik zu tun. Siehe die FreeBSD manpage for date. Sie könnten die vorherigen Linux-Antworten mit dieser Antwort kombinieren, die Ihnen möglicherweise eine ausreichende Kompatibilität bietet.

Auf BSD, Linux, date ausgeführt werden Sie das aktuelle Datum geben:

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

Jetzt morgige Datum Auflistung mit -v, tun Mathe mit BSDs Datum zum Beispiel können Sie (+1d ist plus einen Tag):

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

Sie können ein vorhandenes Datum als Basis verwenden und optional das Parse-Format mit strftime angeben, und stellen Sie sicher, dass Sieverwenden, so dass Sie nicht Ihr Systemdatum ändern:

$ 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 

Und Sie können diese als Basis für Datumsberechnungen verwenden:

$ 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 

Beachten Sie, dass -v-j impliziert.

Mehrere Einstellungen können sequentiell bereitgestellt werden:

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

Die Manpage für weitere Details.

+0

Danke für die Magie! – Yoga

-1

Dies funktioniert für mich:

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'`