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)
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 and the BSDs) over the SHELL environment variable, so it stays accurate when SHELL is unset or points to a different shell than the one executing.
Symlink resolution: implementation over interface¶
Shell detection resolves symlinks in the SHELL environment variable before identifying the shell. This means detection always reports the concrete shell implementation rather than the POSIX interface name.
On most modern Unix systems, /bin/sh is a symlink to a concrete shell:
Distribution |
|
|---|---|
Debian, Ubuntu |
|
Fedora, RHEL |
|
Alpine |
|
macOS |
|
When /bin/sh symlinks to /bin/bash:
is_bash()returnsTrue(the actual binary running is bash).is_sh()returnsFalse(the resolved binary is notsh).is_bourne_shells()returnsTrue(bash is a Bourne-compatible shell).
This design gives developers the most precise information about what binary is executing their code. If you need to check whether the shell provides a Bourne-compatible interface (regardless of which binary implements it), test against the BOURNE_SHELLS group with is_bourne_shells() instead of individual shell functions.
Multiple shells can match at once¶
Shell detection functions are independent heuristics. Each one reads three channels in turn:
A version environment variable that only the shell itself sets on startup (like
FISH_VERSION).The
SHELLenvironment variable: the configured login shell, resolved through symlinks as described above.The parent process tree (read from
/procon Linux,pson macOS and the BSDs, 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()(fromSHELL) andis_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
PSModulePathon startup and every child process inherits it, sois_powershell()can stayTruealongside 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, 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 |
|---|---|---|---|
🪶 |
Almquist Shell |
||
# |
Bash |
||
▶ |
Command Prompt |
||
𝐂 |
C shell |
||
💨 |
Dash |
||
🐟 |
Fish |
||
𝐊 |
Korn shell |
||
𝜈 |
Nushell |
||
🔷 |
PowerShell |
||
𝐒 |
Bourne Shell |
||
𝐓 |
tcsh |
||
🐍 |
Xonsh |
||
ℤ |
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 |
||
|---|---|---|---|---|
🐚 |
All shells |
|||
💲 |
Bourne-compatible shells |
⬥ |
||
🅲 |
C shells |
⬥ |
||
◇ |
Other shells |
⬥ |
||
⌨️ |
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')¶
ID:
ashAliases: -
Name: Almquist Shell
Icon: 🪶
Reference: <https://en.wikipedia.org/wiki/Almquist_shell>_
Detection function:
is_ash()Pytest decorators:
@skip_ash/@unless_ashGroups (3):
ALL_SHELLS,ALL_TRAITS,BOURNE_SHELLS⬥
- extra_platforms.BASH = Shell(id='bash', name='Bash')¶
ID:
bashAliases: -
Name: Bash
Icon: #
Reference: <https://www.gnu.org/software/bash/>_
Detection function:
is_bash()Pytest decorators:
@skip_bash/@unless_bashGroups (3):
ALL_SHELLS,ALL_TRAITS,BOURNE_SHELLS⬥
- extra_platforms.CMD = Shell(id='cmd', name='Command Prompt')¶
ID:
cmdAliases: -
Name: Command Prompt
Icon: ▶
Reference: <https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/cmd>_
Detection function:
is_cmd()Pytest decorators:
@skip_cmd/@unless_cmdGroups (3):
ALL_SHELLS,ALL_TRAITS,WINDOWS_SHELLS⬥
- extra_platforms.CSH = Shell(id='csh', name='C shell')¶
ID:
cshAliases: -
Name: C shell
Icon: 𝐂
Reference: <https://en.wikipedia.org/wiki/C_shell>_
Detection function:
is_csh()Pytest decorators:
@skip_csh/@unless_cshGroups (3):
ALL_SHELLS,ALL_TRAITS,C_SHELLS⬥
- extra_platforms.DASH = Shell(id='dash', name='Dash')¶
ID:
dashAliases: -
Name: Dash
Icon: 💨
Reference: <https://en.wikipedia.org/wiki/Almquist_shell#dash>_
Detection function:
is_dash()Pytest decorators:
@skip_dash/@unless_dashGroups (3):
ALL_SHELLS,ALL_TRAITS,BOURNE_SHELLS⬥
- extra_platforms.FISH = Shell(id='fish', name='Fish')¶
ID:
fishAliases: -
Name: Fish
Icon: 🐟
Reference: <https://fishshell.com>_
Detection function:
is_fish()Pytest decorators:
@skip_fish/@unless_fishGroups (3):
ALL_SHELLS,ALL_TRAITS,OTHER_SHELLS⬥
- extra_platforms.KSH = Shell(id='ksh', name='Korn shell')¶
ID:
kshAliases: -
Name: Korn shell
Icon: 𝐊
Reference: <https://en.wikipedia.org/wiki/KornShell>_
Detection function:
is_ksh()Pytest decorators:
@skip_ksh/@unless_kshGroups (3):
ALL_SHELLS,ALL_TRAITS,BOURNE_SHELLS⬥
- extra_platforms.NUSHELL = Shell(id='nushell', name='Nushell')¶
ID:
nushellAliases: -
Name: Nushell
Icon: 𝜈
Reference: <https://www.nushell.sh>_
Detection function:
is_nushell()Pytest decorators:
@skip_nushell/@unless_nushellGroups (3):
ALL_SHELLS,ALL_TRAITS,OTHER_SHELLS⬥
- extra_platforms.POWERSHELL = Shell(id='powershell', name='PowerShell')¶
ID:
powershellAliases: -
Name: PowerShell
Icon: 🔷
Reference: <https://learn.microsoft.com/en-us/powershell/>_
Detection function:
is_powershell()Pytest decorators:
@skip_powershell/@unless_powershellGroups (3):
ALL_SHELLS,ALL_TRAITS,WINDOWS_SHELLS⬥
- extra_platforms.SH = Shell(id='sh', name='Bourne Shell')¶
ID:
shAliases: -
Name: Bourne Shell
Icon: 𝐒
Reference: <https://en.wikipedia.org/wiki/Bourne_shell>_
Detection function:
is_sh()Pytest decorators:
@skip_sh/@unless_shGroups (3):
ALL_SHELLS,ALL_TRAITS,BOURNE_SHELLS⬥
- extra_platforms.TCSH = Shell(id='tcsh', name='tcsh')¶
ID:
tcshAliases: -
Name: tcsh
Icon: 𝐓
Reference: <https://www.tcsh.org>_
Detection function:
is_tcsh()Pytest decorators:
@skip_tcsh/@unless_tcshGroups (3):
ALL_SHELLS,ALL_TRAITS,C_SHELLS⬥
- extra_platforms.UNKNOWN_SHELL = Shell(id='unknown_shell', name='Unknown shell')¶
ID:
unknown_shellAliases: -
Name: Unknown shell
Icon: ❓
Reference: <https://en.wikipedia.org/wiki/Shell_(computing)>_
Detection function:
is_unknown_shell()Pytest decorators:
@skip_unknown_shell/@unless_unknown_shellGroups (2):
ALL_TRAITS,UNKNOWN⬥
- extra_platforms.XONSH = Shell(id='xonsh', name='Xonsh')¶
ID:
xonshAliases: -
Name: Xonsh
Icon: 🐍
Reference: <https://xon.sh>_
Detection function:
is_xonsh()Pytest decorators:
@skip_xonsh/@unless_xonshGroups (3):
ALL_SHELLS,ALL_TRAITS,OTHER_SHELLS⬥
- extra_platforms.ZSH = Shell(id='zsh', name='Zsh')¶
ID:
zshAliases: -
Name: Zsh
Icon: ℤ
Reference: <https://www.zsh.org>_
Detection function:
is_zsh()Pytest decorators:
@skip_zsh/@unless_zshGroups (3):
ALL_SHELLS,ALL_TRAITS,BOURNE_SHELLS⬥