Summary
AeshRuntimeRunner.detectShell() incorrectly returns BASH when the CLI tool is invoked from PowerShell on macOS/Linux. This is because most CLI tools (including jbang) use a bash wrapper script to launch the JVM, and bash automatically sets BASH_VERSION in its environment. Java inherits this variable, so the detection logic hits the BASH_VERSION check before ever reaching the PSModulePath check.
Reproduction
# In PowerShell on macOS/Linux:
PS> jbang completion
# Generates bash completion instead of pwsh completion
The execution chain is:
pwsh → jbang (bash wrapper script) → bash sets BASH_VERSION → java inherits it
Root Cause
In AeshRuntimeRunner.java:716-740, the detection order is:
static ShellType detectShell() {
if (System.getenv("FISH_VERSION") != null) // 1st
return ShellType.FISH;
if (System.getenv("ZSH_VERSION") != null) // 2nd
return ShellType.ZSH;
if (System.getenv("BASH_VERSION") != null) // 3rd — hits this from pwsh!
return ShellType.BASH;
String shell = System.getenv("SHELL");
if (shell == null || shell.isEmpty()) {
if (System.getenv("PSModulePath") != null) // only as last resort
return ShellType.PWSH;
return null;
}
...
}
BASH_VERSION is set by the bash wrapper script that launches the JVM, not by the user's shell. PSModulePath is set by PowerShell and is never set by bash/zsh/fish. But PSModulePath is only checked as a "last resort" when $SHELL is not set — which never happens on macOS/Linux.
Proposed Fix
Move the PSModulePath check before the BASH_VERSION/ZSH_VERSION/FISH_VERSION checks:
static ShellType detectShell() {
// PSModulePath is set by PowerShell — check it first, before
// BASH_VERSION etc. which leak from wrapper scripts
if (System.getenv("PSModulePath") != null)
return ShellType.PWSH;
if (System.getenv("FISH_VERSION") != null)
return ShellType.FISH;
if (System.getenv("ZSH_VERSION") != null)
return ShellType.ZSH;
if (System.getenv("BASH_VERSION") != null)
return ShellType.BASH;
// Fall back to $SHELL
String shell = System.getenv("SHELL");
...
}
Why this is safe: PowerShell reliably sets PSModulePath on all platforms (Windows, macOS, Linux). Unix shells (bash, zsh, fish) do not set PSModulePath. So checking it first correctly identifies the user's actual shell even when BASH_VERSION leaks from a wrapper.
Impact
Any aesh-based CLI tool that uses detectShell() (via --aesh-completion, --aesh-completion-install, or programmatic AeshRuntimeRunner.detectShell()) will misdetect PowerShell as bash on macOS/Linux when launched via a bash wrapper script.
Related
Summary
AeshRuntimeRunner.detectShell()incorrectly returnsBASHwhen the CLI tool is invoked from PowerShell on macOS/Linux. This is because most CLI tools (including jbang) use a bash wrapper script to launch the JVM, and bash automatically setsBASH_VERSIONin its environment. Java inherits this variable, so the detection logic hits theBASH_VERSIONcheck before ever reaching thePSModulePathcheck.Reproduction
The execution chain is:
Root Cause
In
AeshRuntimeRunner.java:716-740, the detection order is:BASH_VERSIONis set by the bash wrapper script that launches the JVM, not by the user's shell.PSModulePathis set by PowerShell and is never set by bash/zsh/fish. ButPSModulePathis only checked as a "last resort" when$SHELLis not set — which never happens on macOS/Linux.Proposed Fix
Move the
PSModulePathcheck before theBASH_VERSION/ZSH_VERSION/FISH_VERSIONchecks:Why this is safe: PowerShell reliably sets
PSModulePathon all platforms (Windows, macOS, Linux). Unix shells (bash, zsh, fish) do not setPSModulePath. So checking it first correctly identifies the user's actual shell even whenBASH_VERSIONleaks from a wrapper.Impact
Any aesh-based CLI tool that uses
detectShell()(via--aesh-completion,--aesh-completion-install, or programmaticAeshRuntimeRunner.detectShell()) will misdetect PowerShell as bash on macOS/Linux when launched via a bash wrapper script.Related
Completion.javacopies the same detection logic from aesh and has the same bug.