Matched signals
- syntax error: unexpected
- source: not found
- local: not in a function
- [[: not found
- declare: not found
- function: not found
- Illegal option
- no such file or directory.*bash
Shell compatibility issue (sh vs bash)
What this failure means
A shell script uses bash-specific syntax but is executed by /bin/sh, which
is commonly dash on Ubuntu/Debian or ash on Alpine. Constructs like
[[ ]], source, local outside functions, arrays, and process
substitution are bash extensions and fail silently or loudly under POSIX sh.
Symptoms
Faultline looks for one or more of these log fragments:
syntax error: unexpected
source: not found
local: not in a function
[[: not found
declare: not found
function: not found
Illegal option
no such file or directory.*bash
Diagnosis
On many Linux distributions, /bin/sh is not bash:
- Ubuntu/Debian:
/bin/sh→dash(faster POSIX sh, no bash extensions) - Alpine:
/bin/sh→busybox ash(minimal POSIX sh) - macOS:
/bin/sh→bash 3.2(old bash, missing newer features)
A script with #!/bin/bash shebang runs correctly. The same script run by a
CI step without a shebang, or run with sh script.sh, may fail.
Check the actual shell:
ls -la /bin/sh # reveals symlink target
sh --version 2>/dev/null || sh -c "echo $0"
Find bash-specific syntax in scripts:
# Common bash-only constructs
grep -rn '\[\[' --include="*.sh" .
grep -rn 'source ' --include="*.sh" .
grep -rn 'declare -' --include="*.sh" .
grep -rn '<<(' --include="*.sh" . # process substitution
Fix steps
-
Add a
#!/usr/bin/env bashshebang to scripts that use bash extensions:#!/usr/bin/env bash set -euo pipefail # Now bash-specific syntax is safe [[ -n "${VAR}" ]] && echo "set" declare -a arr=(a b c) source helpers.sh -
Install bash in Alpine containers that use bash scripts:
# Alpine does not include bash by default RUN apk add --no-cache bash -
Alternatively, rewrite bash-specific constructs using POSIX sh equivalents:
# bash: [[ ]] → POSIX: [ ] if [ -n "$VAR" ]; then ... # bash: source → POSIX: . (dot) . helpers.sh # bash: local → POSIX: only valid inside functions (both support it) # bash arrays → No POSIX equivalent; restructure or use bash # bash: <<() → POSIX alternative uses temporary files -
For GitHub Actions, specify the shell explicitly:
- name: Run script shell: bash run: | [[ -n "$VAR" ]] && echo "set" -
Use ShellCheck to detect bash-isms in scripts intended to be portable:
shellcheck --shell=sh script.sh # strict POSIX mode shellcheck script.sh # uses shebang to determine shell
Validation
- Run
shellcheck script.shand resolve any issues it reports. - Test with the target shell:
sh script.shandbash script.sh. - Re-run the CI step and confirm the syntax error is resolved.
Why it matters
Shell compatibility issues are extremely common when scripts are written on macOS (bash 3.2 or zsh) and run in Alpine-based CI containers (ash). They produce cryptic “syntax error: unexpected” messages that do not indicate a bash vs sh problem.
Prevention
- Always include a shebang line in shell scripts.
- Run ShellCheck as part of CI lint.
- Install bash explicitly in Alpine Dockerfiles that use bash scripts.
- Prefer
#!/usr/bin/env bashover#!/bin/bashfor portability across install locations.
How Faultline detects it
Use faultline explain shell-sh-vs-bash to see the full playbook.
faultline analyze build.log
faultline explain shell-sh-vs-bash
Generated from playbooks/bundled/log/build/shell-sh-vs-bash.yaml. Do not edit directly.