Calling QSH utilities from PASE

2 minute read

QSH provides a wonderful utility called db2 which allows you to run SQL queries from within the QSH shell, but what if you’re in a QP2TERM shell or using SSH - what then?

QSH from PASE

Well, PASE provides a qsh utility which allows you to call programs within the QSH environment. Like most shells, QSH provides a -c option, which allows you to pass a string of commands to be executed instead of reading from stdin, so we can call db2 like so:

-bash-4.3$ qsh -c "db2 'select 30 from sysibm.sysdummy1'"

00001      
-----------
         30

  1 RECORD(S) SELECTED.

Notice that we had to quote the commands twice. What happens if we need to use quotes inside our SQL?

-bash-4.3$ qsh -c "db2 \"select message_id, message_timestamp from table(qsys2.joblog_info('*')) x\""

MESSAGE_ID  MESSAGE_TIMESTAMP         
----------- --------------------------
CPC7301     2018-05-29-10.50.07.842411

  1 RECORD(S) SELECTED.

Since you can’t escape inside a single-quoted string, we had to switch to using double quotes around the SQL statement and then had to escape those, since they’re inside a double quoted string already. This is starting to get messy. Now what if we need to use delimited names?

-bash-4.3$ qsh -c "db2 \"select message_id, message_timestamp from table(\\\"QSYS2\\\".\\\"JOBLOG_INFO\\\"('*')) x\""

MESSAGE_ID  MESSAGE_TIMESTAMP         
----------- --------------------------
CPC7301     2018-05-29-10.52.08.632485

  1 RECORD(S) SELECTED.

Ugh! Now we had to double-escape the quoted strings! That’s nearly vomit inducing!

One thing you may not have realized is that the PASE qsh utility has a few tricks up its sleeve. Try this:

-bash-4.3$ ln -s /QOpenSys/usr/bin/qsh /QOpenSys/usr/bin/db2
-bash-4.3$ db2 "select message_id, message_timestamp from table(\"QSYS2\".\"JOBLOG_INFO\"('*')) x"

MESSAGE_ID  MESSAGE_TIMESTAMP         
----------- --------------------------
CPD000D     2018-05-29-10.54.07.331486
CPC7301     2018-05-29-10.54.07.335762

  2 RECORD(S) SELECTED.

Now you can call db2 “directly” and everything just works! No messing with qsh -c and less escaping. But how does it work?

Diving Under the Hood

The qsh PASE command is actually a shell script and it has special code to check what the first argument was. The first argument, being stored at index 0 is the name and path of the program which was run (in shell scripts, this is the $0 variable). By creating a symlink to the same shell script called db2, when it gets called it will think it’s the db2 program instead of qsh. Thus, instead of calling /usr/bin/qsh, the script will call /usr/bin/db2 instead! You can do this with any IBM i program you want, so long as there’s a symlink in /usr/bin (just be aware that for RPG and CL programs, the parameters need to be padded out).

One thing to note is that when you call programs this way instead of using qsh -c, they will run in a multi-threaded job, which may give you issues. If that is a problem, you can always make a copy of the qsh script and remove the -i argument to system on the last line of the script, then symlink to your qsh copy instead. This will cause system to execute in a new job, just as qsh -c would have.

Use it or Lose it

Just remember that the program name is an argument just like any other and you can take advantage of it in your own programs! In shell scripts you can use the special variable $0; in Python use sys.argv[0]; and in C/C++ it’s in argv[0] (in PASE this gives you /path/to/program, while in ILE it gives you LIBRARY/PROGRAM).

Are there any situations you can think of where this would be useful?

Categories: ,

Updated: