My Unix Services shell is yucky – how do I change it?

You can get into Unix Services from TSO OMVS, or you can use SSH to get directly into a Unix Services session (without access to ISPF). I was interested in using SSH. At first glance it works just like a Linux command window.

My first experience was the keys did not behave as I expected, and how can I change this.

Why do I care which shell I am using?

On Linux I can type the first part of a file name, press the tab key, and it either completes the name of the file (if there was only one) or lists the names of the possible matching files. This is known as autocomplete.

I tried this in an SSH session on z/OS, and this did not work. I was using the default shell, which does not have this support. I needed to use the bash shell or the zsh shell. Note the zsh was developed on Unix and ported to z/OS, it is not a z/OS special.

How do I use a different shell?

The first question is, “is the shell available on my system?”, to which the answer is maybe.

  • z/OS ships with the default shell in /bin/sh.
  • You can get a free version of bash for z/OS. My ADCD based system does not have Bash pre-installed. I believe you can get it for free from Rocket.
  • My ADCD system has zsh installed (but not configured).

Your default shell

This is set in the in your RACF OMVS segment PROGRAM field . To display it, use

TSO LU COLIN OMVS

This gave me

OMVS INFORMATION
----------------
UID= 0000990021
HOME= /u/colin
PROGRAM= /bin/sh
...

Once you logon to your shell various profiles may be executed if they exist for example for the default shell

  • /etc/profile and ~/.profile where ~ is your default home directory (see HOME= in the output of the LU command, above;

Typically the profile script will set the environment variable SHELL.
I put the following in my ~/.profile so my SSH sessions get the zsh shell.

#!/bin/sh 

if [[ -z "$SSH_CLIENT" ]]
then
# dummy
xxx="$SSH_CLIENT"
else
# SSH_CLIENT has a value ... so an SSH terminal
#cho "using the zsh shell"
zsh="/bin/zsh"
echo "shell $SHELL zsh $zsh"
if [[ $SHELL != $zsh ]]
then
echo "using the zsh shell"
export SHELL="$zsh"
exec "$zsh"
fi
fi


zsh is documented in the UNIX System Services Command Reference. This is a big book, so I extracted the section of zsh using

pdftk USSCommand_v3r1.pdf cat 899-1071 output zsh.pdf

zsh education

I found https://thevaluable.dev/zsh-completion-guide-examples/ was a good source of information about zsh.

If zsh has been installed properly you should have an environment variable $ZDOTDIR defined. If this is missing then $HOME is used instead.

There are files

  • $ZDOTDIR/.zshenv
  • $ZDOTDIR/.zprofile
  • $ZDOTDIR/.zshrc
  • $ZDOTDIR/.zlogin
  • $ZDOTDIR/.zlogout

Which should contain configuration information

There are comment on the IBM community about the poor documention of zsh on z/OS, an how you can get started. It gives hints on setting up colours etc.

The IBM documentation says

  • Commands are first read from /etc/zshenv; this cannot be overridden. …
  • Commands are then read from $ZDOTDIR/.zshenv.
    • If the shell is a login shell, commands are read from /etc/zprofile and then $ZDOTDIR/.zprofile.
    • Then, if the shell is interactive, commands are read from /etc/zshrc and then $ZDOTDIR/.zshrc.
    • Finally, if the shell is a login shell, /etc/zlogin and $ZDOTDIR/.zlogin are read.
  • When a login shell exits, the files $ZDOTDIR/.zlogout and then /etc/zlogout are read. This happens with either an explicit exit via the exit or logout commands, or an implicit exit by reading end-of-file from the terminal

If you start with the system shell, then invoke the zsf shell, the PATH and LIBPATH statements etc will be inherited from the system shell.
If you go directly to the zsh you’ll need to set up a profile based on the system profile. ( or just invoke the existing profile).

You may want to set up a profile to set the command prompt for example the default is

$LOGNAME:$PWD:

You can set it with

export prompt='%n:%/'

to give

COLIN:/u/colin

Changing what keys do … getting the delete key to work as expected

This took a while to understand see Linux mapping the keyboard and on z/OS SSH.

Installing useful commands

I installed zopen. zopen provides lots of Unix-like packages on z/OS. Its home page says

The zopen community is here to provide popular Open Source tools and to encourage z/OS Open Source tools development. We currently host 200+ z/OS Open Source projects and we’re looking for more!

See installing zopen. I then installed the less command , and openssl. See installing packages.

Linux mapping the keyboard and on z/OS SSH

I wanted to configure the keyboard in zsh and bash on z/OS so the delete key (top right of the keyboard) deleted the current character. (Backspace deletes the previous character) I could not find any good documentation on how to do it.

I found some bash for Linux documentation and have based this blog post on that. I’ve given the command I used, then sections on the background of how it works.

Note the binding of key to action depends on the shell. The shells zsf and bash support binding. shell does/may not.

Having written the original blog post, I found some additional useful information which will make the document clearer.

How to set the delete key to delete

With my Bash shell, I initially had to do it in stages:

  • Type bind “ that’s bind space double quote
  • Control+V, delete key to insert the value of the key. This made it bind “^[[3~
  • Type “:delete-char with no space after the :
  • It now looks like bind “^[[3~”:delete-char
  • Press enter
  • The delete key should delete the character under the cursor

The command bind -p |grep delete gave

"\C-h": backward-delete-char
"\C-?": backward-delete-char
"\C-d": delete-char
"\M-[3~": delete-char
"\\": delete-char
# delete-char-o

Which shows that Ctrl+d and \\ also deletes the current character.

In a shell (in ASCII) this is (in ISPF edit with hex on)

bind '" ∇3~":delete-char' 
6666222153723666676266672222
29E4072BB3E2A45C545D38127000

Where the incantation is x1b5b337e.

What does the mapping mean?

It took me many hours of looking for a good description of what the key mapping is.

The Wikipedia page ANSI escape codes, was very helpful and clear.

  • You can press the “a” key – and “a” is displayed in the command window.
  • You can press shift “a” – and “A” is displayed in the command window
  • You can press Control (Ctrl) “a” – and this may do something
  • You can press Meta “the Windows” key “a” and this may do something
  • You can press Alt + “a” and it may do something.
  • You can press combinations of those keys, such as Ctrl+shift + “a” and this may do something.

The operating system may intercept the key and not pass it to the window. For example on my Linux, Ctrl+Alt+Delete is logout.

In the command window the shell code will intercept a key press combination and take an action, for example move the cursor left, clear the screen, delete word, or just the the character.

You can change the mapping using the bind or bindkeys command for bash and zsf shells.

You can find the code for a key combination by using ctrl+v. For example with the left cursor key

Ctrl+v, < gives ^[[D

How to decode ^[[D ?

There are several formats.

  • <esc>[ number(;modifier)~
  • <esc>[ letters

The string starts with an escape sequence. ^[ This can be written as \e ,\033,  0x1b, or ^[. Where \0 is octal 33, which is hex 1B, (and decimal 27). When specifying a key sequence any of the values can be used. I mentioned above the incantation x1b5b337e.

Because there is no trailing ~ we need to lookup the “[D” in the Wikipedia page section xterm sequences. This has

<esc>[D     - Left 

So for ^[[D we have <esc>, Left key pressed

For ^[[3;5~ we have

  • ^[ is escape
  • [3 isDelete key
  • ; is a modifier
  • 5 is Control + shift
  • ~ end of escape

so the key presses was Control + Delete key (top right on my keyboard). Shift seems to be ignored!

Another popular key is ^M which is Carriage Return – (and process any data on the line) and move the cursor to column 0.

\C is the Ctrl key, \M is the meta key. For keyboards without the Meta key this is displayed as Escape \e.

Keys like \M-OD comes from

Keypad mode
"\M-OD":backward-char
"\M-OC":forward-char
"\M-OA":previous-history
"\M-OB":next-history

This information is not well documented.

What is key mapping

On Linux Ubuntu the control-right arrow combination moves the cursor right one word. The control-left arrow moves the cursor left one word. Control-L clears the screen, but leaves the command line intact.

You can list the possible actions

bind - l

This gave

...
arrow-key-prefix
backward-byte
backward-char
backward-delete-char
backward-kill-line
backward-kill-word
...

Get a list of functions and what keys use them

With upper case -P

bind -P |grep backward

This gave

backward-byte is not bound to any keys
backward-char can be found on "\C-b", "\eOD", "\e[D".
backward-delete-char can be found on "\C-h", "\C-?".

Where does \eOD come from ? See here.

Get a list of keys and their functions in bind format

with lower case -p

bind -p |grep backward

gave

"\eOD": backward-char
"\e[D": backward-char
"\C-h": backward-delete-char
"\C-?": backward-delete-char
...

where

  • Control-h moves the cursor left one character and deletes the character
  • \e is the escape key. The alt key is usually mapped to the escape key by terminal emulators.

This output is slightly untrue. If there is no blank after the : you can use bind….

If you have a blank after the : you need to enclose it in single quotes.

bind ‘”\C-h”: backward-delete-char’

to set the key.

What is the code for a key press combination?

The Linux command Ctrl-V (verbatim insert) followed by a key, gives the mapping for that key.

Ctrl+V LeftArrow
^[[D

Ctrl+V Ctrl+Leftarrrow
^[[1;5D

Where ^[ means the escape key and [D is OD.

^[[3~ is escape Delete key.

Verbatim insert gives the code of the key which was pressed. This works on z/OS if you have the Bash or zsf shell installed.

What keys are mapped?

In the zsh shell you can issue

bindkey -L

(With bash you can use the bind command).

This gives output like

bindkey "^E" end-of-line
...
bindkey "^L" clear-screen

Where ^ is the ctrl key.

If you type (on Linux) man bash , and locate Readline Command Names if lists the function names and what they mean.

The bash command bind -l lists all of the functions

$ bind -q backward-char
backward-char can be invoked via "\C-b", "\M-OD", "\M-[D".

It gets very confusing

A sequence can be created in different formats. For example many commands begin with the Escape (or Meta key). This can be written as \e ,\033,  0x1b, or ^[. Where \0 is octal 33 which is hex 1B, ( or decimal 27). It is confusing when you display information with different commands.

Where does \eOD (or \M-OD)come from?

It was a challenge to find this information.

In the Linux terminfo documentation it says

The codes sent by the left arrow, right arrow, up arrow, down arrow, and home keys can be given as kcub1, kcuf1, kcuu1, kcud1, and khome respectively. If there are function keys such as f0, f1, …, f10, the codes they send can be given as kf0, kf1, …, kf10. If these keys have labels other than the default f0 through f10, the labels can be given as lf0, lf1, …, lf10.

The linux command infocmp

infocmp|grep kcu 

gave

kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,

so this all says the left arrow key maps to backward character. – Phew

Problems

If there was a space after the : I got

readline: delete-char: no key sequence terminator

from the bind command.

Putting the command into my .profile file didn’t work because of the wrong character set.

I could put the command into a shell script, and invoke it – but I could not get it to work from the .profile.