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
pfexecorsudo). - 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
| Shell | Solaris 10 | Solaris 11.4 | Notes |
|---|---|---|---|
| Bourne shell (legacy) | /bin/sh, /sbin/sh | Removed; /bin/sh is ksh93 | No $( ), 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/ksh | Removed 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/ksh93 | Floating 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/csh | Avoid 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 installed | Not 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
| Account | Solaris 10 | Solaris 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
| Shell | Login (system, then user) | Every interactive shell |
|---|---|---|
| sh / ksh88 (Solaris 10) | /etc/profile, ~/.profile | ksh: 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:
echobehaves differently with backslashes and-n. Useprintfin anything portable.- In ksh93, functions declared with
function name { }have local variables and traps, whilename() { }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 varworks. In bash it runs in a subshell andvarstays empty. - Tools on the
PATHdiffer too. That is covered in its own post on/usr/binvs/usr/xpg4/binvs/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 usernameshows the new shell in the last field.- A fresh login (for example
ssh username@localhost) followed byps -p $$ -o commshows the expected shell. Existing sessions keep their old shell. - On Solaris 11,
getent passwd rootshows the shell you set withrolemod, andsu -lands in it. useradd -Dreports the new default, and a test account created afterwards gets it.
Troubleshooting and gotchas
- Do not point root at a shell under
/usron Solaris 10 without thinking. If/usris 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.profileonly when it exists, or use a personal account plussudoor 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/shellswhen that file exists. Fix it from another root session or single-user mode withusermod -s. usermoddid nothing for an LDAP or NIS user. It only edits local files. ChangeloginShellin the directory instead (or usepasswd -r ldap -ewhere your setup allows it).usermodon root fails on Solaris 11. root is a role; userolemod -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,
ENVmust be exported from~/.profile. - Backspace prints
^Hor^?. Runstty erase ^Horstty 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)).
