The Solaris PATH Problem: /usr/bin vs /usr/xpg4/bin vs /usr/sfw/bin vs /usr/gnu/bin

Solaris Admin Series, part 8. Previous: Solaris Man Pages: Sections, man -k/-K, catman and the Oracle Documentation Library. Related: part 3, Solaris Filesystem Layout Tour, and part 6, Shells on Solaris 10 and 11. Next up: text editors on a bare system, vi essentials and ed when nothing else works.

Sooner or later every admin coming from Linux runs grep -q, id -u or sed -i on a Solaris box and gets an error, or worse, silently different behavior. The reason is that Solaris ships several versions of the same commands in different directories: the traditional Solaris (System V) tools in /usr/bin, POSIX/XPG-conforming versions in /usr/xpg4/bin and /usr/xpg6/bin, BSD versions in /usr/ucb, bundled freeware in /usr/sfw/bin on Solaris 10, and GNU versions in /usr/gnu/bin on Solaris 11. Which one runs depends entirely on the order of your PATH. This post explains what lives where, how to see which binary you are really getting, and how to set PATH for interactive users, root, cron and SMF so scripts behave the same everywhere.

Applies to: both Solaris 10 and Solaris 11 (11.4), SPARC and x86. The headline differences: Solaris 10 has /usr/sfw/bin (GNU and open-source extras such as gtar, ggrep, gmake, wget) and real tools in /usr/ccs/bin; Solaris 11 drops /usr/sfw, delivers GNU tools as IPS packages into /usr/gnu/bin (with g-prefixed names in /usr/bin), moves most of /usr/ccs/bin into /usr/bin, and modernizes many /usr/bin commands, so the gaps are smaller but not gone.

Prerequisites

  • A shell on a Solaris 10 or Solaris 11.4 system (global zone or non-global zone).
  • Root or a role with the Software Installation profile only if you want to add packages (for example the GNU tools on a minimal Solaris 11 install).
  • Basic familiarity with ksh or bash; see part 6 for which shell you are actually running.

The directories and what is in them

DirectoryWhat it holdsSolaris 10Solaris 11.4
/usr/binTraditional Solaris (SVR4-derived) commands; the default behaviorYes; many tools pre-POSIX (old awk, grep without -q/-E, id without -u)Yes; many tools updated, plus g-prefixed GNU links (gtar, ggrep, gsed, gmake)
/usr/xpg4/binPOSIX.2 / XPG4 (SUSv1/v2) conforming versionsYes, dozens of commands (grep, awk, sed, id, tr, sh, …)Yes, kept for standards conformance
/usr/xpg6/binSUSv3 (POSIX.1-2001) versions where they differ from XPG4Yes, a handful of commandsYes, a handful of commands
/usr/ucbBSD-compatible commands (ps, ls, echo, ln, …)Yes (SUNWscpu)Optional package compatibility/ucb
/usr/ccs/binC Compilation System: make, ar, ld, as, lex, yacc, dumpYes, real binaries; not in default PATHMostly compatibility links; the tools live in /usr/bin
/usr/sfw/binSun Freeware companion bundle: gtar, ggrep, gmake, wget, openssl, gcc 3.4.3YesGone
/usr/gnu/binGNU versions under their plain names (grep, sed, tar, make, ls, find, …)NoYes, from packages such as text/gnu-grep, text/gnu-sed, archiver/gnu-tar, file/gnu-coreutils, file/gnu-findutils
/usr/sbin, /sbinSystem administration commandsYesYes
/opt/csw/bin, /opt/sfw/binThird-party packages (OpenCSW, Companion CD)Common add-onRare; prefer IPS packages

Remember from part 3 that /bin is a symlink to usr/bin on both releases, so /bin/grep and /usr/bin/grep are the same file.

Step 1: See your current PATH and what it resolves to

$ echo $PATH
/usr/bin:/usr/sbin

$ type grep awk sed tar
grep is /usr/bin/grep
awk is /usr/bin/awk
sed is /usr/bin/sed
tar is /usr/bin/tar

$ whence -v id            # ksh / ksh93
id is /usr/bin/id

$ type -a grep            # bash: every match in PATH order
grep is /usr/bin/grep
grep is /usr/xpg4/bin/grep
grep is /usr/gnu/bin/grep

Use type (all shells) or whence -v (ksh) rather than which. On Solaris, /usr/bin/which is a csh script that reads your ~/.cshrc, so it can report a different answer from the one your ksh or bash will actually run.

Step 2: See all the versions of a command on the system

$ ls -l /usr/bin/grep /usr/xpg4/bin/grep /usr/gnu/bin/grep /usr/sfw/bin/ggrep 2>/dev/null

# Which package delivers each one?
# Solaris 10 (SVR4):
# pkgchk -l -p /usr/xpg4/bin/grep
# pkgchk -l -p /usr/sfw/bin/ggrep

# Solaris 11 (IPS):
$ pkg search -l /usr/gnu/bin/grep
$ pkg contents -t link -o path,target -a path=usr/bin/ggrep text/gnu-grep

The first column of output tells you which directories exist on this box. On a minimal Solaris 11.4 install (solaris-minimal-server or solaris-small-server) the GNU tools may not be installed at all; pkg install text/gnu-grep text/gnu-sed archiver/gnu-tar file/gnu-coreutils file/gnu-findutils adds them.

Step 3: Know the classic behavior differences

These are the ones that break Linux-written scripts most often. They are worst on Solaris 10; on 11.4 several /usr/bin commands already accept the POSIX options, but check the man page for your SRU instead of assuming.

TaskSolaris 10 /usr/binPortable fix
Quiet match: grep -qNot supported/usr/xpg4/bin/grep -q, or grep pattern file >/dev/null
Extended regex: grep -ENot supported/usr/bin/egrep or /usr/xpg4/bin/grep -E
Numeric user ID: id -uNot supported (prints usage)/usr/xpg4/bin/id -u
awk with functions, -v, getline var/usr/bin/awk is the old 1977 awknawk or /usr/xpg4/bin/awk
In-place edit: sed -iNot supported (no Solaris sed has it)Write to a temp file and mv, use perl -pi -e, or gsed -i on 11
Compressed tar: tar xzfNo z flag on Solaris 10gzip -dc f.tgz | tar xf -, or gtar xzf
Epoch seconds: date +%sNot supportednawk 'BEGIN{srand(); print srand()}' or perl -e 'print time'
Character ranges: tr a-z A-ZTreats - literally unless bracketedtr '[a-z]' '[A-Z]' or /usr/xpg4/bin/tr
Full command lines in psps -ef truncates args at 80 chars/usr/ucb/ps -auxww or pargs PID
$(...) and test -e in /bin/shBourne shell rejects themUse #!/bin/ksh or #!/usr/xpg4/bin/sh (see part 6)
# Solaris 10 examples
$ /usr/bin/id -u
id: illegal option -- u
Usage: id [-ap] [user]

$ /usr/xpg4/bin/id -u
0

$ echo "a b" | /usr/bin/grep -q a
grep: illegal option -- q
Usage: grep -hblcnsviw pattern file . . .

$ echo "a b" | /usr/xpg4/bin/grep -q a && echo found
found

Step 4: Choose a PATH order on purpose

There is no single right order, only a deliberate one. Pick based on what the account is for:

GoalSuggested PATHNotes
Native Solaris behavior (most admin accounts)/usr/bin:/usr/sbin (+ /usr/sfw/bin at the end on S10)What Oracle documentation and Solaris scripts assume. GNU tools reachable by g name.
POSIX/SUS conformance (portable scripts)/usr/xpg6/bin:/usr/xpg4/bin:/usr/bin:/usr/sbin (+ /usr/ccs/bin on S10)The order standards(5) on S10 / standards(7) on 11.4 recommends for SUSv3 applications. getconf PATH prints the system’s conforming PATH.
Linux-like interactive feel (Solaris 11 only)/usr/gnu/bin:/usr/bin:/usr/sbinComfortable for people, risky for scripts: anything that parses ls, ps or df output may change.
$ getconf PATH
# prints the PATH that finds the standards-conforming utilities on this release

Whatever you choose, keep /usr/gnu/bin out of root’s PATH, or at least after /usr/bin and /usr/sbin. Oracle’s own tools and SMF method scripts expect Solaris behavior, and a GNU tar or df in front of them is a classic source of odd failures.

Step 5: Set PATH in the right places

Interactive users

# ~/.profile (ksh, sh) or ~/.bash_profile (bash)
PATH=/usr/bin:/usr/sbin:/usr/sfw/bin      # Solaris 10
PATH=/usr/bin:/usr/sbin                   # Solaris 11.4
export PATH

Neither release sets PATH in /etc/profile by default. New-user templates come from /etc/skel, so put your site default there before creating accounts. On Solaris 11, check what the installer wrote into the first user’s and root’s dotfiles; do not assume, just run echo $PATH after logging in.

Login and su defaults

When a user has no PATH in their dotfiles, login and su fall back to /etc/default/login and /etc/default/su. Both files use PATH for ordinary users and SUPATH for root:

# grep -E '^#?(PATH|SUPATH)=' /etc/default/login /etc/default/su
/etc/default/login:#PATH=/usr/bin:
/etc/default/login:#SUPATH=/usr/sbin:/usr/bin
/etc/default/su:#PATH=/usr/bin:
/etc/default/su:#SUPATH=/usr/sbin:/usr/bin

# Uncomment and edit to change the system default, for example:
PATH=/usr/bin:/usr/sbin
SUPATH=/usr/sbin:/usr/bin

su - builds a fresh environment and uses SUPATH before reading root’s profile; plain su keeps your current PATH, which is a common reason “the same command” behaves differently as root. On Solaris 11, root is a role by default, so this is the path you hit every time you su.

cron jobs

cron does not read your profile. It sets a minimal environment from /etc/default/cron (PATH for users, SUPATH for root, defaulting to roughly /usr/bin and /usr/sbin:/usr/bin). Either set PATH at the top of every script, or use full paths:

#!/bin/ksh
PATH=/usr/bin:/usr/sbin; export PATH

SMF method scripts

SMF starts methods with a small, clean environment. Set PATH inside the method script, or add it to the service’s method environment:

# svccfg -s svc:/site/myapp:default setenv -m start PATH /usr/bin:/usr/sbin
# svcadm refresh svc:/site/myapp:default
# svcprop -p start/environment svc:/site/myapp:default

sudo

If you use sudo (bundled on Solaris 11, an add-on on Solaris 10), it replaces PATH with its secure_path setting when one is configured. Check with sudo -V as root, or simply sudo sh -c 'echo $PATH'.

Step 6: Make scripts immune to PATH

The most reliable fix is not to depend on the caller’s PATH at all:

  • Set PATH explicitly as the first line after the shebang in every production script.
  • Call the specific version you need when it matters: /usr/xpg4/bin/grep -q, /usr/bin/nawk, /usr/gnu/bin/sed -i.
  • On scripts that must run on both releases, test once and branch: case $(uname -r) in 5.10) AWK=nawk ;; *) AWK=/usr/bin/awk ;; esac.
  • Prefer POSIX options that work in /usr/xpg4/bin on both releases over GNU-only options.
#!/bin/ksh
# portable-check.sh - behaves the same on Solaris 10 and 11.4
PATH=/usr/xpg4/bin:/usr/bin:/usr/sbin
export PATH

if id -u | grep -q '^0$'; then
    echo "running as root"
fi

awk -v host="$(uname -n)" 'BEGIN { printf "host=%s\n", host }'

Verification

$ echo $PATH
$ for c in grep awk sed tar id make; do type $c; done
$ su - root -c 'echo $PATH'      # what root gets after su -
$ crontab -l                     # confirm scripts set their own PATH
$ svcprop -p start/environment svc:/site/myapp:default

Run the portable script above as an ordinary user, as root and from cron; it should print the same thing in all three contexts.

Troubleshooting and gotchas

  • “illegal option” errors almost always mean you got /usr/bin where the script expected XPG4 or GNU behavior. Run type cmd in the failing context.
  • Works interactively, fails in cron. cron’s PATH is not yours. Log echo $PATH from the job, then fix the script.
  • A trailing colon or empty entry in PATH means the current directory. The commented default PATH=/usr/bin: in /etc/default/login includes it. Never put . or an empty entry in root’s PATH.
  • GNU first in PATH changes output. GNU ls, df and ps format differently; monitoring scripts that parse columns can quietly break.
  • Same command name, different package on 11.4. /usr/bin/gtar and /usr/gnu/bin/tar are the same GNU tar; /usr/bin/tar is Solaris tar, which understands Solaris ACLs and extended attributes (tar -p, -@) differently. Use Solaris tar or cpio when you need Solaris ACLs preserved.
  • Missing /usr/sfw/bin after migrating a script from 10 to 11. Replace /usr/sfw/bin/gtar with /usr/bin/gtar, /usr/sfw/bin/wget with /usr/bin/wget, and so on.
  • Make on Solaris 10. make lives in /usr/ccs/bin, which is not in the default PATH; add it, or use /usr/sfw/bin/gmake for GNU makefiles.
  • Non-global zones. Each zone has its own dotfiles and /etc/default files; fixing PATH in the global zone changes nothing inside zones. On Solaris 10 sparse-root zones, /usr is shared, so the same binaries exist but PATH is still per zone.

What’s next in the Solaris Admin Series

Next we cover text editors on a bare system: the vi essentials every Solaris admin needs, and ed for when the terminal type is broken or you are in single-user mode. Catch up with part 7, Solaris Man Pages, part 6, Shells on Solaris 10 and 11, and part 5, Solaris First-Day Commands.

Official references: standards(7), filesystem(7), login(1), su(8), cron(8) and pkg(1) in the Oracle Solaris 11.4 Reference Library, and standards(5), su(1M) and crontab(1) in the Oracle Solaris 10 1/13 Information Library.

Leave a Reply

Your email address will not be published. Required fields are marked *