Summary
install.sh uses $EUID to decide whether to install to /usr/local/bin (root) or ~/.local/bin (user). $EUID is a bash-only variable — under POSIX shells like dash (the default /bin/sh on Debian/Ubuntu), it expands to an empty string. The script doesn't notice and falls through to the user-mode install path, even when run as root.
The script's shebang is #!/bin/bash, so executing it directly is fine. The problem only appears when it's piped to sh, which is a common copy-paste idiom across the ecosystem (e.g. curl ... | sudo sh).
Reproduce
On Debian 13 (trixie), as root, with /bin/sh symlinked to dash:
# curl -fsSL https://raw.githubusercontent.com/xdevplatform/xurl/main/install.sh | sh
sh: 10: [: Illegal number:
>> Starting installation...
>> Downloading latest release: xurl_Linux_arm64.tar.gz...
>> Installing to /root/.local/bin...
>> Installation complete! You can now run 'xurl' from anywhere.
>> /root/.local/bin is not in your PATH.
# echo "exit: $?"
exit: 0
# which xurl
(nothing)
The [: Illegal number: warning on line 1 is from dash failing to evaluate [ "$EUID" -eq 0 ] (because $EUID is empty). The script reports success and exits 0, but the binary lands in ~/.local/bin rather than /usr/local/bin as intended for root.
Expected
When run as root via any shell, the binary should be installed to /usr/local/bin.
If the shell is unsupported, the script should fail loudly and exit non-zero — not report a successful install.
Actual
[ "$EUID" -eq 0 ] errors under dash but is treated as a false branch (because set -e doesn't trip inside if conditions).
INSTALL_DIR falls through to ${HOME}/.local/bin.
- The script reports "Installation complete!" and exits 0.
- Anyone running
... | sudo sh ends up with the binary in root's home, not on the system PATH.
Root cause
if [ "$EUID" -eq 0 ]; then
INSTALL_DIR="/usr/local/bin"
else
INSTALL_DIR="${HOME}/.local/bin"
fi
$EUID is a bash builtin variable. It does not exist in dash, ash, or other POSIX sh implementations.
Suggested fix
Replace $EUID with the POSIX-portable equivalent:
if [ "$(id -u)" -eq 0 ]; then
INSTALL_DIR="/usr/local/bin"
else
INSTALL_DIR="${HOME}/.local/bin"
fi
id -u is in POSIX and works identically under bash, dash, ash, and zsh.
Optionally, add an explicit shell guard near the top so users piping to sh get a clear error instead of silent misbehaviour:
if [ -z "$BASH_VERSION" ]; then
echo "Error: this script requires bash. Run it with: curl ... | bash" >&2
exit 1
fi
Environment
- OS: Debian 13 (trixie), arm64
- Shell:
/bin/sh → dash 0.5.12
- Install command:
curl -fsSL .../install.sh | sh
- xurl install.sh: current
main (commit at time of report)
Why it matters
- The README correctly says
| bash, but | sh and | sudo sh are widespread copy-paste patterns from other tools' install instructions, and users often substitute without noticing.
- The failure is silent — exit 0, success message, no binary on PATH — which makes it hard to debug. A loud failure would be much friendlier.
- The fix is one line.
Summary
install.shuses$EUIDto decide whether to install to/usr/local/bin(root) or~/.local/bin(user).$EUIDis a bash-only variable — under POSIX shells like dash (the default/bin/shon Debian/Ubuntu), it expands to an empty string. The script doesn't notice and falls through to the user-mode install path, even when run as root.The script's shebang is
#!/bin/bash, so executing it directly is fine. The problem only appears when it's piped tosh, which is a common copy-paste idiom across the ecosystem (e.g.curl ... | sudo sh).Reproduce
On Debian 13 (trixie), as root, with
/bin/shsymlinked to dash:The
[: Illegal number:warning on line 1 is from dash failing to evaluate[ "$EUID" -eq 0 ](because$EUIDis empty). The script reports success and exits 0, but the binary lands in~/.local/binrather than/usr/local/binas intended for root.Expected
When run as root via any shell, the binary should be installed to
/usr/local/bin.If the shell is unsupported, the script should fail loudly and exit non-zero — not report a successful install.
Actual
[ "$EUID" -eq 0 ]errors under dash but is treated as a false branch (becauseset -edoesn't trip insideifconditions).INSTALL_DIRfalls through to${HOME}/.local/bin.... | sudo shends up with the binary in root's home, not on the system PATH.Root cause
$EUIDis a bash builtin variable. It does not exist in dash, ash, or other POSIXshimplementations.Suggested fix
Replace
$EUIDwith the POSIX-portable equivalent:id -uis in POSIX and works identically under bash, dash, ash, and zsh.Optionally, add an explicit shell guard near the top so users piping to
shget a clear error instead of silent misbehaviour:Environment
/bin/sh→ dash 0.5.12curl -fsSL .../install.sh | shmain(commit at time of report)Why it matters
| bash, but| shand| sudo share widespread copy-paste patterns from other tools' install instructions, and users often substitute without noticing.