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).
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:
$
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.
$ 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.
ls -la /
Output:
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
echo $HOME
pwd
df -k .
Output:
/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.
df -v /
Output:
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)
| Trait | HFS | zFS |
|---|---|---|
| Underlying dataset | HFS (DSORG=PO-E variant) | VSAM linear (LDS) |
| Online grow | Limited | Yes (zfsadm grow) |
| Online shrink | No | Yes (zfsadm shrink) |
| Multi-volume | No | Yes |
| Sysplex-aware | RWSHARE limited | Yes (RLS) |
| Snapshot/backup | Backup of dataset | Backup of LDS + zFS quiesce |
| Status as of z/OS 2.5+ | Migrating to zFS | Current 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.
ls -T /u/alice
Output:
t IBM-1047 T=on notes.txt
t ISO8859-1 T=on readme.utf8
- untagged T=off binary.dat
| Tag form | Meaning |
|---|---|
t <ccsid> T=on | Tagged as text in the named CCSID, auto-conversion enabled |
t <ccsid> T=off | Tagged but conversion disabled (rare — diagnostic state) |
b <ccsid> | Tagged binary (no conversion regardless of T=) |
- untagged | No tag — programs read raw bytes, what you see depends on the app |
The most common CCSIDs on z/OS USS:
| CCSID | Encoding |
|---|---|
| 1047 | "Latin 1 EBCDIC" — the standard z/OS code page |
| 037 | US English EBCDIC (older default) |
| 819 | ISO-8859-1 (Latin-1 ASCII) |
| 1208 | UTF-8 |
| 1200 | UTF-16 |
Set or change a tag with chtag:
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:
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:
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.
| Command | What it does |
|---|---|
oedit | ISPF editor on a USS file (full ISPF Edit power) |
obrowse | ISPF browser on a USS file |
oget | Copy MVS dataset → USS file |
oput | Copy USS file → MVS dataset |
ocopy | Bidirectional copy with explicit DCB control |
chtag | Get/set file tag (encoding metadata) |
extattr | Get/set extended attributes (program-controlled, APF, sharelib) |
bpxbatch | Run a USS command from JCL |
bpxwdyn | Allocate/free MVS datasets from REXX or shell |
mvscmd / mvscmdauth | Issue MVS commands from the shell |
tso | Run a TSO command from the shell |
submit | Submit JCL to JES2 from the shell (via internal reader) |
cp -F | Copy with format conversion (text↔text, no-tag→tagged) |
mount / unmount | Same as mount on Linux but for HFS/zFS |
df | Filesystem usage — extended df -v shows backing dataset |
ps | Process list — includes BPX (USS) and OMVS address spaces |
kill | Same as POSIX kill; signals delivered to USS processes |
Examples:
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:
| Flavor | Behavior |
|---|---|
text | Convert between EBCDIC (1047 default) and ASCII (819) as required |
binary | Byte-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.
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:
MOUNT FILESYSTEM('OMVS.PROJ.ALICE.ZFS')
MOUNTPOINT('/u/alice/proj')
TYPE(ZFS)
MODE(RDWR)
AUTOMOVE
| Option | Meaning |
|---|---|
MODE(RDWR) / MODE(READ) | Read-write or read-only |
AUTOMOVE / NOAUTOMOVE / UNMOUNT | Sysplex move behavior on member shutdown |
RWSHARE / NORWSHARE | Allow cross-system shared writes (zFS only) |
PARM('AGGRGROW') | zFS-specific tuning |
SECURITY | Apply 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:
# /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.
//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:
//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:
//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.
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:
APF Authorized = NO
Program Controlled = YES
Shared Library = NO
Shared Address Space = NO
| Bit | Meaning |
|---|---|
p | Program-controlled — required for any binary that runs in a program-controlled task chain (most SAP/Db2/CICS exits) |
a | APF-authorized — runs with APF authority (very restricted by RACF FACILITY) |
s | Shared library — loadable as a system shared library |
l | Loadable 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.
LU ALICE OMVS NORACF (* TSO command: list OMVS segment *)
Output:
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):
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:
| Daemon | Address space | Purpose |
|---|---|---|
sshd | OMVS process under INETD or standalone | OpenSSH server |
ftpd | FTPD started task | z/OS FTP server (handles MVS dataset transfers too) |
From a workstation:
ssh alice@myhost
sftp alice@myhost
scp file.dat alice@myhost:/u/alice/inbox/
Output: (standard ssh/sftp interaction)
From USS to another host:
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.
# 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:
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.
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:
/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:
TSO ALU ALICE OMVS(PROGRAM('/usr/lpp/IBM/zoss/bash/bin/bash'))
Then in ~/.bashrc:
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
- Forgetting
_BPXK_AUTOCVT=ON— files look like garbage oncateven thoughchtag -pshows the right encoding. Set it in.profileonce. - Mixing tagged and untagged files in a script — shell redirection writes untagged output by default. Tag the result with
chtag -tc IBM-1047 out.txtor set_BPXK_AUTOTAG=ON. - Editing a binary file with
oeditand saving — silently translates line ends and possibly the encoding. Useobrowsefor read-only, orchtag -bbefore opening. - Permissions on
.sshtoo loose — sshd refuses keys if~/.sshis group- or world-writable. Alwayschmod 700 ~/.ssh; chmod 600 ~/.ssh/*. - Mount without the mount-point existing — the mount fails silently or hides existing content. Always
mkdir -pthe directory first; checklsafter mount. unmountfrom inside the mounted directory —Device busy.cdout first.- Filesystem full but
dfsays there's room — could be inodes (df -tshows file counts) or quota (zfsadm aggrinfoshows aggregate-level limits). ogetof a VB dataset withouttext— appends the 4-byte RDW prefix to each record.textstrips it.BPXBATCHwithPARM=longer than 100 bytes — silently truncated. UseSTDPARMfor anything substantial.- No OMVS segment — user gets
ICH408Iand "not RACF-authorized" trying to log into ssh or run OMVS. Confirm withLU userid OMVS NORACF. - Editing
/etc/profilefrom MVS — easy to leave wrong line endings or wrong encoding. Always edit USS-side files withoeditorvi. - Forgetting
STDENVwhen 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.
//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)
# /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:
+ 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.
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:
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.
cat "//'ALICE.MASTER.SEQ'" | iconv -f IBM-1047 -t UTF-8 | awk -F, '{print $1, $3}'
Output:
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.
# 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:
ssh alice@myhost
cd ~/.ssh
ls -la
chmod 700 .
chmod 600 authorized_keys
chtag -tc ISO8859-1 authorized_keys
Output:
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.
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:
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:
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.
ssh alice@myhost 'tail -F /u/alice/log/deploy.out'
Output:
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).
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:
/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.
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.
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:
-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.
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:
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.