cheat sheet

USS

The UNIX side of z/OS — OMVS, ISHELL, oedit, BPXBATCH, file tagging, zFS, and bridging shell scripts to MVS batch.

USS — z/OS UNIX System Services

What it is

z/OS UNIX System Services (USS, historically known as OpenMVS or OMVS) is the POSIX-compliant UNIX subsystem built into the z/OS kernel itself — not a virtual machine, not a container, but a peer to TSO running on the same address space architecture. From a shell prompt inside USS you get bash-like shells, /bin, /etc, /home/<user>, ssh, perl, python, and the GNU userland, all backed by hierarchical filesystems (HFS or zFS) that live on DASD next to your VSAM clusters. Reach for USS whenever you need UNIX-style tooling on z/OS: shell scripts, ssh-driven automation, IBM-shipped Java/Node/Python, MQ administration, web servers, or any modern build pipeline. The alternative — running the same workload on a Linux LPAR under z/VM or zCX — is heavier but isolates the UNIX side from MVS resources.

Entering USS

There are four common entry points into USS, each with different semantics: OMVS (3270-mode shell from TSO), ISHELL (ISPF-style panel UI for USS), ssh (from any workstation, the modern default), and BPXBATCH (batch JCL that executes a shell command).

text
TSO OMVS                   (* full-screen shell from TSO READY or ISPF option 6 *)
TSO ISHELL                 (* ISPF panel browser of the USS file tree *)
ssh alice@myhost           (* from a Mac/Linux/Windows terminal *)

Output:

text
$
alice@myhost ~ $

OMVS is the most common interactive entry from inside a 3270 session — it presents a line-mode shell where you type commands and press Enter; arrow keys scroll the recent output buffer. Exit with exit or oshell keyword quit. OMVS accepts a few non-standard 3270-friendly commands too: subcmd, tso, console are all USS shell built-ins for crossing back to the MVS side.

text
$ tso 'listc ent(alice.master.ksds) all'      # run a TSO command from the shell
$ mvscmd "D A,L"                              # MVS operator command (if authorized)
$ submit "//ALICEJ01 JOB ..."                 # submit JCL by piping to the internal reader

Output: (TSO/MVS reply, then back to the shell prompt)

ISHELL (also reachable as =6.ISHELL or the ISH command from ISPF) is the panel-driven file browser — useful when you want to set ownership bits on a directory tree without leaving the green screen.

File hierarchy and where things live

USS uses the standard FHS-ish layout (/, /etc, /bin, /usr, /home, /tmp, /var) but mounted from z/OS hierarchical filesystems (HFS or zFS) — DASD-backed file containers, not "real" disks. Each user normally has /u/<userid> or /home/<userid> as a home directory, set in the OMVS segment of their RACF profile.

bash
ls -la /

Output:

text
total 256
drwxr-xr-x  20 root  SYS1     8192 May  1 09:11 .
drwxr-xr-x  20 root  SYS1     8192 May  1 09:11 ..
drwxr-xr-x   7 root  SYS1     8192 Apr 15 14:23 bin
drwxr-xr-x  10 root  SYS1     8192 Apr 15 14:23 etc
drwxr-xr-x   3 root  SYS1     8192 Apr 15 14:23 home
drwxr-xr-x   2 root  SYS1     8192 Apr 15 14:23 tmp
drwxr-xr-x  20 root  SYS1     8192 Apr 15 14:23 usr
drwxr-xr-x   3 root  SYS1     8192 Apr 15 14:23 var
bash
echo $HOME
pwd
df -k .

Output:

text
/u/alice
/u/alice
Mounted on        Filesystem            Avail/Total  Files  Status
/u/alice          (OMVS.USERS.ZFS)      450M/500M    1242   Available

The df output shows the dataset name that backs the filesystem — OMVS.USERS.ZFS is a regular z/OS dataset that happens to be mounted as a POSIX filesystem. You can LISTCAT it from IDCAMS just like any other.

HFS vs zFS

z/OS supports two filesystem types for the UNIX side: HFS (Hierarchical File System, the original 1996 implementation, dataset organization PO with a special DSORG) and zFS (z File System, a VSAM linear dataset, introduced in z/OS 1.2). zFS is the current default — better performance, online resize, multi-volume support — but HFS still exists on older sysplexes.

bash
df -v /

Output:

text
Mounted on   Filesystem        Type   Avail/Total
/            OMVS.ROOT.HFS     HFS    25M/100M
/u           OMVS.USERS.ZFS    ZFS    450M/500M
/var         OMVS.VAR.ZFS      ZFS    50M/200M
/tmp         /TMP              TFS    250M/500M    (temp fs in memory)
TraitHFSzFS
Underlying datasetHFS (DSORG=PO-E variant)VSAM linear (LDS)
Online growLimitedYes (zfsadm grow)
Online shrinkNoYes (zfsadm shrink)
Multi-volumeNoYes
Sysplex-awareRWSHARE limitedYes (RLS)
Snapshot/backupBackup of datasetBackup of LDS + zFS quiesce
Status as of z/OS 2.5+Migrating to zFSCurrent default

Converting an HFS to zFS is a one-shot offline operation with BPXWMIGF.

File tagging and EBCDIC vs ASCII

This is the single most important concept on USS for anyone moving files between mainframe and workstation. Every file on USS has a "tag" describing its encoding, and the shell uses the tag to decide whether to translate when reading. Wrong tag → garbled text.

bash
ls -T /u/alice

Output:

text
t IBM-1047    T=on  notes.txt
t ISO8859-1   T=on  readme.utf8
- untagged    T=off binary.dat
Tag formMeaning
t <ccsid> T=onTagged as text in the named CCSID, auto-conversion enabled
t <ccsid> T=offTagged but conversion disabled (rare — diagnostic state)
b <ccsid>Tagged binary (no conversion regardless of T=)
- untaggedNo tag — programs read raw bytes, what you see depends on the app

The most common CCSIDs on z/OS USS:

CCSIDEncoding
1047"Latin 1 EBCDIC" — the standard z/OS code page
037US English EBCDIC (older default)
819ISO-8859-1 (Latin-1 ASCII)
1208UTF-8
1200UTF-16

Set or change a tag with chtag:

bash
chtag -tc IBM-1047 notes.txt          # text, CCSID 1047, conversion on
chtag -tc UTF-8     readme.md         # text, UTF-8
chtag -b            binary.bin        # mark as binary (no conversion)
chtag -r            mystery.dat       # remove tag entirely
chtag -p            notes.txt         # print just the tag
chtag -R -tc IBM-1047 /u/alice/jcl/   # recursive

Output:

text
t IBM-1047    T=on  notes.txt
b binary      T=off binary.bin
- untagged    T=off mystery.dat

_BPXK_AUTOCVT=ON in the environment turns on per-IO automatic conversion based on tags — this is what lets you cat an ASCII README on an EBCDIC terminal and see it readably. Set it once in .profile:

bash
export _BPXK_AUTOCVT=ON
export _CEE_RUNOPTS="FILETAG(AUTOCVT,AUTOTAG) POSIX(ON)"

Output: (none — exits 0 on success)

Without auto-convert, an ASCII file on an EBCDIC terminal looks like garbage and an EBCDIC file copied to an ASCII workstation looks like garbage. This is the single most common "FTP didn't work right" cause.

z/OS-specific commands

Beyond the standard UNIX userland, USS ships with a set of commands that bridge to MVS. These are the ones you'll never find on Linux.

CommandWhat it does
oeditISPF editor on a USS file (full ISPF Edit power)
obrowseISPF browser on a USS file
ogetCopy MVS dataset → USS file
oputCopy USS file → MVS dataset
ocopyBidirectional copy with explicit DCB control
chtagGet/set file tag (encoding metadata)
extattrGet/set extended attributes (program-controlled, APF, sharelib)
bpxbatchRun a USS command from JCL
bpxwdynAllocate/free MVS datasets from REXX or shell
mvscmd / mvscmdauthIssue MVS commands from the shell
tsoRun a TSO command from the shell
submitSubmit JCL to JES2 from the shell (via internal reader)
cp -FCopy with format conversion (text↔text, no-tag→tagged)
mount / unmountSame as mount on Linux but for HFS/zFS
dfFilesystem usage — extended df -v shows backing dataset
psProcess list — includes BPX (USS) and OMVS address spaces
killSame as POSIX kill; signals delivered to USS processes

Examples:

bash
oedit /u/alice/scripts/deploy.sh           # opens ISPF Edit
obrowse /etc/profile                       # ISPF Browse on a config file
oget "ALICE.JCL.LIB(ALICEJ01)" /u/alice/aliciej01.jcl text
oput /u/alice/aliciej01.jcl "ALICE.JCL.LIB(ALICEJ01)" text
chtag -tc IBM-1047 /u/alice/aliciej01.jcl

Output: (none — files transferred; chtag prints the new tag)

oget and oput understand the text and binary flavors:

FlavorBehavior
textConvert between EBCDIC (1047 default) and ASCII (819) as required
binaryByte-for-byte; no conversion

For more control, cp -F (file format) and cp -O (oversized — for files with embedded NL) let you handle line-end conventions explicitly.

Mounting and the BPXPRMxx parmlib

Filesystems are mounted at boot via the BPXPRMxx member of SYS1.PARMLIB, and dynamically with the MOUNT shell command (or TSO MOUNT). The mount point must exist as a directory before the mount; permissions on the mount point are masked by the mounted filesystem's root permissions.

bash
mount -t ZFS -f OMVS.PROJ.ALICE.ZFS /u/alice/proj
mount -t ZFS -f OMVS.WORK.ZFS /work -o RWSHARE     # cross-system R/W share
mount -r -t ZFS -f OMVS.LIB.ZFS /opt/lib            # read-only
unmount -f /u/alice/proj                            # force unmount
unmount -i /opt/lib                                 # immediate, no flush

Output: (none — mount table updated)

The equivalent in BPXPRMxx:

text
MOUNT FILESYSTEM('OMVS.PROJ.ALICE.ZFS')
      MOUNTPOINT('/u/alice/proj')
      TYPE(ZFS)
      MODE(RDWR)
      AUTOMOVE
OptionMeaning
MODE(RDWR) / MODE(READ)Read-write or read-only
AUTOMOVE / NOAUTOMOVE / UNMOUNTSysplex move behavior on member shutdown
RWSHARE / NORWSHAREAllow cross-system shared writes (zFS only)
PARM('AGGRGROW')zFS-specific tuning
SECURITYApply SAF security on mounted filesystem

For on-demand mounts (e.g. user home directories), the automount facility reads /etc/auto.master and /etc/u.map:

text
# /etc/auto.master
/u   /etc/u.map

# /etc/u.map
name *
type ZFS
filesystem OMVS.USERS.<uc_name>.ZFS
mode rdwr
duration 3600
delay 60

When /u/alice is referenced for the first time after a reboot, the automounter mounts OMVS.USERS.ALICE.ZFS on demand and unmounts it after one hour of idleness.

Running shell scripts from JCL with BPXBATCH

BPXBATCH is a tiny MVS program that takes a USS command line via PARM= or STDPARM, runs it under USS, and returns the exit code as the JCL step return code. This is the bridge from batch z/OS into the UNIX side.

text
//ALICEJ01 JOB (ACCT),'ALICE',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//*
//RUNSH    EXEC PGM=BPXBATCH,
//             PARM='SH /u/alice/scripts/deploy.sh prod'
//STDOUT   DD PATH='/u/alice/log/deploy.out',
//             PATHOPTS=(OWRONLY,OCREAT,OTRUNC),
//             PATHMODE=SIRWXU
//STDERR   DD PATH='/u/alice/log/deploy.err',
//             PATHOPTS=(OWRONLY,OCREAT,OTRUNC),
//             PATHMODE=SIRWXU

Output: (none — the shell script's stdout/stderr are written to the named USS files; step RC = shell exit code)

For longer command lines (PARM is limited to ~100 characters), use STDPARM:

text
//RUNSH    EXEC PGM=BPXBATCH
//STDPARM  DD *
SH /u/alice/scripts/deploy.sh prod --quiet --log /var/log/deploy.log
/*
//STDOUT   DD SYSOUT=*
//STDERR   DD SYSOUT=*

PARM='SH ...' runs the command under the standard /bin/sh (z/OS Bourne); PARM='PGM /u/alice/bin/mybin' runs an executable directly. BPXBATCH runs as the userid that submitted the job by default; use BPXBATSL to run as a different user when authorized.

For batch jobs that need to drive ssh-style remote work, the typical pattern is:

text
//SSHTASK  EXEC PGM=BPXBATCH
//STDPARM  DD *
SH /u/alice/scripts/run-remote.sh
/*
//STDIN    DD DUMMY
//STDOUT   DD SYSOUT=*
//STDERR   DD SYSOUT=*
//STDENV   DD PATH='/u/alice/scripts/deploy.env',
//             PATHOPTS=(ORDONLY)

STDENV is a USS file containing VAR=value lines — they become environment variables in the BPXBATCH shell.

Extended attributes (extattr)

USS files carry "extended attributes" beyond the standard POSIX bits: program-controlled, APF-authorized, shared library, and load-from-shared-library-region. These determine whether a binary can be invoked from authorized callers, and are set with extattr.

bash
extattr +p /u/alice/bin/myauth          # mark as program-controlled
extattr +a /u/alice/bin/myauth          # mark as APF-authorized
extattr +s /u/alice/bin/mylib.so        # mark as shared library
extattr +l /u/alice/bin/mylib.so        # load from shared library region
extattr -p /u/alice/bin/myauth          # remove program-controlled
extattr /u/alice/bin/myauth             # display current extattrs

Output:

text
APF Authorized = NO
Program Controlled = YES
Shared Library = NO
Shared Address Space = NO
BitMeaning
pProgram-controlled — required for any binary that runs in a program-controlled task chain (most SAP/Db2/CICS exits)
aAPF-authorized — runs with APF authority (very restricted by RACF FACILITY)
sShared library — loadable as a system shared library
lLoadable from the shared library region

The BPX.FILEATTR.PROGCTL, BPX.FILEATTR.APF, and BPX.FILEATTR.SHARELIB RACF profiles in the FACILITY class control who can set each bit.

RACF and the OMVS segment

A TSO user needs an "OMVS segment" on their RACF profile to log into USS at all. The segment carries the UID, GID, home directory, and login shell.

text
LU ALICE OMVS NORACF                    (* TSO command: list OMVS segment *)

Output:

text
USER=ALICE

OMVS INFORMATION
----------------
UID=                 1234
HOME=                /u/alice
PROGRAM=             /bin/sh
CPUTIMEMAX=          NONE
ASSIZEMAX=           NONE
FILEPROCMAX=         NONE
PROCUSERMAX=         NONE
THREADSMAX=          NONE
MMAPAREAMAX=         NONE

Common changes (only by a security admin):

text
ALU ALICE OMVS(UID(1234) HOME('/u/alice') PROGRAM('/bin/sh'))
ALU ALICE OMVS(FILEPROCMAX(8192) THREADSMAX(2000))
ALU ALICE OMVS(NOUID)                  (* remove USS access *)

GIDs are similarly attached to RACF groups via the OMVS segment on LG/ALG commands.

Networking — ssh, ftp, the OMVS stack

USS uses the same z/OS TCP/IP stack as everything else — ports, addresses, and routes are configured in PROFILE.TCPIP. Two daemons in particular ship from IBM:

DaemonAddress spacePurpose
sshdOMVS process under INETD or standaloneOpenSSH server
ftpdFTPD started taskz/OS FTP server (handles MVS dataset transfers too)

From a workstation:

bash
ssh alice@myhost
sftp alice@myhost
scp file.dat alice@myhost:/u/alice/inbox/

Output: (standard ssh/sftp interaction)

From USS to another host:

bash
ssh remote.example.com
scp /u/alice/out.dat user@remote.example.com:~/inbox/
curl https://api.example.com/v1/health

Output: (same as on any UNIX)

The first ssh from a user requires generating keys with ssh-keygen and placing the public key in ~/.ssh/authorized_keys on the destination — the .ssh directory must be chmod 700 and the authorized_keys file chmod 600, just like on Linux.

Modern open-source toolchain — OEF and ZOAU

z/OS 3.1 (and later) ships with IBM Open Enterprise Foundation for z/OS (OEF) and IBM Z Open Automation Utilities (ZOAU) as bypassable prerequisites — both are no-cost downloads from IBM and install under USS. Together they give a contemporary UNIX-style toolbox on z/OS without licence-cost surprises.

OEF is a curated bundle of vetted open-source tools rebuilt for z/OS — Git 2.45, Curl 8.7.1, GNU Bash, Vim, Perl, GNU Make, Less, Ncurses. The 7 February 2025 OEF refresh added GNU GPG (encryption / code signing), jq (JSON), and GNU which. Tools land under /usr/lpp/IBM/zoss/<tool>/ (paths vary by release).

ZOAU simplifies bridging UNIX scripts and MVS — it ships UNIX-style commands (dls, dcp, dmv, dgrep, mvscmd, jobs) that operate on datasets and JES jobs, plus a Python module (zoautil_py) that exposes the same operations as importable APIs. ZOAU is what the Red Hat Ansible Certified Content for IBM Z (ibm_zos_core) collection uses under the hood.

bash
# Confirm OEF is on PATH
git --version
curl --version
bash --version
jq --version
gpg --version

# ZOAU UNIX-style dataset tools
dls "ALICE.JCL.LIB"                      # list members
dcp "ALICE.JCL.LIB(ALICEJ01)" /tmp/job.jcl
dmv "ALICE.OLD.DATA" "ALICE.NEW.DATA"
dgrep -i 'COMPUTE-TOTAL' "ALICE.SOURCE(*)"

# ZOAU Python — drive z/OS from a script
python3 -c '
from zoautil_py import datasets, jobs
print(datasets.list_datasets("ALICE.JCL.LIB"))
print(jobs.submit("ALICE.JCL.LIB(ALICEJ01)"))
'

Output:

text
git version 2.45.2
curl 8.7.1 (s390x-ibm-zos)
GNU bash, version 5.2.21
jq-1.7.1
gpg (GnuPG) 2.4.3
ALICEJ01    01.04  2026/04/12  ...
ALICE.NEW.DATA copied successfully
ALICE.SOURCE(LEDGER01):    COMPUTE-TOTAL = PAY-RATE * HOURS-WORKED.
[{'name': 'ALICEJ01', ...}]
JOB04219

Python on z/OS — IBM Open Enterprise SDK for Python

IBM Open Enterprise SDK for Python (Python 3.11/3.12/3.13 on z/OS, no cost) ships as a separate USS install — runs natively (not under Linux on Z) and uses the IBM-1047 codepage for stdin/stdout by default. Pip is included; wheels for numpy, pandas, cffi, ibm_db (Db2), pyzos, and zoautil_py are published by IBM. Combine with ZOAU to script MVS workloads from Python.

bash
which python3
python3 -V
pip3 list --user

# Install a wheel into your home directory
pip3 install --user pandas pyyaml requests

# Verify ZOAU Python integration
python3 -c 'from zoautil_py import datasets; print(datasets.exists("ALICE.JCL.LIB"))'

Output:

text
/usr/lpp/IBM/cyp/v3r12/pyz/bin/python3
Python 3.12.4
pandas        2.2.2
pyyaml        6.0.2
requests      2.32.3
zoautil_py    1.3.4
True

bash interactively under z/OS UNIX

Bash from OEF is a full GNU Bash 5.2 — readline, tab completion, programmable completion, all $() and [[ ]] constructs work as on Linux. Set it as your login shell in the OMVS segment to replace the default /bin/sh:

text
TSO ALU ALICE OMVS(PROGRAM('/usr/lpp/IBM/zoss/bash/bin/bash'))

Then in ~/.bashrc:

bash
export PATH=/usr/lpp/IBM/zoss/git/bin:/usr/lpp/IBM/zoss/curl/bin:/usr/lpp/IBM/zoss/bash/bin:$PATH
export _BPXK_AUTOCVT=ON
export _CEE_RUNOPTS="FILETAG(AUTOCVT,AUTOTAG) POSIX(ON)"
alias ll='ls -laT'

Output: (none — .bashrc is sourced on every interactive shell start; verify with echo $PATH).

Common pitfalls

  1. Forgetting _BPXK_AUTOCVT=ON — files look like garbage on cat even though chtag -p shows the right encoding. Set it in .profile once.
  2. Mixing tagged and untagged files in a script — shell redirection writes untagged output by default. Tag the result with chtag -tc IBM-1047 out.txt or set _BPXK_AUTOTAG=ON.
  3. Editing a binary file with oedit and saving — silently translates line ends and possibly the encoding. Use obrowse for read-only, or chtag -b before opening.
  4. Permissions on .ssh too loose — sshd refuses keys if ~/.ssh is group- or world-writable. Always chmod 700 ~/.ssh; chmod 600 ~/.ssh/*.
  5. Mount without the mount-point existing — the mount fails silently or hides existing content. Always mkdir -p the directory first; check ls after mount.
  6. unmount from inside the mounted directoryDevice busy. cd out first.
  7. Filesystem full but df says there's room — could be inodes (df -t shows file counts) or quota (zfsadm aggrinfo shows aggregate-level limits).
  8. oget of a VB dataset without text — appends the 4-byte RDW prefix to each record. text strips it.
  9. BPXBATCH with PARM= longer than 100 bytes — silently truncated. Use STDPARM for anything substantial.
  10. No OMVS segment — user gets ICH408I and "not RACF-authorized" trying to log into ssh or run OMVS. Confirm with LU userid OMVS NORACF.
  11. Editing /etc/profile from MVS — easy to leave wrong line endings or wrong encoding. Always edit USS-side files with oedit or vi.
  12. Forgetting STDENV when BPXBATCH needs env vars — the shell inherits a near-empty environment from JCL. Set everything you need explicitly.

Real-world recipes

Invoke a shell script from a batch JCL job

The classic bridge: a nightly batch job runs a USS shell pipeline (curl, jq, awk) and reports a return code.

text
//ALICEJ01 JOB (ACCT),'ALICE',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//*
//STAGE    EXEC PGM=BPXBATCH
//STDPARM  DD *
SH /u/alice/scripts/fetch-and-load.sh
/*
//STDIN    DD DUMMY
//STDOUT   DD PATH='/u/alice/log/fetch.out',
//             PATHOPTS=(OWRONLY,OCREAT,OTRUNC),
//             PATHMODE=SIRWXU
//STDERR   DD PATH='/u/alice/log/fetch.err',
//             PATHOPTS=(OWRONLY,OCREAT,OTRUNC),
//             PATHMODE=SIRWXU
//STDENV   DD PATH='/u/alice/scripts/fetch.env',
//             PATHOPTS=(ORDONLY)
bash
# /u/alice/scripts/fetch-and-load.sh
set -e
export PATH=/bin:/usr/bin:/usr/local/bin

curl -sSf https://api.example.com/v1/feed.json > /u/alice/work/feed.json
chtag -tc UTF-8 /u/alice/work/feed.json
jq -r '.records[] | [.id, .name, .amount] | @csv' /u/alice/work/feed.json \
   > /u/alice/work/feed.csv
chtag -tc IBM-1047 /u/alice/work/feed.csv

# Hand the CSV off to an MVS dataset for the next job step
cp -F text /u/alice/work/feed.csv "//'ALICE.WORK.FEED.CSV'"

Output:

text
+ curl -sSf https://api.example.com/v1/feed.json
+ jq -r ...
+ cp -F text /u/alice/work/feed.csv //'ALICE.WORK.FEED.CSV'

The MVS dataset ALICE.WORK.FEED.CSV is now usable by the next step's COBOL or DFSORT.

Submit JCL from USS

You're inside a shell pipeline and need to launch an MVS batch job. The submit shell command (or cat ... > //INTRDR) does the trick.

bash
cat > /u/alice/work/ad-hoc.jcl <<'EOF'
//ALICEJ02 JOB (ACCT),'ALICE',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP01   EXEC PGM=IEFBR14
//DD1      DD DSN=ALICE.MARKER,DISP=(NEW,CATLG,DELETE),
//             SPACE=(TRK,1),DCB=(RECFM=FB,LRECL=80)
EOF

chtag -tc IBM-1047 /u/alice/work/ad-hoc.jcl
submit /u/alice/work/ad-hoc.jcl

Output:

text
JOB ALICEJ02(JOB12347) SUBMITTED

submit reads the file, tags-aware, and dispatches it through the internal reader. It returns the JES2 job ID on success. The chtag step ensures the JCL is delivered in EBCDIC even if you authored it on a workstation.

Pipe MVS dataset contents into a UNIX tool

Reverse direction: stream a sequential dataset's contents into awk without copying it to USS first.

bash
cat "//'ALICE.MASTER.SEQ'" | iconv -f IBM-1047 -t UTF-8 | awk -F, '{print $1, $3}'

Output:

text
1001 49.99
1002 24.50
1003 99.95

The //'DSN' shell idiom is cat-able directly; iconv translates from EBCDIC to UTF-8 so awk's regex matches the text correctly. Without iconv, awk operates on EBCDIC bytes and the comma split fails because commas in EBCDIC are X'6B', not X'2C'.

Set up ssh key login for a service account

A workstation needs to push files to USS unattended.

bash
# On the workstation
ssh-keygen -t ed25519 -f ~/.ssh/zos_alice
ssh-copy-id -i ~/.ssh/zos_alice.pub alice@myhost

Output: (none — keys generated and public key appended to remote ~/.ssh/authorized_keys)

Then verify on USS:

bash
ssh alice@myhost
cd ~/.ssh
ls -la
chmod 700 .
chmod 600 authorized_keys
chtag -tc ISO8859-1 authorized_keys

Output:

text
total 8
drwx------ 2 alice STAFF 8192 May 24 09:11 .
drwxr-xr-x 8 alice STAFF 8192 May 24 09:10 ..
-rw------- 1 alice STAFF  582 May 24 09:11 authorized_keys

The chtag is critical — authorized_keys is consumed by sshd in ASCII (ISO-8859-1) regardless of system default. Wrong tag → key not matched → silent password fallback or rejection.

Mount a project filesystem and set ownership

A new project needs its own zFS aggregate with specific UID/GID ownership.

text
TSO> ALLOC DA('OMVS.PROJ.ALPHA.ZFS') NEW CATALOG CYLINDERS(50 10) +
       DSORG(PS) RECFM(U) LRECL(0) BLKSIZE(0) DSNTYPE(BASIC)
TSO> IDCAMS  DEFINE CLUSTER (NAME(OMVS.PROJ.ALPHA.ZFS) LINEAR -
       CYL(50 10) SHAREOPTIONS(2 3))

Then format and mount from USS:

bash
zfsadm format -aggregate OMVS.PROJ.ALPHA.ZFS
mkdir /u/alice/proj-alpha
mount -t ZFS -f OMVS.PROJ.ALPHA.ZFS /u/alice/proj-alpha
chown -R alice:STAFF /u/alice/proj-alpha
chmod 750 /u/alice/proj-alpha
df -v /u/alice/proj-alpha

Output:

text
Mounted on              Filesystem            Avail/Total
/u/alice/proj-alpha     OMVS.PROJ.ALPHA.ZFS   720M/750M

Add the equivalent MOUNT statement to BPXPRMxx so the mount persists across IPLs.

Tail a job log live from USS

A USS log file is being appended by an active batch job. Tail it from anywhere on the network.

bash
ssh alice@myhost 'tail -F /u/alice/log/deploy.out'

Output:

text
2026-05-25 09:10:14 stage 1/4 done
2026-05-25 09:10:29 stage 2/4 done
2026-05-25 09:10:51 stage 3/4 done
2026-05-25 09:11:08 stage 4/4 done   exit=0

The -F flavor of tail reopens the file on rotation. If the log lives in an MVS dataset, swap in tso 'oedit' or download via FTP — there is no streaming tail of an MVS sequential dataset.

Run a Python or Node program from USS

Both shipped by IBM for z/OS — Java even longer. After installation they live under /usr/lpp/IBM/cyp/v3r12/pyz/bin/python3 or /usr/lpp/IBM/cnj/IBM/node-latest/bin/node (paths vary by release).

bash
which python3
python3 --version
python3 -c 'import sys; print(sys.platform, sys.version_info)'

cat > /u/alice/scripts/hello.py <<'EOF'
print("hello from USS")
EOF
chtag -tc IBM-1047 /u/alice/scripts/hello.py
python3 /u/alice/scripts/hello.py

Output:

text
/usr/lpp/IBM/cyp/v3r12/pyz/bin/python3
Python 3.12.4
zos sys.version_info(major=3, minor=12, micro=4, releaselevel='final', serial=0)
hello from USS

Python's standard library is fully available; pip installs into your home directory (pip install --user pkg). For Db2 and IMS access there are dedicated IBM-shipped wheels.

Convert and ship a USS file to an MVS dataset

A pipeline produced a UTF-8 file and the next batch step expects EBCDIC fixed-block 80.

bash
cp -F text /u/alice/work/report.csv "//'ALICE.WORK.REPORT.CSV'"

Output: (none — file copied, lines fitted into 80-byte records with EBCDIC translation)

cp -F text infers source tag from the file (UTF-8 here) and converts to IBM-1047 on the way to the MVS side. The destination dataset must already exist with appropriate DCB; otherwise the copy fails with "file not found". For variable-length output, use cp -F binary and an MVS DCB of RECFM=VB,LRECL=259.

Diagnose "permission denied" on a USS file

The shell rejects access but the bits look fine. Check ACLs, file tag, mount mode, and extended attributes.

bash
ls -laT /u/alice/private/secret.dat
getfacl /u/alice/private/secret.dat
extattr /u/alice/private/secret.dat
df -v /u/alice/private/

Output:

text
-rw-r----- 1 alice  STAFF 1024 May 24 09:11 secret.dat
- untagged    T=off
#file: /u/alice/private/secret.dat
#owner: alice
#group: STAFF
user::rw-
group::r--
other::---

APF Authorized = NO
Program Controlled = NO

Mounted on        Filesystem            Mode
/u                OMVS.USERS.ZFS        RDWR

The four layers to check in order: standard mode bits, ACLs (getfacl), file tag (a tagged file refused by an untagged-only app), mount mode (RO filesystem prevents writes regardless of mode bits), and extended attributes (program-controlled tools refuse files without +p).

One-liner: count records in every dataset under a HLQ

tso from USS is the underrated bridge for ad-hoc queries.

bash
for ds in $(tso 'listc lvl(alice.master) names' \
              | awk '/CLUSTER/ {print $3}'); do
  count=$(tso "listc ent('$ds.data') all" \
            | awk '/REC-TOTAL/ {print $2}')
  echo "$ds  $count"
done

Output:

text
ALICE.MASTER.KSDS    5000
ALICE.MASTER.BACKUP  4982
ALICE.MASTER.WORK    102

This is the kind of stitch-the-two-worlds-together task USS makes simple — the listcat output is EBCDIC text, awk handles it, the loop drives more tso calls, the result lands on stdout for piping further.

Sources