Shells

Each shell represents a command-line interpreter, and is associated with:

  • a unique shell ID

  • a human-readable name

  • an icon (emoji / unicode character)

  • a detection function

  • various metadata in its info() method

Shell usage

Each shell is materialized by a Shell object, from which you can access various metadata:

>>> from extra_platforms import BASH
>>> BASH
Shell(id='bash', name='Bash')
>>> BASH.id
'bash'
>>> BASH.current
False
>>> BASH.info()
{'id': 'bash', 'name': 'Bash', 'icon': '#', 'url': 'https://www.gnu.org/software/bash/', 'current': False, 'version': None, 'path': None}

To check if the current environment is running in a specific shell, use the corresponding detection function:

>>> from extra_platforms import is_bash
>>> is_bash()
False

The current shell can be obtained via the current_shell() function:

>>> from extra_platforms import current_shell
>>> current_shell()
Shell(id='unknown_shell', name='Unknown shell')

The path to the running shell’s executable is available via current_shell_path(). It prefers the actual ancestor process binary (read from /proc on Linux, ps on macOS, the BSDs and illumos, the Win32 API on Windows) over the SHELL environment variable, so it stays accurate when SHELL is unset or points to a different shell than the one executing. It falls back to SHELL only when that variable names the same shell, and returns None otherwise. The path key of Shell.info() follows the same rules.

To get the Shell that a binary path names, use shell_from_path(). It also recognizes a binary whose file name is not the shell ID, like pwsh for PowerShell:

>>> from extra_platforms import shell_from_path
>>> shell_from_path("/usr/local/bin/pwsh")
Shell(id='powershell', name='PowerShell')
extra_platforms.shell_from_path(path)[source]

Return the Shell a binary path names.

For a shell path read from configuration rather than from the running process: the SHELL variable, a passwd entry, a terminal profile. current_shell() answers a different question, which shell the process runs in.

The path is resolved through symlinks when it exists, so /bin/sh on a system linking it to bash gives BASH, and its lowercased file name, suffix dropped, is matched against every shell’s executable_names. A link to a binary going by no shell’s name keeps its own name, the way a multi-call binary picks its program, and Alpine’s /bin/sh and /bin/ash, both links to BusyBox, give ASH. An unmatched name gives UNKNOWN_SHELL.

Return type:

Shell

Multiple shells can match at once

Shell detection functions are independent heuristics. Each one reads three channels in turn:

  1. A version environment variable that only the shell itself sets on startup (like FISH_VERSION).

  2. The SHELL environment variable: the configured login shell, resolved through symlinks as described above.

  3. The parent process tree (read from /proc on Linux, ps on macOS, the BSDs and illumos, the Win32 API on Windows): the shells actually running as ancestors of the current process.

These channels describe different things, so several detection functions can legitimately return True at the same time:

  • The login shell differs from the running shell: a fish user running a bash script is detected by both is_fish() (from SHELL) and is_bash() (from the process tree).

  • Shells nest: a build chroot driven from a fish terminal keeps fish in the ancestor tree, above the bash chain running the build, and both are real ancestor processes.

  • PowerShell modifies PSModulePath on startup and every child process inherits it, so is_powershell() can stay True alongside the shell really executing your code. This is a permanent fixture of GitHub Ubuntu runners, where the variable leaks from the Azure infrastructure.

An is_*() function therefore answers “is this shell part of the current environment?”, not “is this the shell executing me?”. For the latter, use current_shell(): it arbitrates all matches down to a single primary shell, preferring active version variables, then running ancestor processes (the nearest one when shells nest), then the configured login shell. current_traits() applies no such arbitration, so it may contain several shells.

For example, on a Mac where the terminal is configured to launch fish while SHELL still points at the stock zsh:

>>> from extra_platforms import current_shell, is_fish, is_zsh
>>> is_zsh()  # The configured login shell, from SHELL.
True
>>> is_fish()  # The shell actually running, from the process tree.
True
>>> current_shell()
Shell(id='fish', name='Fish')

Recognized shells

Icon

Symbol

Name

Detection function

🪶

ASH

Almquist Shell

is_ash()

#

BASH

Bash

is_bash()

▶

CMD

Command Prompt

is_cmd()

𝐂

CSH

C shell

is_csh()

💨

DASH

Dash

is_dash()

🐟

FISH

Fish

is_fish()

𝐊

KSH

Korn shell

is_ksh()

𝜈

NUSHELL

Nushell

is_nushell()

🔷

POWERSHELL

PowerShell

is_powershell()

𝐒

SH

Bourne Shell

is_sh()

𝐓

TCSH

tcsh

is_tcsh()

🐍

XONSH

Xonsh

is_xonsh()

ℤ

ZSH

Zsh

is_zsh()

Hint

The UNKNOWN_SHELL trait represents an unrecognized shell. It is not included in the ALL_SHELLS group, and will be returned by current_shell() if the current shell is not recognized.

Groups of shells

Icon

Symbol

Description

Detection

Canonical

🐚

ALL_SHELLS

All shells

is_any_shell()

💲

BOURNE_SHELLS

Bourne-compatible shells

is_bourne_shells()

⬥

🅲

C_SHELLS

C shells

is_c_shells()

⬥

◇

OTHER_SHELLS

Other shells

is_other_shells()

⬥

⌨️

WINDOWS_SHELLS

Windows shells

is_windows_shells()

⬥

Hint

Canonical groups are non-overlapping groups that together cover all recognized traits. They are marked with a ⬥ icon in the table above.

Other groups are provided for convenience, but overlap with each other or with canonical groups.

        ---
config:
  sankey:
    height: 800
    showValues: false
    width: 800

---
sankey-beta

ALL_SHELLS,BOURNE_SHELLS,6
ALL_SHELLS,OTHER_SHELLS,3
ALL_SHELLS,WINDOWS_SHELLS,2
ALL_SHELLS,C_SHELLS,2
BOURNE_SHELLS,ASH,1
BOURNE_SHELLS,BASH,1
BOURNE_SHELLS,DASH,1
BOURNE_SHELLS,KSH,1
BOURNE_SHELLS,SH,1
BOURNE_SHELLS,ZSH,1
OTHER_SHELLS,FISH,1
OTHER_SHELLS,NUSHELL,1
OTHER_SHELLS,XONSH,1
WINDOWS_SHELLS,CMD,1
WINDOWS_SHELLS,POWERSHELL,1
C_SHELLS,CSH,1
C_SHELLS,TCSH,1
    
        ---
config:
  mindmap:
    padding: 5

---
mindmap
    ((🐚 ALL_SHELLS))
        )⌨️ WINDOWS_SHELLS(
            (▶ CMD)
            (🔷 POWERSHELL)
        )◇ OTHER_SHELLS(
            (🐟 FISH)
            (𝜈 NUSHELL)
            (🐍 XONSH)
        )🅲 C_SHELLS(
            (𝐂 CSH)
            (𝐓 TCSH)
        )💲 BOURNE_SHELLS(
            (🪶 ASH)
            (# BASH)
            (💨 DASH)
            (𝐊 KSH)
            (𝐒 SH)
            (ℤ ZSH)
    

Predefined shells

Shell definitions and metadata.

extra_platforms.ASH = Shell(id='ash', name='Almquist Shell')
extra_platforms.BASH = Shell(id='bash', name='Bash')
extra_platforms.CMD = Shell(id='cmd', name='Command Prompt')
extra_platforms.CSH = Shell(id='csh', name='C shell')
extra_platforms.DASH = Shell(id='dash', name='Dash')
extra_platforms.FISH = Shell(id='fish', name='Fish')
extra_platforms.KSH = Shell(id='ksh', name='Korn shell')
extra_platforms.NUSHELL = Shell(id='nushell', name='Nushell')
extra_platforms.POWERSHELL = Shell(id='powershell', name='PowerShell')
extra_platforms.SH = Shell(id='sh', name='Bourne Shell')
extra_platforms.TCSH = Shell(id='tcsh', name='tcsh')
extra_platforms.UNKNOWN_SHELL = Shell(id='unknown_shell', name='Unknown shell')
extra_platforms.XONSH = Shell(id='xonsh', name='Xonsh')
extra_platforms.ZSH = Shell(id='zsh', name='Zsh')