Home / Alt manpages / add-shell(8)

  • add-shell(8)
  • Admin command
  • linux

Add a Login Shell Safely with add-shell

You will add one or more full-path shell names to /etc/shells, verify the resulting file, and know how to restore the previous version. The installed command is from Debianutils 5.17build1 on this machine.

Allow about ten minutes. You need a shell account with sudo access, the exact path of a shell that is already installed, and a maintenance window if the file is used by an authentication service. This guide changes a system authentication list, so read the proposed path before running the privileged command.

1. Check the installed command and its contract

Confirm which executable will run and record its package version. These are ordinary read-only commands:

$ command -v add-shell
/usr/sbin/add-shell
$ dpkg-query -W -f='${Package} ${Version}\n' debianutils
debianutils 5.17build1
$ man -P cat 8 add-shell | sed -n '1,35p'

The command accepts one or more shell names. Its documented form is add-shell shellname [shellname...]. It copies /etc/shells to /etc/shells.tmp, adds entries that are not already present, then copies the temporary file back. A successful run normally prints nothing.

Checkpoint

You should have confirmed the binary and the package version. The command does not install a shell, make a file executable, or change a user's login shell. It only edits the list used to identify valid login shells.

2. Inspect the current list and the proposed shell

Read the current file before changing it:

$ sed -n '1,120p' /etc/shells
# /etc/shells: valid login shells
/bin/sh
/usr/bin/bash

Your output will differ. Keep comments and existing entries in mind when reviewing the change. To choose a candidate, use its absolute path and check that it exists and is executable:

$ SHELL_PATH='/usr/local/bin/example-shell'
$ test -f "$SHELL_PATH" && test -x "$SHELL_PATH"
$ printf 'candidate: %s\n' "$SHELL_PATH"
candidate: /usr/local/bin/example-shell

Replace the placeholder with the real path. Do not add a wrapper, interpreter or test binary just because it is executable. A path in /etc/shells tells login-related software that the program is suitable as a login shell. Check the program's own documentation and local policy first.

The manpage requires full pathnames. Use /usr/bin/bash, not bash. In this installed version, the utility does not reject a relative argument itself, so a typo such as bash can be written as a useless literal line. Treat the documented full-path requirement as a safety rule and verify the exact line afterwards.

3. Back up the file before the privileged change

This step needs elevated privileges and creates a recovery copy. Do not overwrite the backup if you may need it later:

$ sudo install -m 0644 /etc/shells "/etc/shells.add-shell-backup.$(date +%Y%m%d%H%M%S)"
$ sudo ls -l /etc/shells /etc/shells.add-shell-backup.*

Record the backup path printed by ls. The timestamped copy is the undo point for this change. Keep it until you have confirmed that affected login tools behave correctly.

4. Add the full pathname

Run the command with the exact path. This is the state-changing step and requires elevated privileges:

$ sudo /usr/sbin/add-shell /usr/local/bin/example-shell
$ printf 'add-shell exit status: %s\n' "$?"
add-shell exit status: 0

For several shells, put each full pathname in the same command:

$ sudo /usr/sbin/add-shell /usr/local/bin/example-shell /opt/tools/another-login-shell

There is no interactive confirmation. The command adds an entry only when that exact line is not already present. Running it again with the same path is therefore useful for an idempotence check, but it does not validate that the program is a suitable shell.

5. Verify the exact result

Check the file as a normal user. grep -Fx means the whole line must match, so a partial match cannot give a false success:

$ grep -Fx '/usr/local/bin/example-shell' /etc/shells
/usr/local/bin/example-shell
$ grep -Fx '/opt/tools/another-login-shell' /etc/shells
/opt/tools/another-login-shell

If a command returns status 1 and prints nothing, that exact entry is absent. Investigate the path, permissions and command error rather than editing the file by hand. Also check for an accidental relative entry:

$ awk 'NF && $1 !~ /^#/ && $1 !~ /^\// { print NR ":" $0 }' /etc/shells

That command should print nothing. Any output is a malformed non-comment entry that deserves review. Do not remove an existing line automatically: another package or administrator may depend on it.

6. Test the workflow without touching the real file

The DPKG_ROOT environment variable makes add-shell use a different base directory for /etc/shells. This is useful for rehearsing a change or testing automation without root:

$ test_root=$(mktemp -d)
$ mkdir -p "$test_root/etc"
$ printf '%s\n' '# test shells' '/bin/sh' > "$test_root/etc/shells"
$ DPKG_ROOT="$test_root" add-shell /usr/local/bin/example-shell
$ cat "$test_root/etc/shells"
# test shells
/bin/sh
/usr/local/bin/example-shell
$ test ! -e "$test_root/etc/shells.tmp"
$ printf 'isolated test status: %s\n' "$?"
isolated test status: 0

The test also demonstrates that the temporary file is not normally left behind. Remove the temporary directory after reviewing it with your system's usual temporary-file cleanup process. Do not set DPKG_ROOT during the real system change unless you deliberately intend to edit a staged root.

7. Undo the change if required

Restoring the backup is another privileged, state-changing action. First stop any process that is actively depending on the file if your system policy requires it, then use the exact backup path you recorded:

$ sudo install -m 0644 /etc/shells.add-shell-backup.YYYYMMDDhhmmss /etc/shells
$ if grep -Fx '/usr/local/bin/example-shell' /etc/shells; then echo 'entry still present'; else echo 'entry absent after restore'; fi
entry absent after restore

Replace the placeholder timestamp. If the backup is missing or the file has changed since the backup, stop and compare both files before choosing a recovery version. The restored file may contain other legitimate entries, so review it rather than assuming that this one check proves every detail is correct.

Common failure points

  • No argument: add-shell prints a usage line and exits with status 1.
  • Relative path: the installed command may append it literally even though the manpage requires full pathnames.
  • Duplicate entry: the exact existing line is left alone. Verify the final file rather than relying on command output.
  • Permission denied: use sudo for the command that changes /etc/shells, not for every read-only check.
  • Wrong root: an inherited DPKG_ROOT changes the file that the command targets. Check env | grep '^DPKG_ROOT=' before a production run if your automation sets environment variables.

Done means

  • The candidate is an installed program with a full absolute pathname.
  • You backed up /etc/shells and recorded the backup path.
  • add-shell returned status 0 under sudo.
  • grep -Fx confirms each intended entry and no accidental relative entry was added.
  • You know how to restore the recorded backup if login behaviour is not acceptable.