Operating System - OpenVMS
1752732 Members
6284 Online
108789 Solutions
New Discussion

Re: VMS Script - Search for String on a Line

 
Bushytea
Occasional Advisor

VMS Script - Search for String on a Line

Hi,

 

I am working on a VMS script that was written by a former employee. The script works like I need it to besides with the addition of one If/Then statement. I am trying to get the statement to search for a particular string on a line that could have up to 5 or so other strings on the same line. However, I just need it to locate the one certain string.

 

An example of what the line could look like is below but it can vary and I am just needing to figure out how to have the script search this line, find the DisUser flag and if present move onto the next record.

 

Flags: DisCtlY Restricted DisUser DisNewMail DisReport Captive

 

Below is a piece of the script that I am working with as well. As you can see in the middle there is a statement that is trying to search for the DisUser string. However, instead of skipping the records with this string it still includes them in the file it writes.

 

$ GETREC:
$ read/end=quit infile rec
$ if (f$locate("Username:",rec) .eq. 0) .and. (f$locate(":",rec) .ne. 0)
$ then usr = f$extract(10,9,rec)
$ endif
$ if (f$locate("Flags",rec) .eqs. "DisUser")
$ then goto getrec
$ endif
$ if (f$locate("Last Login:",rec) .eq. 0) .and. (f$locate(":",rec) .ne. 0)
$ then date1 = f$extract(12,11,rec)
$ date2 = f$extract(45,11,rec)
$ else goto getrec
$ endif

 

Thank you in advance for any help or assistance.

17 REPLIES 17
Hoff
Honored Contributor

Re: VMS Script - Search for String on a Line

Here's the basic logic to check for the existence of a substring within the specified string:

 

$ if f$locate("DisUser",rec) .ne. f$length(rec) then write sys$output "Found DisUser"

 

I'd probably change that to the following to force the search string to single-spaced and trimmed uppercase text, and tweak the search as follows:

 

$ rec = f$edit(rec,"UPCASE,COMPRESS,TRIM")   ! or analogous

$ if f$locate("DISUSER",rec) .ne. f$length(rec) then write sys$output "Found DisUser"

And FWIW, parsing the output of commands and utilities is generally considered unsupported and fragile.  What you have probably adapts, but changes to the command or utility output can cause weird run-time errors.

 

As for alternatives, there is the GETUAI tool.   This uses documented interfaces to access SYSUAF.

 

I'd also suggest reading the OpenVMS User's Manual in the OpenVMS documentation shelf, if you're unfamiliar with DCL programming and DCL command procedures.

 

Mike Kier
Valued Contributor

Re: VMS Script - Search for String on a Line

The author of the script is confused on the use of f$Locate.  The return value is an integer, either a byte offset beginning with zero if the substring is found, or the length of the string being searched if the substring is not found.  See $ HELP LEXICAL F$LOCATE.

 

That said, the first IF statement's second clause is superfluous since if "Username:" is found at byte offset zero then there cannot also be a ":" at byte offset zero.

 

The IF statement for the "DisUser" is completely wrong.

 

I would suggest something like

 

$ IF f$Locate("Flags:", rec) .ne. f$Length(rec) ! This is the Flags line

$ Then

$   IF f$Locate("DisUser") .ne. f$Length(rec) Then GoTo getrec

$ EndIf

Practice Random Acts of VMS Marketing
Hein van den Heuvel
Honored Contributor

Re: VMS Script - Search for String on a Line

>> ind the DisUser flag and if present move onto the next record.

 

 I don't think that is really what you want, unless you mean 'next sysuaf record' versus next record in listing.

I strongly suspect that the script should not just skip that line, but skip everything else for that username.

For that the script needs to maintain a flag to remember the event.

Personally I often prefer  to use a multi-purpose 'skip' flag versus a specific 'disuser_seen' flag.

 

The code might then ROUGHLY look like:

 

$ close/nolog infile
$ open /read infile sysuaf.lis
$
$ skip = 0
$ GETREC:
$ read/end=quit infile rec
$ len = f$length(rec)
$ if f$extract (0,9,rec) .EQS. "Username:"
$ then
$   skip = 0
$   usr  = f$extract(10,9,rec)
$ endif
$ IF (f$extract (0,6,rec).EQS."Flags:") .AND. (len .NE. f$locate("DisUser",rec)) THEN skip = 1
$ IF skip THEN GOTO getrec
$ if (f$locate("Last Login:",rec) .eq. 0) .and. (f$locate(":",rec) .ne. 0)
$ then
$   date1 = f$extract(12,11,rec)
$   date2 = f$extract(45,11,rec)
$   WRITE SYS$OUTPUT usr, " ", date1, " ", date2
$   skip = 1  ! All done untill next "Username:"
$ endif
$  goto getrec
$quit:
$ CLOSE infile

 

 

 Hein.

 

Bushytea
Occasional Advisor

Re: VMS Script - Search for String on a Line

Thanks for the quick replies.

 

Yes, I do mean the next sysauf record. What the script is doing is writing users to a file but I am needing to omit the users who have this DisUser flag from the file.

 

Thanks,

Mike

The Brit
Honored Contributor

Re: VMS Script - Search for String on a Line

Spoiler
 

If all you are trying to do is separate "DisUser'd" accounts from the rest, you might want to consider using the "/Brief" output from the SYSUAF.     This way you get 1 line per account, and the the DISUSER flag will be the last item on the line, (if the account is disusered)

 

$ mc authorize show /brief   smithd

Owner            Username         UIC         Account      Privs     Pri       Directory

David Smith  SMITHD       [120,4127]      912            All         4         Disuser

 

The obvious advantage is that there is less garbage to churn through.

 

Having said that, you should look in to using GETUAI utility, as suggested by Hoff.

 

Dave.

Craig A Berry
Honored Contributor

Re: VMS Script - Search for String on a Line

The problem with the GETUAI utility that a couple of people have suggested is that it doesn't really fit the problem being presented, which appears to be to query for all the usernames that have not been disusered.  As far as I can tell, with GETUAI you have to already know the username you are interested in.  You'd still have loop over a listing or SYSUAF.DAT itself to find out what all the usernames are and then test each one for whether it's been disusered.

 

Joe Meadows' UAF utility, on the other hand, makes querying easy.  Just do:

 

$ uaf/select=flags=nodisuser/display=username

 

It's available from:

 

http://code.google.com/p/jmuaf/

x2084
Trusted Contributor

Re: VMS Script - Search for String on a Line

You may want to use search to reduce and select the wanted information. Something like

 

$ pipe define/user sysuaf sys$system:sysuaf ; mc authorize show * |search sys$input Username:,Flags: |search sys$input DisUser/match=nand/out=x.lis
$ pipe search x.lis Flags:/wind=(1,0) |search sys$input Username:

 

 

Hein van den Heuvel
Honored Contributor

Re: VMS Script - Search for String on a Line

So are you all set now with the various suggestions?

btw 1... careful with the hardcoded 9 character username.

OpenVMS itself supports 12.

 

Btw 2... I 'happened' to have a bit of DCL which possibly does pretty much what you want.

It can go straight for SYSUAF (local copy in example below).

And yeah, it could flip the disuser bit and update as well, but I prefer to go through a file.

 

$!
$! uaf_lastlogin.com    Hein van den Heuvel,August 2007.
$
$! List records from SYSUAF for which the Last Interactive Login is more
$! than 'p1' days ago (default 90).
$
$IF p1.eqs."" THEN p1 = "90"
$cutoff_date = f$cvtime("TODAY -''p1'-")
$!libr/extr=$uafdef/out=uafdef.tmp sys$library:lib.mlb
$!sea uafdef.tmp flag...
$!EQU    UAF$Q_LASTLOGIN_I       396
$!EQU    UAF$L_FLAGS     468
$!EQU    UAF$V_DISACNT   4
$
$define sysuaf sys$disk:[]sysuaf.dat  ! Local copy for testting
$sysuaf = f$parse("SYSUAF","SYS$SYSTEM:.DAT",,,"SYNTAX_ONLY")
$open /read/share=write uaf 'sysuaf'
$loop:
$ read/end=done uaf rec
$ lastlogin_bin = F$EXTR(396,8,rec)
$ lastlogin_asc = F$FAO("!%D",f$cvui(32,32,f$fao("!AD",8,lastlogin_bin)))
$ IF f$cvtime(lastlogin_asc) .GTS. cutoff_date THEN GOTO loop
$ IF f$cvsi(468*8+4,1,rec) THEN GOTO loop ! disuser already?
$ username = F$EXTRACT(4,12,rec)
$ WRITE SYS$OUTPUT "MODIFY ''username' /FLAG=DISUSEER ! Last Login: ", lastlogin_asc
$ GOTO loop
$done:
$close uaf

 

Also, a similar script using perl....

 

while (<>) {
  if (/^Username:(.{9})/) {
    $skip = 0;
    $user = $1;
  }
  next if $skip;
  $skip = 1 if /^Flags.*DisUser/;
  if (/^Last Login:.(.{11}).{22}(.{11})/) {
    print qq($user $1 $2\n);
    $skip = 1;
  }
}

 

Bushytea
Occasional Advisor

Re: VMS Script - Search for String on a Line

Yeah, the suggestions help give a bit of a clearer picture as to how some of this works. It is always difficult to work with a new language plus try to figure out what the previous person was doing.

 

The way this was setup was to compare dates to see who has not logged on in 90 days, the user does not have a DisUser flag already and then write the Username plus dates to a .dat file. Then, you run another script that actually goes through the .dat  file created and DisUser's all the usernames. I believe we write to the file with one script do allow us to proof the usernames before going ahead and setting the DisUser flag.

 

Just so you guys can see here is the entire script that searches through the sysuaf.lis file for the users.

 

$ !
$ set noverify
$ set noon
$ !
$ compdt = "28-FEB-2008"
$ defdt = "01-JUL-2008"
$ !
$ if p1 .nes. ""
$ then compdt = p1
$ write sys$output "Compare date is " + compdt
$ else
$ write sys$output "No comparison date included."
$ goto quit
$ endif
$ !
$ set def sys$system
$ uaf := "$authorize"
$ uaf list * /full
$ set def mvx
$ open/read infile sys$system:sysuaf.lis
$ open/write outfile uaf_old_accts.dat
$ GETREC:
$ read/end=quit infile rec
$ if (f$locate("Username:",rec) .eq. 0) .and. (f$locate(":",rec) .ne. 0)
$ then usr = f$extract(10,9,rec)
$ endif
$ if (f$locate("Last Login:",rec) .eq. 0) .and. (f$locate(":",rec) .ne. 0)
$ then date1 = f$extract(12,11,rec)
$ date2 = f$extract(45,11,rec)
$ else goto getrec
$ endif
$ if date1 .eqs. "           " then date1 = defdt
$ if date2 .eqs. "           " then date2 = defdt
$ if (f$cvtime(date1,,) .lts. f$cvtime(compdt,,)) .and. -
 (f$cvtime(date2,,) .lts. f$cvtime(compdt,,)) then goto writerec
$ goto getrec
$ WRITEREC:
$ wrtrec = usr+"   "+date1+" - "+date2
$ write outfile wrtrec
$ goto getrec
$ QUIT:
$ close infile
$ close outfile
$ exit
[End of file]

 Thanks!