Shells on Solaris 10 and 11: sh, ksh88, ksh93, bash, csh and Setting the Default Shell

Solaris Admin Series, part 6. Previous: Solaris First-Day Commands: uname, /etc/release, showrev, isainfo and isalist. Related: part 3, Solaris Filesystem Layout Tour, and part 1, Solaris 10 and 11.4 Releases, Support Lifecycle and Which to Run. Next up: Solaris man pages, man sections, catman and the Oracle documentation library.

Nothing trips up an admin moving between Solaris 10 and Solaris 11 faster than the shell. A script that runs fine on one breaks on the other, root logs in to a different shell on each release, and /bin/sh is not even the same program. This post maps out which shells ship with each release, where they live, which one each account gets by default, how to change a user’s login shell safely, and what to watch for when you write scripts that must run on both.

Applies to: both Solaris 10 and Solaris 11 (11.4), SPARC and x86. The headline difference: on Solaris 10, /bin/sh is the old Bourne shell and /usr/bin/ksh is ksh88. On Solaris 11, both /bin/sh and /usr/bin/ksh are ksh93, and new users (and root) get bash by default.

Prerequisites

  • A login on the system. Looking at shells needs no privileges; changing another user’s shell needs root, or on Solaris 11 the User Management rights profile (for example via pfexec or sudo).
  • Sample output is illustrative. Version strings depend on your update or SRU.
  • If you have not yet identified the host, start with part 5, Solaris First-Day Commands: uname, /etc/release, showrev, isainfo and isalist, so you know whether you are on 5.10 or 5.11.

Which shells ship with Solaris 10 and 11

ShellSolaris 10Solaris 11.4Notes
Bourne shell (legacy)/bin/sh, /sbin/shRemoved; /bin/sh is ksh93No $( ), no export VAR=value, no test -e
POSIX shell/usr/xpg4/bin/sh (ksh88 based)/bin/sh, /usr/xpg4/bin/sh (ksh93)Use for portable POSIX scripts on Solaris 10
Korn shell 88/usr/bin/kshRemoved in 11.4 (was /usr/sunos/bin/ksh in 11.0 to 11.3)The ksh most Solaris 10 scripts were written for
Korn shell 93/usr/dt/bin/dtksh only (CDE)/usr/bin/ksh, /usr/bin/ksh93Floating point math, associative arrays, [[ ]]
bash/usr/bin/bash (3.x)/usr/bin/bash (newer 4.x/5.x by SRU)Default for new users and root on Solaris 11
C shell/usr/bin/csh/usr/bin/cshAvoid for scripts
tcsh/usr/bin/tcsh/usr/bin/tcsh (package shell/tcsh)Interactive use only
zsh/usr/bin/zsh/usr/bin/zsh (package shell/zsh)May need installing on Solaris 11
Restricted shells/usr/lib/rsh, /usr/bin/rksh/usr/bin/rksh, /usr/bin/rbash where installedNot remote shell; that is /usr/bin/rsh

Package names and exact paths can vary with your software group and SRU, so confirm on your own host before relying on them:

# Solaris 10: which package delivers a shell?
$ pkgchk -l -p /usr/bin/bash | grep -i package
Referenced by the following packages:
        SUNWbash

# Solaris 11.4: which IPS package delivers it?
$ pkg search -l -o pkg.name /usr/bin/bash
PKG.NAME
shell/bash

# What is /bin/sh really?
$ ls -l /usr/bin/sh                     # Solaris 11.4
lrwxrwxrwx   1 root     root           5 Sep 12 10:02 /usr/bin/sh -> ksh93

Default login shells

AccountSolaris 10Solaris 11.4
root/sbin/sh (statically linked Bourne shell)/usr/bin/bash (root is a role)
New users from useradd/bin/sh unless -s is given/usr/bin/bash
Empty shell field in /etc/passwd/bin/sh/bin/sh (ksh93)

Find out which shell you are running

$SHELL tells you the login shell from the password entry, not necessarily the one you are typing into right now. Check both:

$ echo $SHELL
/usr/bin/bash
$ ps -p $$ -o pid,comm
  PID COMMAND
 4127 -bash
$ echo $0
-bash

# For ksh93, the version is in a variable (Solaris 11, or dtksh on 10)
$ ksh -c 'echo ${.sh.version}'
Version AJM 93u+ 2012-08-01

# bash
$ bash --version | head -1
GNU bash, version 5.2.15(1)-release (sparcv9-sun-solaris2.11)

To see what shell an account is configured with, read the password database rather than /etc/passwd directly, so LDAP or NIS accounts show up too:

$ getent passwd oracle
oracle:x:100:100:Oracle software owner:/export/home/oracle:/usr/bin/ksh

# logins(1M) gives a tidy extended view (run as root)
# logins -x -l oracle
oracle          100     oinstall        100     Oracle software owner
                        /export/home/oracle
                        /usr/bin/ksh
                        PS 082726 7 91 7

Changing a user’s login shell

Step 1: make sure the shell is valid

passwd -e and some daemons (FTP, for example) only accept shells listed in /etc/shells. On Solaris 10 that file does not exist by default, and when it is missing a built-in list of standard shells is used. If you create it, list every shell you want to allow, because the built-in list stops applying.

$ ls -l /etc/shells
$ cat /etc/shells          # if present
/bin/sh
/bin/csh
/bin/ksh
/usr/bin/bash
/usr/bin/ksh
/usr/bin/tcsh
/usr/bin/zsh

Step 2: change it

As root (or with the User Management profile on Solaris 11), usermod -s works on both releases for local accounts:

# usermod -s /usr/bin/bash alice
UX: usermod: alice is currently logged in, some changes may not take effect until next login.

# getent passwd alice
alice:x:1001:10:Alice Admin:/export/home/alice:/usr/bin/bash

A user can change their own shell with passwd -e, which prompts for the new value:

$ passwd -e
Old shell: /bin/sh
New shell: /usr/bin/bash

On Solaris 11, root is a role, so you change its shell with rolemod, not usermod:

# rolemod -s /usr/bin/ksh93 root
# getent passwd root
root:x:0:0:Super-User:/root:/usr/bin/ksh93

Step 3: set the default for future accounts

useradd -D shows and changes the defaults stored in /usr/sadm/defadduser:

# useradd -D
group=other,1  project=default,3  basedir=/home
skel=/etc/skel  shell=/bin/sh  inactive=0
expire=  auths=  profiles=  roles=  limitpriv=
defaultpriv=  lock_after_retries=

# useradd -D -s /usr/bin/bash
# grep defshell /usr/sadm/defadduser
defshell=/usr/bin/bash

That example output is from Solaris 10; Solaris 11.4 prints a similar line with shell=/usr/bin/bash already set and a basedir under /export/home.

Startup files: what each shell reads

ShellLogin (system, then user)Every interactive shell
sh / ksh88 (Solaris 10)/etc/profile, ~/.profileksh: file named by $ENV (often ~/.kshrc)
ksh93 (Solaris 11)/etc/profile, ~/.profile$ENV, defaulting to ~/.kshrc for interactive ksh93
bash/etc/profile, then the first of ~/.bash_profile, ~/.bash_login, ~/.profile~/.bashrc (non-login shells only)
csh / tcsh/etc/.login, ~/.cshrc, ~/.login~/.cshrc

Template files for new home directories live in /etc/skel. On Solaris 10 they are named local.profile, local.cshrc and local.login; copy or rename them to .profile, .cshrc and .login if your account tool does not. Solaris 11 ships ready-to-use dot files such as .profile and .bashrc.

A common gotcha with bash: if a user has a ~/.bash_profile, bash never reads ~/.profile, and a login shell does not read ~/.bashrc unless you source it. A minimal ~/.bash_profile that works on both releases:

# ~/.bash_profile
[ -f ~/.profile ] && . ~/.profile
[ -f ~/.bashrc ] && . ~/.bashrc

Writing scripts that run on both releases

The safest choice for admin scripts that must run on Solaris 10 and 11 is #!/bin/ksh. You get ksh88 on Solaris 10 and ksh93 on Solaris 11, and the ksh88 feature set works on both. Use #!/usr/bin/bash only if you know bash is installed everywhere, and avoid #!/bin/sh unless you write strict Bourne syntax.

Here is the classic way a script breaks. It runs on Solaris 11 because /bin/sh is ksh93, then fails on Solaris 10:

$ cat check.sh
#!/bin/sh
today=$(date +%Y%m%d)
if [ -e /var/tmp/flag ]; then echo "flag set $today"; fi

# Solaris 10
$ ./check.sh
./check.sh: syntax error at line 2: `today=$' unexpected

# Fix 1: write Bourne-compatible syntax
today=`date +%Y%m%d`
if [ -f /var/tmp/flag ]; then echo "flag set $today"; fi

# Fix 2: change the interpreter line
#!/bin/ksh                 (or #!/usr/xpg4/bin/sh for POSIX)

Other differences that bite when moving scripts from Solaris 10 ksh88 to Solaris 11 ksh93:

  • echo behaves differently with backslashes and -n. Use printf in anything portable.
  • In ksh93, functions declared with function name { } have local variables and traps, while name() { } functions do not. ksh88 was looser.
  • ksh93 does floating point arithmetic: (( x = 7 / 2.0 )) gives 3.5. ksh88 truncates or errors.
  • Command output in a pipeline: in both ksh versions the last command of a pipeline runs in the current shell, so cmd | read var works. In bash it runs in a subshell and var stays empty.
  • Tools on the PATH differ too. That is covered in its own post on /usr/bin vs /usr/xpg4/bin vs /usr/gnu/bin.

A quick way to test a script under both shells on a Solaris 11 host is to run it with the shell explicitly:

$ ksh93 -n check.sh        # syntax check only, no execution
$ bash -n check.sh
$ /usr/xpg4/bin/sh check.sh

Interactive comfort settings

Some settings make a stock Solaris shell much nicer to use, especially on Solaris 10:

# ~/.profile (sh, ksh)
EDITOR=vi; export EDITOR
ENV=$HOME/.kshrc; export ENV
PS1="`uname -n`:\$PWD\$ "; export PS1    # ksh expands $PWD at each prompt

# ~/.kshrc
set -o vi            # vi-style command line editing and history (Esc, then k)
HISTSIZE=2000
alias ll='ls -l'

# ~/.bashrc
PS1='\u@\h:\w\$ '
HISTSIZE=5000
set -o vi            # optional; bash defaults to emacs keys

Verification

  • getent passwd username shows the new shell in the last field.
  • A fresh login (for example ssh username@localhost) followed by ps -p $$ -o comm shows the expected shell. Existing sessions keep their old shell.
  • On Solaris 11, getent passwd root shows the shell you set with rolemod, and su - lands in it.
  • useradd -D reports the new default, and a test account created afterwards gets it.

Troubleshooting and gotchas

  • Do not point root at a shell under /usr on Solaris 10 without thinking. If /usr is a separate filesystem, or a library is broken, root cannot log in during maintenance. That is why Solaris 10 uses the statically linked /sbin/sh. If you want bash, run it from root’s .profile only when it exists, or use a personal account plus sudo or RBAC.
  • “Login incorrect” or an instant disconnect after a shell change. The path is wrong, the binary is not installed, or it is not in /etc/shells when that file exists. Fix it from another root session or single-user mode with usermod -s.
  • usermod did nothing for an LDAP or NIS user. It only edits local files. Change loginShell in the directory instead (or use passwd -r ldap -e where your setup allows it).
  • usermod on root fails on Solaris 11. root is a role; use rolemod -s.
  • Scripts that relied on ksh88 quirks break on Solaris 11.4. ksh88 is gone in 11.4. Test with ksh93 -n, fix the syntax, and as a stopgap run them in a Solaris 10 branded zone.
  • Your prompt or aliases do not load. Check which startup file your shell actually reads (table above). For ksh, ENV must be exported from ~/.profile.
  • Backspace prints ^H or ^?. Run stty erase ^H or stty erase ^? to match your terminal; we will cover terminal settings in the remote console post.

What’s next in the Solaris Admin Series

Next we look at Solaris man pages: man sections and how they differ from Linux, building the index with catman, and finding the right Oracle documentation library for your release. Catch up with part 5, Solaris First-Day Commands: uname, /etc/release, showrev, isainfo and isalist, and part 3, Solaris Filesystem Layout Tour.

Official references: Oracle Solaris 11.4 documentation library (man pages for ksh93(1), bash(1), sh(1), usermod(8), rolemod(8), useradd(8) and shells(5)) and Oracle Solaris 10 1/13 documentation library (man pages for sh(1), ksh(1), passwd(1) and useradd(1M)).

Leave a Reply

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