Blog Archives

Not Command Revisited

The reason for https://rvdheij.wordpress.com/2011/11/03/fun-with-while-in-specs/ was that I wanted to write something like the “-p” option on the Linux “mkdir” command. This option ensures that “mkdir” creates any intermediate directories needed to produce the full path. Unfortunately CMS lacks this for SFS, so time I wrote my MKDIR-P EXEC to do it.

The parsing of the path into sub-paths is done as in the referenced post. That provides me with a list of directories to check. When the directory does not yet exist, I need to create it. This is a good opportunity to use my favorite not command again. Normally the return code comes out of the secondary output of command. With not you reverse the streams, so return code is out of the primary and command output is on the secondary. This is handy if you don’t want the output of the command.

From the records with directory sub-paths, the fanout gets two copies. The first copy goes through a predsel stage that takes its secondary input (later) to decide whether to write the input record to the primary output (which leads to the command stage) or to secondary output (not connected, so ignored).


   '| o: fanout',           /* The sub-paths generated */
   '| p: predsel',                                       
   '| spec ,CREATE DIR, 1 w1 nw',     /* Build command */
   '| command',             /* .. and execute it       */
   '| cons',                

The other copy of the directory sub-path is used to construct a QUERY DIRATTR command. The not command just retains the return code, and if it is “28” (not there) we use that record to trigger the predsel stage to pass the record to the primary output.

                              
   '\ o:',                                               
   '| spec ,QUERY DIRATTR, 1 w1 nw ',  /* Query to see */
   '| c: not command',      /* if directory is there   */
   '| strfind ,28,',        /* Use 'Not found' to tick */
   '| p:'                   /* one in predsel          */

Depending on the complexity of the pipeline, you may need to wrap this in the privwu stage to have the command run in a private workunit (more about that in a future post).

Select Records by Example

It’s Plumber Time again… I needed a way to select records based on the first record in the stream (as the selection for a recursive pipeline – more about that later). A popular way to do this is using peekto in a REXX stage to retrieve the key, and then build a selection stage based on that key.


'peekto rec'
key = word(rec,2)
'callpipe (end \) *: | pick w2 == ,'key', | *:'

Although this works, there are some places where the pipe can leak, like when the “key” field contains the character you use as the delimiter of the string (you can fix that by encoding it in hex). But it did not meet my subjective criteria for elegant plumbing…

What I came up with is to use juxtapose to combine the key of the first record with the key of the remaining records, and then have pick select the ones where both keys are identical (implicitly matching the first record obviously). Trying to do this with the live data (prefixing the record with the key) tends to get a bit tricky, so another case where predselect comes handy.

The following is the core of my QBE REXX filter. The argument for the stage is the input range of the record that is used for the matching. The idea is that qbe w3 will select all input records where the third word matches that of the first record.

The spec stage takes care of extracting the specified range and keeps it as a hexadecimal string, so pick can simply compare words to select input records.


parse arg range                                                        
                                                                       
'addpipe (end \ name QBE.REXX:6)',                                     
   '\ *: ',                                                            
   '| o: fanout ',                    /* Original records to pass    */
   '| p: predselect',                                                  
   '| *:',                                                             
   '\ o:',                                                             
   '| spec' range 'c2x 1 , , n ',     /* Extract range as a token    */
   '| z: fanout',                                                      
   '| take',                          /* Keep the first token        */
   '| j: juxtapose',                  /* .. and combine with other   */
   '| x: pick w1 == w2',              /* When identical, pass to     */
   '| p:',                            /* .. predselect for output 0  */
      out1,                                                            
   '\ x:',                            /* Not identical, to           */
   '| p:',                            /* .. predselect to reject     */
   '\ z: ',                                                            
   '| j:'                             /* To juxtapose for pairing    */
                                                                       
return rc * ( rc  12 )                                               


The variable "out1" in the code is set only when the secondary output of the stage is connected. This makes the stage behave like most built-in selection stages. When the secondary output is not connected, the rejected records will be silently dropped. The pre-amble of the stage thus looks like this:

'streamstate all x'                                                    
if words(x) = 1 then out1 = ''        /* Check for secondary output  */
else                                                                   
  do                                                                   
    out1 = '| *.output.1:'                                             
    parse var x . s .                                                  
    parse var s si ':' so .                                            
    call msg si < 12, 1196            /* Reject with secondary input */
  end                                                                  


The error message is issued when the secondary input is connected. The code for the message routine uses the ISSUEMSG Pipeline command.

msg:                        
  parse arg cond, id        
  if cond then              
    do                      
      'ISSUEMSG' id 'ROBQEB'
      exit id               
    end                     
  return                    

Design a site like this with WordPress.com
Get started