Ksh: Read line parse characters into variable and remove the line if the date is older than 50 days

I have a test file with the following format, It contains the username_date when the user was locked from the database.

$ cat lockedusers.txt

TEST1_21062016          
TEST2_02122015  
TEST3_01032016  
TEST4_01042016

I'm writing a ksh script and faced with this difficult scenario for my level of scripting. What I would like to do is:

Read the line from text file,
If the date value of this line is older than 50 days,
then read the line till before the underscore character e.g. TEST1 into a variable,
and then remove this line.
The variable will be used for removing the user from database. Any help would be appreciated.

Save as beyondfifty.pl

#!/usr/bin/perl
use strict;
use warnings;
use Time::Local;
use File::Copy qw(move);

my $locked = "lockedusers.txt";
my $keep = "$locked.tmp";

my $fifty_days_ago = 86400 * 50;
my $now = time;

open my $in, '<', $locked  or die;
open my $out, '>', $keep or die;
while(<$in>) {
    my ($user, $day, $month, $year) = /(\w+)_(\d{2})(\d{2})(\d{4})/;
    my $time_ago = timelocal(0, 0, 0, $day, $month-1, $year-1900);
    ($now - $time_ago) > $fifty_days_ago ? print "$user\n" : print $out $_;
}
close $in;
close $out;
move $locked, $locked . ".bk" and move $keep, $locked;

Run as:

perl beyondfifty.pl | while read user; do echo "db command to delete $user"; done

Output:

db command to delete TEST2
db command to delete TEST3
db command to delete TEST4

Substitute the echo "db command to delete" for your own database command. It removes the matched entries. It creates a backup lockedusers.txt.bk

1 Like

Not sure what you mean with "then remove this line", but with this, you'll have the desired LUSER variable if the date portion is longer than 50 days back:

while IFS="_ " read LUSER LDATE
  do    [ $(( $(date +%s) - $(date +%s -d"${LDATE:2:2}/${LDATE:0:2}/${LDATE: -4}") - 4320000 )) -ge 0 ] && echo $LUSER, $LDATE
  done < file

This is not ksh tested. You might be able to use ksh 's printf "%(fmt)T" builtin should your system's date not allow for the -d option...

@RudicC

This is great, it worked like a charm after a little modification. Where are we setting the 50 days? in case I need to adjust the days.

Thanks a lot

50 * 86400 sec = 4320000 sec

1 Like

and 86400 sec = 60 * 60 * 24 sec = 1 day
A - B > 0 is equal to A > B so you can do

while IFS="_ " read LUSER LDATE
do
  if [ $(( $(date +%s) - $(date +%s -d"${LDATE:2:2}/${LDATE:0:2}/${LDATE: -4}") )) -ge 4320000 ]
  then
    echo "$LUSER, $LDATE"
  fi
done < lockedusers.txt
1 Like

Hi guys, for some reason I kept getting the following error when I run the test script.

#!/usr/bin/ksh
while IFS="_ " read LUSER LDATE
do
  if [ $(( $(date +%s) - $(date +%s -d"${LDATE:2:2}/${LDATE:0:2}/${LDATE: -4}") )) -ge 4320000 ]
  then
    echo "$LUSER" >> userRemovalList
	chmod 755 userRemovalList
    grep -v "$LUSER" test> test.new && mv test.new test
	echo "UserID: "${LUSER}         "Date Removed: "${TODAYS_DATE} >> RemovedUserList
  fi
done < test


./remove.ksh[8]: : bad substitution
TEST1, 03012016
./remove.ksh[8]: : bad substitution
TEST2, 11072016
./remove.ksh[8]: : bad substitution
TEST3, 05052016
./remove.ksh[8]: : bad substitution
TEST4, 02012016
./remove.ksh[8]: : bad substitution
TEST5, 20022016

And when I run this same script without #!/usr/bin/ksh , it runs fine

TEST1, 03012016
TEST3, 05052016
TEST4, 02012016
TEST5, 20022016
oracle@11204_home>which ksh
/usr/bin/ksh

Please provide suggestion

Are you sure the output posted comes from that script? It nowhere prints the username, a comma, a space, the date.
For that error message, is it possible that either variable name has an unprinting control char in it?

BTW, TODAYS_DATE is undefined in that script (which doesn't hurt, there's just an empty string printed)
And, it's not too wise to mv a temp file to the actual input file, overwriting it, as you may pull the rug out from under your own feet...

Anyone can provide any hint as to what could be wrong? Thanks

---------- Post updated at 02:45 PM ---------- Previous update was at 02:37 PM ----------

Sorry here's the actual script, yes I still get the same error.

#!/usr/bin/ksh
while IFS="_ " read LUSER LDATE
do
  if [ $(( $(date +%s) - $(date +%s -d"${LDATE:2:2}/${LDATE:0:2}/${LDATE: -4}") )) -ge 4320000 ]
  then
    echo "$LUSER,  $LDATE"
    grep -v "$LUSER" test > test.new && mv test.new test
  fi
done < test
 /dbashare/ToolBox/UserTermination/MonthlyDeletionoracle@11204_home>./remove.ksh
./remove.ksh[9]: : bad substitution
./remove.ksh[9]:  1468262786 -  : unexpected `end of expression'
 /dbashare/ToolBox/UserTermination/MonthlyDeletionoracle@11204_home>

---------- Post updated at 03:25 PM ---------- Previous update was at 02:45 PM ----------

I can confirm this one more time that it is caused by adding

#!/usr/bin/ksh

on top and works fine when I remove this line. However I use this as a function for a lrger ksh script and get the same error when I integrate it with my larger script.

  • You are removing lines from test inside the loop when it is also the input file for the loop. That is not right.
  • What version of ksh are you using ? If it is ksh88 then the substitutions will not work.
  • and what is your OS?
1 Like

Hi everyone, thanks for your help. A collegue helped me and it works with this code

#!/bin/ksh
echo
cat test |
while IFS="_ " read LUSER LDATE
do
        Day=`echo $LDATE | cut -c1,2`
        Mon=`echo $LDATE | cut -c3,4`
        Year=`echo $LDATE | cut -c5-8`
        Date=`echo $Mon/$Day/$Year`
        if [[ $(( $(date +%s) - $(date +%s -d${Date}) )) -ge 4320000 ]] ; then
        #echo "$LUSER, $LDATE"i
        echo "$LUSER" >> userRemovalList
        grep -v "$LUSER" test > temp && mv temp test
        fi
done
chmod 755 userRemovalList

You should avoid changing the "test" file when cat could still be reading it, this will become evident if your input file is large and cat hasn't buffered the whole thing before the script starts changing it.

Also, if you have a large file to process this should run a bit quicker (not calling external cut command 3 times per line):

#!/bin/ksh

TSEC=$(date +%s)
while read LINE
do
   LDATE=${LINE#*_}
   YEAR=${LDATE#????}
   MDAY=${LDATE%????}
   DAY=${MDAY%??}
   MON=${MDAY#??}
   [[ $(( TSEC - $(date -d $MON/$DAY/$YEAR +%s) )) -lt 4320000 ]] &&
      echo "$LINE"
done < test > test.$$
mv test.$$ test
1 Like