getjobid, cl, and liblist — Oh, My!
With the latest update to BASH, there are 3 new IBM i-specific BASH builtin
functions: liblist
, cl
, and getjobid
. These builtins function nearly
identically to the liblist
, system
, and getjobid
commands that exist in
PASE and/or QSH.
If these commands already exist, why add them to BASH? Let's explore:
$ /QOpenSys/usr/bin/getjobid
Process identifier 1575015 is 073984/QSECOFR/QP0ZSPWT
$ /QOpenSys/usr/bin/getjobid
Process identifier 1575016 is 073985/QSECOFR/QP0ZSPWT
$ /QOpenSys/usr/bin/getjobid
Process identifier 1575017 is 073986/QSECOFR/QP0ZSPWT
Every time I run getjobid, I'm getting the job id of the getjobid
process, not
my shell's jobid. If I want to get my shell's job id, I have to remember to pass
the shell's pid (which is helpfully in the $$
special variable)
/QOpenSys/usr/bin/getjobid $$
Kind of a pain, but once you figure it out it's not sooooo bad.
CL to the Rescue?
Ok, so now let's say I am trying to run a PASE program which is running in to
problems while calling some ILE code. The ILE code is writing messages to the
job log, but the job log isn't being saved due to the log level of the job!
Let's use the system
command to change the log level:
/QOpenSys/usr/bin/system "CHGJOB LOG(4 00 *SECLVL)"
If I run this, I will get no difference in behavior.
$ /QOpenSys/usr/bin/system 'dspjob' | grep -E -A3 'LOG$'
Message logging: LOG
Level . . . . . . . . . . . . . . . . . . : 4
Severity . . . . . . . . . . . . . . . . . : 0
Text . . . . . . . . . . . . . . . . . . . : *NOLIST
What gives? Oh, duh! I forgot to add the -i
flag to run in the current
process. Silly me. Wait, even after adding -i
, it's still not working. womp,
womp
What is going on? Well, if you understand the Unix process model, this does
starts to makes sense: we executed the system
command, causing BASH to start a
new process in which the system
command starts up (and if the -i
flag is
not passed, system
spawns an additional job), calls CHGJOB
, and
immediately exits. It sort of looks like this:
bash
└── system -i ... -> CHGJOB
bash
└── system ...
└── <spawned job> -> CHGJOB
In either case, this is quite pointless (even more so without -i
), as it still
hasn't affected our job. Luckily, the CHGJOB
command supports passing in a
job name, so we can use getjobid
to retrieve it:
/QOpenSys/usr/bin/system "CHGJOB JOB($(/QOpenSys/usr/bin/getjobid -s $$)) LOG(4 00 *SECLVL)"
What a pain in the butt! Of course this only works because CHGJOB
allows you
to specify a job name to affect. Other CL commands are not so lucky, like
ADDLIBLE
or CHGCURLIB
😞...
Bash to the Rescue
Ok, so back to BASH. Because these new functions are builtins and not
commands, they execute in BASH itself, ie. the current job. This means when we
run getjobid
it gives you what you probably expected:
$ getjobid
Process identifier 1574877 is 073846/QSECOFR/QP0ZSPWT
$ getjobid
Process identifier 1574877 is 073846/QSECOFR/QP0ZSPWT
$ getjobid $$
Process identifier 1574877 is 073846/QSECOFR/QP0ZSPWT
In addition, we also have a cl
function, which behaves almost the same as
system
, except it defaults to running in the current job, so no -i
is
needed. With that, we don't even need getjobid
for the above example:
cl "CHGJOB LOG(4 00 *SECLVL)"
And of course with builtins, it's finally possible to implement a liblist
utility to change the library list of PASE jobs:
$ liblist -a kadler
CPC2196: Library KADLER added to library list.
$ liblist -d kadler
CPC2197: Library KADLER removed from library list.
$ liblist -c kadler
CPC2198: Current library changed to KADLER.
$ liblist
QSYS SYS
QSYS2 SYS
QHLPSYS SYS
QUSRSYS SYS
QSHELL PRD
KADLER CUR
QGPL USR
QTEMP USR
QDEVELOP USR
QBLDSYS USR
And because we're using the the
LIBRARY_LIST_INFO
SQL service, we get the schema name for free so I figured why not add a -s
option?
$ liblist -a LONGSCHEM
CPC2196: Library LONGSCHEM added to library list.
$ liblist -s
QSYS SYS QSYS
QSYS2 SYS QSYS2
QHLPSYS SYS QHLPSYS
QUSRSYS SYS QUSRSYS
QSHELL PRD QSHELL
KADLER CUR KADLER
LONGSCHEM USR longschemaname
QGPL USR QGPL
QTEMP USR QTEMP
QDEVELOP USR QDEVELOP
QBLDSYS USR QBLDSYS
Note that for compatibility with QSH liblist, the default output is kept the same.
Finally, ff that wasn't enough, if you ever forget what the command line arguments do, we have much better help text than the PASE or QSH equivalents 😁
$ cl --help
cl: cl [-beEhiIkKnOpqsSv] COMMAND [ARG ...]
Executes a CL command.
Options:
-i Run COMMAND in the current job (default)
-S Run COMMAND in a spawned job
-K Keep all spool files generated by COMMAND and the job log
-k Keep all spool files generated by COMMAND
-n Do not include the message identifier when writing the messages to standard error
-p Only write the messages sent to the program's message queue by COMMAND to standard error
-q Do not write messages generated by COMMAND to standard error
-s Do not write spool files generated by COMMAND to standard output
-v Write the complete command string to standard output before executing it
-b Force binary mode for standard streams used by the CL command
-e Copy PASE for i environment variables to ILE before running COMMAND
-I Force CCSID conversion for standard input used by COMMAND
-O Force CCSID conversion for standard output used by COMMAND
-E Force CCSID conversion for standard error used by COMMAND
Arguments:
COMMAND The CL command to run.
ARG One or more arguments to pass to the CL command
Questions, Caveats, and More
Checking for support
These builtins are available once you install bash 4.4-3. You can check if you
have the support and ensure you're using the builtin by using the type
builtin:
$ type liblist
liblist is a shell builtin
$ type cl
cl is a shell builtin
$ type getjobid
getjobid is a shell builtin
Note, the which
command will give you different results, since which
is an
external command and not a BASH builtin:
$ which getjobid
/QOpenSys/usr/bin/getjobid
How do I use them?
Just call them like you would any other command. It's likely you're already
using BASH builtins and didn't even know it! (Things like echo
, cd
, and even
more used in scripting are also BASH builtins.)
When BASH has a builtin, it takes precedence over any external command found on
the $PATH
:
$ which getjobid
/QOpenSys/usr/bin/getjobid
$ type getjobid
getjobid is a shell builtin
Why 'cl' and not 'system'?
While both getjobid
and liblist
match the names their PASE/QSH counterparts,
we decided not to do the same with cl
— even though it has all the same
options. We did initially have it as system
, but there was concern that too
many existing PASE scripts relied on system
and depending on whether you ran a
script in bash
vs bsh
, ksh
, etc you could get unexpected behavior.
You can always create a BASH alias for system
if you like. Run the following
or add it to one of BASH's startup
files
(eg. ~/.bashrc):
alias system=cl
In addition, because we didn't have to worry about full compatibility with
system
, we could change the default behavior to running in the current job,
which is probably what you want most of the time anyway. 🎉 This means no
-i
is not needed (but supported for compatibility) and also a new
builtin-specific option -S
has been added to force cl
to spawn a new job.
This spawned job will run in a non-multithreaded job and without PASE loaded,
which can be useful for some commands.
How do I run the non-builtin version?
Since builtins have higher precedence over external commands, changing your
$PATH
will have no effect. You need to either fully qualify the command or
disable the shell builtin.
# Fully qualify getjobid
/QOpenSys/usr/bin/getjobid
# Disable getjobid builtin for the current session
enable -n getjobid