Link to home
Create AccountLog in
Avatar of pavelmed
pavelmed

asked on

How to verify dates in UNIX shell script

I have a shell script that accepts two dates of a date range from the command line and sends the dated into the sqlplus script.
How can I verify that the dates are correct before sending to sqiplus?

I would like to check if:
a) date is entered in a correct format (MM/DD/YYYY) and exit if not;
b) beginning date is less than ending date - exit if not;
c) date is not skipped (no blanks from the user).

Is that possible?

Please assist.
Avatar of MikeOM_DBA
MikeOM_DBA
Flag of United States of America image

Why not do the checking in SQL*Plus?
Otherwise, use awk like this:
#!/usr/bin/ksh
# chk_date.sh - Check two dates
#-------------------------------
if [ $# -ne 2 ]
then
  echo "Usage:\n $0 {Date begin mm/dd/yyyy} {Date end mm/dd/yyyy}"
  exit 1
fi
d0=$1
d1=$2
# Set calendar
os=`/bin/uname`
case $os in
  AIX) calwidth=4;;
    *) calwidth=3;;
esac
rx=0
show_err(){
  echo "!Error: Invalid date $1"
  #exit 1
}
chk_date(){
dt=$1
mm=${dt%%/*}
dd=`echo $dt|cut -d'/' -f2`
yy=${dt##*/}
#echo "In date: $yy $mm $dd"
[ `cal $mm $yy 2>&1|grep '0702-001'|wc -l` -gt 0 ] && return 1
cal $mm $yy |\
awk -v dd=$dd -v w=$calwidth '
BEGIN {n=1; d0=int(dd)}
NR < 3 {next}
{
 for(i=0;i<=6;++i){
   da=int(substr($0,(i*w)+1,w));
   if(da==0||da<dd||da>dd){continue}
   n=0; exit n}
} END {exit n}'
return $?
}
chk_date $d0
rc=$?
(( rx += rc ))
[ $rc -ne 0 ] && show_err $d0
db=`printf "%04d%02d%02d\n" $yy $mm $dd`
chk_date $d1
rc=$?
(( rx += rc ))
[ $rc -ne 0 ] && show_err $d1
de=`printf "%04d%02d%02d\n" $yy $mm $dd`
[ $de -lt $db ] && echo "Bad dates: $de less than $db"
[ rx -eq 0 ] && echo "$db $de"
exit $rx

Open in new window



;)
Avatar of skullnobrains
skullnobrains

you can use the date command like this

# read user input
read -p "type in min date" min
read -p "type in min date" max

# convert these to timestamps
min_=$(date --date="$min" +%s) || ! echo "invalid min date '$min'" || exit 1
max_=$(date --date="$max" +%s) || ! echo "invalid max date '$max'" || exit 1

test $min_ -gt $max_ || ! echo min date should be less than max >&2 || exit 1

# add whatever other checks you need here such as a reasonable range

# reconvert the dates to the format you need
min=$(date --date="@$min" +%m/%d/%Y)
max=$(date --date="@$max" +%m/%d/%Y)

Open in new window


the script will additionally understand the wide variety of formats the date function understands including MM/DD/YYYY but also things like DD-MM-YYYY, YYYY-MM-DD or even "next week"

if you need to limit the input format to MM/DD/YYYY, it should be rather trivial to add an additional simple regular expression to validate the basics "[0-9]\{2\}/[0-9]\{2\}/[0-9]\{4\}" and let date complain if the user types invalid dates like 02/29/2003
Avatar of pavelmed

ASKER

Sorry for the delay in my response.
I appreciate both solutions shown above but they don't work for me.
The first one errors out and I don't have enough knowledge to debug.
The second one with "date" also errors out because the --date option is not available for me.

So I am trying not to use a compromised solution:
I am going to check for an empty string is shell string, and verify date in the sqlplus.

How can I return an error description from the sqlplus back to shell script so that I could display it from there?

I am going to execute a statement like this in the sqlplus:
select to_date('$BEGDATE', 'MM/DD/YYYY') from dual;
where $BEGDATE is a value of date received from the user's input in shell script.

In my opinion it should error if the date is entered in a wrong format.

Also, how can I compare dates in sqlplus and return an error to the shell script if BEGDATE is less than ENDDATE?

Thank you.
Try something like this:
if [ $# -ne 2 ]
then
  echo "!Error: missing parameters, 1-{begin date} 2-{end_date}."
  exit 1
fi
BEGDATE=$1
ENDDATE=$2
ORAENV_ASK=NO
. /usr/local/bin/oraenv orcl >/dev/null
result=`
sqlplus -s / <<EOSQL
set echo off term off feed off
set serverout on size 1000000;
DECLARE
  dt_beg   DATE;
  dt_end   DATE;
BEGIN
  BEGIN
    SELECT TO_DATE ( '$BEGDATE', 'MM/DD/YYYY') INTO dt_beg FROM DUAL;
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line ('!Error: $BEGDATE ' || SQLERRM);
  END;
  BEGIN
    SELECT TO_DATE ( '$ENDDATE', 'MM/DD/YYYY') INTO dt_end FROM DUAL;
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line ('!Error: $ENDDATE ' || SQLERRM);
  END;
  BEGIN
    IF dt_beg > dt_end
    THEN
      dbms_output.put_line ('!Error: $BEGDATE is greater than $ENDDATE.');
    END IF;
  END;
END;
/
exit
EOSQL`
[ ! -z $result ] && echo "$result"

Open in new window

:p
MikeOM_DBA,
Thank you for your suggestion.

I copied your code into mine with slight changes, for example, I changed EOSQL to EOF which worked in my code.
That line below I commented out because my script stopped responding when I entered it:
. /usr/local/bin/oraenv orcl >/dev/null

Anyway, the script returns the following:
./audsh2.sh[50]: 01/01/2001:  not found

where audsh2.sh is a name of the script file, and 01/01/2001 is a date entered as a beginning date.

Please advise.
I also have a different sqlplus string (and a connection string of course):

sqlplus -s /nolog > /dev/null 2>&1 << EOF
connect username/mypassword@mydatabase
can you post the complete command you type, the result and some data about your environment (at least the OS, shell, and sqlplus versions) ? either for the sqlpus of the shell way ?
SQL*Plus: Release 11.2.0.1.0
OS - UNIX
The default shell script on the server is KSH, so I use this line at the beginning:
#!/bin/ksh

I tried to change it to #!/bin/sh but then it displays a different error:
./audsh2.sh: 03/03/2000: not found
./audsh2.sh: test: argument expected
SOLUTION
Avatar of MikeOM_DBA
MikeOM_DBA
Flag of United States of America image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
OS - UNIX ???
sco ? bsd ? solaris ? the environment are very different

the error you have in sh will disapear if you quote BEGDATE="$1" and other parameters at the beginning of the script
OS - SunOS dover 5.10 Generic_142900-13 sun4u sparc SUNW,Netra-T12

I get data into BEGDATE from the user's input in response to a prompt, like this:

echo "Enter beginning date of a date range in MM/DD/YYYY format:"
read BEGDATE
#echo "$BEGDATE"

if [ -z "$BEGDATE" ]; then
    echo "Empty beginning date.  Please try again"
    exit
fi

echo "Enter ending date of a date range in MM/DD/YYYY format:"
read ENDDATE

if [ -z "$ENDDATE" ]; then
    echo "Empty ending date.  Please try again"
    exit
fi

There are just two parameters, BEGDATE and ENDDATE
ckdate should be available on your machine
it will prompt the user for  date, take care of validation, and send explicit error messages
the format you want is the default one
http://docs.oracle.com/cd/E19253-01/816-5165/6mbb0m9c0/index.html

if you want to validate without propmpting, you can use valdate instead
ckdate is available but it does not accept some valid dates.
But most importantly, I need to save the value of a user's input to a variable, and I can't figure out how to send the ckdate input to a variable.
ASKER CERTIFIED SOLUTION
Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
I am sorry I forgot to complete this question.

I resolved this by combining your and some other suggestions, and I would like to split the points.

Thank you for your help.