I have a Rexx program that I want to run as a batch job. How can I do this?
This is my program :-
/* Rexx – HELLO – Write Hello World */
Say "hello World"
The program is located as member HELLO in the PDS ME.USER.EXEC.
A valid JOB CARD for my installation is (our environment includes ISPF/PDF as opposed to ROSCOE):-
//MYJOB JOB ,,CLASS=1,MSGCLASS=H,NOTIFY=&SYSUID
Note! this has been written as a tutorial
There are a number of ways that you can run the program via batch. I will cover 3 ways all of which are different according to the environment (i.e. what they can utilise.)
Method 1 - Run the program in a Rexx environment.
This entails running the program IRXJCL and passing the name of the program (i.e. the PDS's member name) via the PARM field (you can also pass parameters; accessing them via a PARSE ARG statement).
IRXJCL requires (normally) 3 DDNAMES they are SYSEXEC (The PDS where the program is located), SYSTSIN (this can reflect terminal input) and SYSTSPRT (this is where terminal output is sent).
Here is the JCL that would work according the information provided above:-
//MYJOB JOB ,,CLASS=1,MSGCLASS=H,NOTIFY=&SYSUID
//*-------------------------------------------------------------------
//RUNPROG EXEC PGM=IRXJCL,PARM=’HELLO’
//*
//* RUN OUR REXX PROGRAM CALLED HELLO
//*
//SYSEXEC DD DSN=ME.USER.EXEC,DISP=SHR
//SYSTSIN DD DUMMY
//SYSTSPRT DD SYSOUT=*
//*-------------------------------------------------------------------
This method, although the simplest (by just a few lines of JCL), is the most restrictive in that it does not allow the use of
TSO/E services, such as TSO/E commands and most of the TSO/E external
functions.
However, as IRXJCL is a Rexx processor, there is no requirement to let
TSO/E know that it is a Rexx program (the first line must include
REXX).
Method 2 - Run the program from a TSO/E environment
This entails running one of the TSO/E batch processing programs IKJEFT01 is used in this example. Alternatives are IKJEFT1A and IKJEFT1B. TSO/E services and commands can be used via this method (e.g. note at end of this method using the TIME command)
Comprehensive information about the differences between the programs
can be found at Writing JCL for command
execution
The JCL for IKJEFT01 is similar to that used in Method 1. An additional DDNAME SYSPROC can be coded. SYSPROC is the DDNAME where CLISTS would be located; You can have Rexx programs found here in addition to SYSEXEC (which isn't required, a suggestion is that both are coded and that SYSEXEC is used for Rexx programs and SYSPROC is used for CLISTS).
It's the requirement that Rexx is on the first line that
differentiates a Rexx program from a CLIST (by the TSO/E processor). Thus, if the Rexx program is found/located via SYSEXEC, if I recall correctly, this negates the requirement).
Another suggestion is to always include REXX in the first line of a Rexx program>
The EXEC statement invokes the IKJEFT01 program instead of IRXJCL. PARM can be used to specify the first command (and therefore our HELLO program). However, as for foreground, you can specify this via the terminal i.e. the SYSTSIN DDNAME.
Here is some JCL that would work for the second method; noting that the HELLO program is invoked via the SYSTSIN DDNAME as instream data :-
//MYJOB JOB ,,CLASS=1,MSGCLASS=H,NOTIFY=&SYSUID
//*-------------------------------------------------------------------
//RUNPROG EXEC PGM=IKJEFT01
//*
//* RUN OUR REXX PROGRAM CALLED HELLO IN A TSO/E ENVIRONMENT
//*
//SYSPROC DD DSN=ME.USER.CLIST,DISP=SHR
//SYSEXEC DD DSN=ME.USER.EXEC,DISP=SHR
//SYSTSIN DD *
HELLO
//SYSTSPRT DD SYSOUT=*
//*-------------------------------------------------------------------
If, for example, the following were used (i.e. added TIME as another
line to SYSTSIN) then the TSO/E TIME command would be run (which would
cause the time to be displayed to SYSTSPRT).
//SYSTSIN DD *
HELLO
TIME
Method 3 - Run the program in an ISPF environment
This method uses the IKJEFT01 program (see method 2 for IKJEFT1A/B alternatives). However, it then uses the ISPSTART command to run the program in an ISPF environment; enabling the use of ISPF services (e.g file tailoring (skeletons) ISPF tables etc).
An ISPF environment has additional requirements in that ISPF libraries need to be allocated in order to start an ISPF environment. At a minimum have the supplied ISPF libraries allocated to ddnames ISPPLIB (ISPF Panels), ISPMLIB (ISPF Messages) & ISPTLIB (ISPF Tables). ISPPROF is where ISPF keeps some profile data for the session, so a temporary store is sufficient (UNIT=SYSDA is often available if not always).
Note you would likely allocate, at a minimum, the installations system
libraries (the TSO/E command LISTA can likely be used to determine
these from a foreground session). Alternately, ask you local friendly system programmers. In the following they are SYS1.ISPPLIB, SYS1.ISPMLIB and SYS1.ISPTLIB.
Here is some JCL that would work for the 3rd method. Note that HELLO is passed as a parameter to the ISPSTART command.
//MYJOB JOB ,,CLASS=1,MSGCLASS=H,NOTIFY=&SYSUID
//*-------------------------------------------------------------------
//RUNPROG EXEC PGM=IKJEFT01
//*
//* RUN OUR REXX PROGRAM HELLO IN A TSO/E/ISPF ENVIRONMENT
//*
//SYSPROC DD DSN=ME.USER.CLIST,DISP=SHR
//SYSEXEC DD DSN=ME.USER.EXEC,DISP=SHR
//ISPPLIB DD DSN=SYS1.ISPPLIB,DISP=SHR
//ISPMLIB DD DSN=SYS1.ISPMLIB,DISP=SHR
//ISPTLIB DD DSN=SYS1.ISPTLIB,DISP=SHR
//ISPPROF DD UNIT=SYSDA,SPACE=(CYL,(10,1)),
// RECFM=FB,LRECL=80,BLKSIZE=0
//SYSTSIN DD *
ISPSTART CMD(HELLO)
//SYSTSPRT DD SYSOUT=*
//*-------------------------------------------------------------------
Note this is not a fully comprehensive, it is an overview that should
suffice for getting started with running Rexx programs in batch.
Additional comments to the above answer.. The below technote may be helpful if you wish to run your REXX exec using ISPF services...
http://www.ibm.com/support/docview.wss?uid=swg21023990
Make sure that the ISPPROF file is concatenated as the first file in ISPTLIB. The example uses a temporary file that will be unique to the job. If the REXX exec does table services you may need an ISPTABL DD. I would suggest using the same file for ISPPROF and ISPTABL and concatenate it first in ISPTLIB. This can be a permanent file if the table needs to be save, however it should not be in use by other jobs or TSO users so as to avoid enqueue errors.
As the previous update stated his answers suffice for the simple REXX exec.
Yet more additional comments to the answer by MikeT...
Suppose you want your JCL to be self-contained: you want to include a REXX exec as an in-stream data set in the JCL, rather than referring to a REXX exec stored in a PDS member.
Here's the "trick" (for IRXJCL; I haven't tested this trick with other programs): specify a single null (X'00') byte as the value of the PARM attribute of the EXEC statement.
To specify the null byte, use the z/OS ISPF editor with HEX ON:
//REXX EXEC PGM=IRXJCL,PARM=' '
66DCEE44444CECC4DCD7CDEDCD6DCDD7707
1195770000057530774E997133B7194ED0D
For example, if you entered PARM=' ' with a space as the value, overtype the 4 of the hex value of that space with a 0.
Here's an example job step containing an in-stream REXX exec that processes the output from a previous step in the same job:
//* PARM value is a single X'00' byte
//REXX EXEC PGM=IRXJCL,PARM=' '
//SYSEXEC DD DATA,DLM=$$
/*
Transposes first line of input CSV into one record per field
Reads CSV from ddname SYSTSIN.
Writes output to ddname SYSTSPRT.
*/
columnSeparator = ","
/* Get the header row */
parse pull row
/* Get column names */
do i = 1 until row = ""
parse value row with columnName "," row
say columnName
end
exit 0
$$
//SYSTSIN DD DSN=&&CSV,DISP=OLD
//SYSTSPRT DD SYSOUT=*
Notes:
In this context, the first line of the REXX exec does not need to contain the string "REXX"
DLM=$$ enables you to use REXX comment syntax (/*) without prematurely ending the in-stream data set
&&CSV refers to a CSV file created by a previous job step (not shown)
I use this "in-stream REXX" technique mostly for ad hoc execs to transform the output of a batch program into what I really want. This technique can be useful for demonstrations and quickly bouncing ideas around; other developers can view the REXX code in situ in the JCL in SDSF output, tweak it, and then submit the tweaked version.
Related
I have an Oracle concurrent request that calls a SQLPlus program. The program itself is working correctly, but I would like to add some logging information to the concurrent request output / log in EBS.
I have tried a number of variations of:
set heading off
--set pagesize 0 embedded on
set pagesize 50000
set linesize 32767
set feedback off
set verify off
set term off
set echo off
set newpage none
set serveroutput on
dbms_output.enable(1000000);
--prepare data
EXECUTE program (&1,&2,&3,&4,&5);
--extract data
#"path/file.SQL";
fnd_file.put_line(FND_FILE.LOG,'do some logging here');
fnd_file.put_line(FND_FILE.OUTPUT,'do some logging here');
/
But everything I've tried so far results with either
no logging added to request output or log
no request output whatsoever
errors like:
SP2-0734: unknown command beginning "dbms_outpu..." - rest of line ignored.
and
PLS-00103: Encountered the symbol "ENABLE" when expecting one of the following: := . ( # % ;
Is it possible to write to the request output or log from a SQLPlus script that is called from concurrent manager?
First of all, your SQL*Plus script does not even run without your attempts at logging.
dbms_output enable(...) is missing a dot ('.').
Your anonymous PL/SQL block has no end; statement
#"path/file.SQL` is a SQL*Plus command -- it cannot be embedded in an anonymous PL/SQL block.
Aside from those basic problems, FND_FILE.PUT_LINE is only for PL/SQL concurrent programs. That is, concurrent programs whose executable points to a PL/SQL package procedure and not a .sql file under $APPL_TOP.
For SQL*Plus concurrent programs, i.e., running a .sql file under $APPL_TOP, FND_FILE.PUT_LINE does not work. Instead, your SQL*Plus output is automatically written to the request output. There is no standard way to write to the request log.
If you really need to write to the request log, you could maybe call FND_FILE.PUT_NAMES to cause FND_FILE.PUT_LINE to write to temporary files that you name. Then, knowing the concurrent request ID and the logic Oracle EBS uses to local output and log files, do a FND_FILE.CLOSE and host command to move the custom-named files you specified to the actual locations. That might work.
It'd be much better to redo your concurrent program as a PL/SQL package. Then FND_FILE works just fine. If you know how to call Java from the database, there is very little you can do in a .sql script that you cannot do in a PL/SQL package.
I have not written a .sql concurrent program in years, and I write concurrent programs all the time.
I have resolved this problem. The solution is incredibly simple - and now I'm bent out of shape because it took so long to realize.
Step 1 - SET ECHO ON
Step 2 - PROMPT whatever you want written to concurrent request output
The following sample writes 'Output is written to this folder' to the concurrent request output.
set heading off
--set pagesize 0 embedded on
set pagesize 50000
set linesize 32767
set feedback off
set verify off
set term off
set echo on
set newpage none
set serveroutput on
prompt Output is written to this folder
--prepare data
EXECUTE program (&1,&2,&3,&4,&5);
--extract data
#"path/file.SQL";
/
This is exactly what I was looking for. Maybe this will be useful to someone in another galaxy.
If this is for testing/debugging purposes, you can specify the location of the log and output files with the routine: FND_FILE.PUT_NAMES and as soon as you log all the required information you need to close the file with: FND_FILE.CLOSE
As Matthew mentioned, logging in SQL*Plus executables doesn't work well. If you can't move your code to a PL/SQL Stored Procedure for some reason, a Host script might work for you instead. From there, you can execute SQL, e.g. sqlplus -s $FCP_LOGIN ... and write log information as required.
If you just need to prepare data by PLSQL and then spool it to CSV via SQL, you can use our company's Blitz Report instead, which does this more convenient and is for free for such use. It also uses a Host type executable and calls sqlplus from there.
I need to create a batch job for searching a data set list; I need to submit the job over FTP.
I am a complete newbie in JCL, so could anyone help me with an example job with SRCHFOR-command?
I need to also create a job for the IDCAMS utility command Listcat
I would be really thankful only for an example job.
Given you indicated you don't know where to start here is a sample job that will run the search in the first step and run IDCAMS to execute a Listcat in the second step.
This link has some useful information on SuperC.
Note: Your jobcard will need to be customized based on your installation's requirements. This job can be submitted via FTP and the subsequent output can be retrieved via FTP as well.
//SRCHCMP JOB (CCCCCCCC),'HOGSTROM',
// MSGLEVEL=(1,1),
// MSGCLASS=O,
// CLASS=A,
// NOTIFY=&SYSUID
//*
//SRCHFOR EXEC PGM=ISRSUPC,PARM=('SRCHCMP,ANYC')
//NEWDD DD DSN=USER1.TEST.CNTL,DISP=SHR
//OUTDD DD SYSOUT=*
//SYSIN DD *
SRCHFOR 'NEWDD'
/*
//*
//IDCAMS EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
LISTCAT ENTRY('USER1.TEST.CNTL') ALL
/*
//
Output from the first step will look like this:
ISRSUPC - MVS/PDF FILE/LINE/WORD/BYTE/SFOR COMPARE UTILITY- ISPF
FOR z/OS 2019/02/26 22.45 PAGE 1 LINE-#
SOURCE SECTION SRCH DSN: USER1.TEST.CNTL
SRCHFOR --------- STRING(S) FOUND
-------------------
8 //NEWDD DD DSN=USER1.TEST.CNTL,DISP=SHR
11 SRCHFOR 'NEWDD'
ISRSUPC - MVS/PDF FILE/LINE/WORD/BYTE/SFOR COMPARE UTILITY- ISPF
FOR z/OS 2019/02/26 22.45 PAGE 2
SEARCH-FOR SUMMARY SECTION SRCH DSN: USER1.TEST.CNTL
LINES-FOUND LINES-PROC MEMBERS-W/LNS MEMBERS-WO/LNS COMPARE-COLS
LONGEST-LINE
2 4436 1 41 1:80 80
PROCESS OPTIONS USED: ANYC
THE FOLLOWING PROCESS STATEMENTS (USING COLUMNS 1:72) WERE PROCESSED:
SRCHFOR 'NEWDD'
Output from the IDCAMS Listcat looks like this:
IDCAMS SYSTEM SERVICES TIME: 22:45:34 02/26/19 PAGE 1
LISTCAT ENTRY('USER1.TEST.CNTL') ALL
NONVSAM ------- USER1.TEST.CNTL
IN-CAT --- CATALOG.T70502
HISTORY
DATASET-OWNER-----(NULL) CREATION--------2017.089
RELEASE----------------2 EXPIRATION------0000.000
ACCOUNT-INFO-----------------------------------(NULL)
SMSDATA
STORAGECLASS -----CLASS2 MANAGEMENTCLASS---(NULL)
DATACLASS --------(NULL) LBACKUP ---0000.000.0000
ENCRYPTIONDATA
DATA SET ENCRYPTION-----(NO)
VOLUMES
VOLSER------------T70502 DEVTYPE------X'3010200F' FSEQN------------------0
ASSOCIATIONS--------(NULL)
ATTRIBUTES
IDCAMS SYSTEM SERVICES TIME: 22:45:34 02/26/19 PAGE 2
THE NUMBER OF ENTRIES PROCESSED WAS:
AIX -------------------0
ALIAS -----------------0
CLUSTER ---------------0
DATA ------------------0
GDG -------------------0
INDEX -----------------0
NONVSAM ---------------1
PAGESPACE -------------0
PATH ------------------0
SPACE -----------------0
USERCATALOG -----------0
TAPELIBRARY -----------0
TAPEVOLUME ------------0
TOTAL -----------------1
THE NUMBER OF PROTECTED ENTRIES SUPPRESSED WAS 0
IDC0001I FUNCTION COMPLETED, HIGHEST CONDITION CODE WAS 0
IDC0002I IDCAMS PROCESSING COMPLETE. MAXIMUM CONDITION CODE WAS 0
Here is a reference to the SuperC utility
Another approach, if you are more familiar with the Unix side of the house, would be to consider doing these things via USS or TSO. For USS (Unix System Services), check out my blog:
https://makingdeveloperslivesbetter.wordpress.com/2019/01/06/mvs-utilities-an-almost-real-world-example/
If nothing else, reading the code for the various utilities (they are mostly just shell scripts) will help you map from Unix concepts to z/OS (MVS) concepts.
I'm trying to run a series of shell commands with Perl6 to the variable $cmd, which look like
databricks jobs run-now --job-id 35 --notebook-params '{"directory": "s3://bucket", "output": "s3://bucket/extension", "sampleID_to_canonical_id_map": "s3://somefile.csv"}'
Splitting the command by everything after notebook-params
my $cmd0 = 'databricks jobs run-now --job-id 35 --notebook-params ';
my $args = "'{\"directory\": \"$in-dir\", \"output\": \"$out-dir\",
\"sampleID_to_canonical_id_map\": \"$map\"}'"; my $run = run $cmd0,
$args, :err, :out;
Fails. No answer given either by Databricks or the shell. Stdout and stderr are empty.
Splitting the entire command by white space
my #cmd = $cmd.split(/\s+/);
my $run = run $cmd, :err, :out
Error: Got unexpected extra arguments ("s3://bucket", "output":
"s3://bucket/extension",
"sampleID_to_canonical_id_map":
"s3://somefile.csv"}'
Submitting the command as a string
my $cmd = "$cmd0\"$in-dir\", \"output\": \"$out-dir\", \"sampleID_to_canonical_id_map\": \"$map\"}'";
again, stdout and stderr are empty. Exit code 1.
this is something about how run can only accept arrays, and not strings (I'm curious why)
If I copy and paste the command that was given to Perl6's run, it works when given from the shell. It doesn't work when given through perl6. This isn't good, because I have to execute this command hundreds of times.
Perhaps Perl6's shell https://docs.perl6.org/routine/shell would be better? I didn't use that, because the manual suggests that run is safer. I want to capture both stdout and stderr inside a Proc class
EDIT: I've gotten this running with shell but have encountered other problems not related to what I originally posted. I'm not sure if this qualifies as being answered then. I just decided to use backticks with perl5. Yes, backticks are deprecated, but they get the job done.
I'm trying to run a series of shell commands
To run shell commands, call the shell routine. It passes the positional argument you provide it, coerced to a single string, to the shell of the system you're running the P6 program on.
For running commands without involving a shell, call the run routine. The first positional argument is coerced to a string and passed to the operating system as the filename of the program you want run. The remaining arguments are concatenated together with a space in between each argument to form a single string that is passed as a command line to the program being run.
my $cmd0 = 'databricks jobs run-now --job-id 35 --notebook-params ';
That's wrong for both shell and run:
shell only accepts one argument and $cmd0 is incomplete.
The first argument for run is a string interpreted by the OS as the filename of a program to be run and $cmd0 isn't a filename.
So in both cases you'll get either no result or nonsense results.
Your other two experiments are also invalid in their own ways as you discovered.
this is something about how run can only accept arrays, and not strings (I'm curious why)
run can accept a single argument. It would be passed to the OS as the name of the program to be run.
It can accept two arguments. The first would be the program name, the second the command line passed to the program.
It can accept three or more arguments. The first would be the program name, the rest would be concatenated to form the command line passed to the program. (There are cases where this is more convenient coding wise than the two argument form.)
run can also accept a single array. The first element would the program name and the rest the command line passed to it. (There are cases where this is more convenient.)
I just decided to use backticks with perl5. Yes, backticks are deprecated, but they get the job done.
Backticks are subject to code injection and shell interpolation attacks and errors. But yes, if they work, they work.
P6 has direct equivalents of most P5 features. This includes backticks. P6 has two variants:
The safer P6 alternative to backticks is qx. The qx quoting construct calls the shell but does not interpolate P6 variables so it has the same sort of level of danger as using shell with a single quoted string.
The qqx variant is the direct equivalent of P5 backticks or using shell with a double quoted string so it suffers from the same security dangers.
Two mistakes:
the simplistic split cuts up the last, single parameter into multiple arguments
you are passing $cmd to run, not #cmd
use strict;
my #cmd = ('/tmp/dummy.sh', '--param1', 'param2 with spaces');
my $run = run #cmd, :err, :out;
print(#cmd ~ "\n");
print("EXIT_CODE:\t" ~ $run.exitcode ~ "\n");
print("STDOUT:\t" ~ $run.out.slurp ~ "\n");
print("STDERR:\t" ~ $run.err.slurp ~ "\n");
output:
$ cat /tmp/dummy.sh
#!/bin/bash
echo "prog: '$0'"
echo "arg1: '$1'"
echo "arg2: '$2'"
exit 0
$ perl6 dummy.pl
/tmp/dummy.sh --param1 param2 with spaces
EXIT_CODE: 0
STDOUT: prog: '/tmp/dummy.sh'
arg1: '--param1'
arg2: 'param2 with spaces'
STDERR:
If you can avoid generating $cmd as single string, I would generate it into #cmd directly. Otherwise you'll have to implement complex split operation that handles quoting.
When I run ANY test I get the same message. Here is an example test:
package require tcltest
namespace import -force ::tcltest::*
test foo-1.1 {save 1 in variable name foo} {} {
set foo 1
} {1}
I get the following output:
WARNING: unknown option -run: should be one of -asidefromdir, -constraints, -debug, -errfile, -file, -limitconstraints, -load, -loadfile, -match, -notfile, -outfile, -preservecore, -relateddir, -singleproc, -skip, -testdir, -tmpdir, or -verbose
I've tried multiple tests and nothing seems to work. Does anyone know how to get this working?
Update #1:
The above error was my fault, it was due to it being run in my script. However if I run the following at a command line I got no output:
[root#server1 ~]$ tcl
tcl>package require tcltest
2.3.3
tcl>namespace import -force ::tcltest::*
tcl>test foo-1.1 {save 1 in variable name foo} {expr 1+1} {2}
tcl>echo [test foo-1.1 {save 1 in variable name foo} {expr 1+1} {2}]
tcl>
How do I get it to output pass or fail?
You don't get any output from the test command itself (as long as the test passes, as in the example: if it fails, the command prints a "contents of test case" / "actual result" / "expected result" summary; see also the remark on configuration below). The test statistics are saved internally: you can use the cleanupTests command to print the Total/Passed/Skipped/Failed numbers (that command also resets the counters and does some cleanup).
(When you run runAllTests, it runs test files in child processes, intercepting the output from each file's cleanupTests and adding them up to a grand total.)
The internal statistics collected during testing is available in AFACT undocumented namespace variables like ::tcltest::numTests. If you want to work with the statistics yourself, you can access them before calling cleanupTests, e.g.
parray ::tcltest::numTests
array set myTestData [array get ::tcltest::numTests]
set passed $::tcltest::numTests(Passed)
Look at the source for tcltest in your library to see what variables are available.
The amount of output from the test command is configurable, and you can get output even when the test passes if you add p / pass to the -verbose option. This option can also let you have less output on failure, etc.
You can also create a command called ::tcltest::ReportToMaster which, if it exists, will be called by cleanupTests with the pertinent data as arguments. Doing so seems to suppress both output of statistics and at least most resetting and cleanup. (I didn't go very far in investigating that method.) Be aware that messing about with this is more likely to create trouble than solve problems, but if you are writing your own testing software based on tcltest you might still want to look at it.
Oh, and please use the newer syntax for the test command. It's more verbose, but you'll thank yourself later on if you get started with it.
Obligatory-but-fairly-useless (in this case) documentation link: tcltest
I am running PS 4.0 and the following command in interaction with a Veritas Netbackup master server on a Unix host via plink:
PS C:\batch> $testtest = c:\batch\plink blah#blersniggity -pw "blurble" "/usr/openv/netbackup/bin/admincmd/nbpemreq -due -date 01/17/2014" | Format-Table -property Status
As you can see, I attempted a "Format-Table" call at the end of this.
The resulting value of the variable ($testtest) is a string that is laid out exactly like the table in the Unix console, with Status, Job Code, Servername, Policy... all that listed in order. But, it populates the variable in Powershell as just that: a vanilla string.
I want to use this in conjunction with a stored procedure on a SQL box, which would be TONS easier if I could format it into a table. How do I use Powershell to tabulate it exactly how it is extracted from the Unix prompt via Plink?
You'll need to parse it and create PS Objects to be able to use the format-* cmdlets. I do enough of it that I wrote this to help:
http://gallery.technet.microsoft.com/scriptcenter/New-PSObjectFromMatches-87d8ce87
You'll need to be able to isolate the data and write a regex to capture the bits you want.