resueltos - Fecha aritmética en scripts de shell Unix
shell script ejemplos (14)
¿Por qué no escribir sus scripts usando un lenguaje como perl o python en su lugar, que de forma más natural es compatible con el complejo procesamiento de fechas? Claro que puedes hacerlo todo en bash, pero creo que también obtendrás más consistencia en todas las plataformas que usan Python, por ejemplo, siempre y cuando puedas asegurarte de que Perl o Python estén instalados.
Debo añadir que es bastante fácil de conectar en scripts de Python y Perl en un script de shell que contiene.
Necesito hacer aritmética de fecha en las secuencias de comandos de shell de Unix que utilizo para controlar la ejecución de programas de terceros.
Estoy usando una función para incrementar un día y otra para disminuir:
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
pero ahora necesito hacer una aritmética más compleja.
¿Cuál es la mejor y más compatible manera de hacer esto?
Al analizarlo más a fondo, creo que simplemente puede usar la fecha. He intentado lo siguiente en OpenBSD: tomé la fecha del 29 de febrero de 2008 y una hora aleatoria (en el formato de 080229301535) y agregué +1 a la parte del día, de esta manera:
$ date -j 0802301535
Sat Mar 1 15:35:00 EST 2008
Como puede ver, la fecha se formateó correctamente ...
HTH
Si la versión de la fecha GNU le funciona, ¿por qué no toma la fuente y la compila en AIX y Solaris?
http://www.gnu.org/software/coreutils/
En cualquier caso, la fuente debe ayudarlo a obtener la aritmética de la fecha correcta si va a escribir su propio código.
Por otro lado, comentarios como "esa solución es buena pero seguramente se nota que no es tan buena como puede ser. Parece que a nadie se le ocurrió jugar con las fechas cuando se construyó Unix". no nos lleves a ningún lado. Encontré que cada una de las sugerencias hasta ahora es muy útil y objetivo.
El libro "Recetas de Shell Script: un enfoque de solución de problemas" (ISBN: 978-1-59059-471-1) de Chris FA Johnson tiene una biblioteca de funciones de fecha que podría ser útil. El código fuente está disponible en http://apress.com/book/downloadfile/2146 (las funciones de fecha están en Chapter08 / data-funcs-sh dentro del archivo tar).
Me he topado con esto un par de veces. Mis pensamientos son:
- La aritmética de la fecha es siempre un dolor
- Es un poco más fácil cuando se utiliza el formato de fecha EPOCH
- la fecha en Linux se convierte en EPOCH, pero no en Solaris
- Para una solución portátil, debe hacer una de las siguientes cosas:
- Instalar gnu date en solaris (ya mencionado, necesita interacción humana para completar)
- Use perl para la parte de fecha (la mayoría de las instalaciones de Unix incluyen perl, por lo que generalmente asumiría que esta acción no requiere trabajo adicional).
Un script de muestra (verifica la antigüedad de ciertos archivos de usuario para ver si la cuenta se puede eliminar):
#!/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);
}
Si desea continuar con awk, entonces las funciones mktime y strftime son útiles:
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]
}
}
Para hacer operaciones aritméticas con fechas en UNIX, obtienes la fecha como el número de segundos desde la época UNIX, haz algunos cálculos y luego vuelve a convertir a tu formato de fecha imprimible. El comando de fecha debería poder darle los segundos desde la época y convertir desde ese número a una fecha imprimible. Mi comando de fecha local hace esto,
% date -n
1219371462
% date 1219371462
Thu Aug 21 22:17:42 EDT 2008
%
Consulte su página de manual de date(1)
local date(1)
. Para incrementar un día, agregue 86400 segundos.
He escrito un script bash para convertir fechas expresadas en inglés en fechas mm / dd / aaaa convencionales. Se llama ComputeDate .
Aquí hay algunos ejemplos de su uso. Para abreviar, he colocado la salida de cada invocación en la misma línea que la invocación, separada por dos puntos (:). Las comillas que se muestran a continuación no son necesarias cuando se ejecuta 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
He incluido esta secuencia de comandos como una respuesta a este problema porque ilustra cómo hacer la aritmética de la fecha a través de un conjunto de funciones de bash y estas funciones pueden ser útiles para otras. Maneja los años bisiestos y salta siglos correctamente:
#! /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 $*
Este script hace un uso extensivo de otro script que escribí (llamado col ... muchas disculpas para aquellos que usan el col estándar suministrado con Linux). Esta versión de col simplifica la extracción de columnas del stdin. Así,
$ echo a b c d e | col 5 3 2
huellas dactilares
e c b
Aquí está el script 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}"
También utiliza printdirections para imprimir indicaciones cuando el script se invoca incorrectamente:
#!/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.$$
Para utilizarlo, coloque las tres secuencias de comandos en los archivos ComputeDate , col e printdirections , respectivamente. Coloque el archivo en el directorio nombrado por su RUTA, generalmente, ~ / bin. Entonces hazlos ejecutables con:
$ chmod a+x ComputeDate col printdirections
¿Problemas? Envíeme un correo electrónico: morell AT cs.atu.edu Coloque ComputeDate en el asunto.
Estos son mis dos centavos que vale la pena: un envoltorio de script que utiliza date
y grep
.
Ejemplo de uso
> 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 :-(
Guión
#!/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
Aquí hay una manera fácil de hacer cálculos de fecha en scripts de shell.
meetingDate=''12/31/2011'' # MM/DD/YYYY Format
reminderDate=`date --date=$meetingDate''-1 day'' +''%m/%d/%Y''`
echo $reminderDate
A continuación hay más variaciones del cálculo de fechas que se pueden lograr usando la utilidad de date
. http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/
Esto funcionó para mí en RHEL.
date --date=''1 days ago'' ''+%a''
No es una solución muy compatible. Funcionará solo en Linux. Al menos, no funcionó en Aix y Solaris.
Funciona en RHEL:
date --date=''1 days ago'' ''+%Y%m%d''
20080807
Para la compatibilidad BSD / OS X, también puede usar la utilidad de fecha con -j
y -v
para hacer la fecha matemática. Consulte la página de manual de FreeBSD para conocer la fecha . Podría combinar las respuestas de Linux anteriores con esta respuesta que podría proporcionarle compatibilidad suficiente.
En BSD, como Linux, la date
ejecución le dará la fecha actual:
$ date
Wed 12 Nov 2014 13:36:00 AEDT
Ahora con la fecha de BSD puedes hacer matemática con -v
, por ejemplo, enumerar la fecha de mañana ( +1d
es más un día ):
$ date -v +1d
Thu 13 Nov 2014 13:36:34 AEDT
Puede utilizar una fecha existente como base y, opcionalmente, especificar el formato de análisis utilizando strftime, y asegurarse de usar -j
para no cambiar la fecha del sistema:
$ 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
Y puede usar esto como la base de los cálculos de fecha:
$ 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
Tenga en cuenta que -v
implica -j
.
Se pueden proporcionar múltiples ajustes secuencialmente:
$ date -v +1m -v -1w
Fri 5 Dec 2014 13:40:07 AEDT
Vea la página de manual para más detalles.
Esto funciona para mí:
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''`