Skip to main content
DevOps Insightsguides & answers

Linux shell · troubleshooting

set: Illegal option -o pipefail

A script written for bash ran under /bin/sh, which on Debian and Ubuntu is dash. That shell rejects pipefail, source, [[ ]], arrays and the function keyword, and two of those fail silently.

Reproduced on Ubuntu 24.04, . Where the text says “reproduced”, the behaviour was run on that version, not taken from documentation.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 9 sections

Before you start

  • How the script is started: its first line, and the command or tool that runs it.

Run this first

step 1 of 1
readlink -f /bin/sh

Look for /usr/bin/dash

Shows which shell sh really is here.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

Is bash available where this runs?

Cause space

2 of 2 still possible

  • A bash script is run with shCommon
  • The script has to run under shOccasional

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    readlink -f /bin/sh

    Shows which shell sh really is here.

    Look for /usr/bin/dash

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

A bash script is run with sh

Common

sh script.sh, a #!/bin/sh shebang, a Dockerfile RUN line, a Makefile recipe or a CI step whose shell is sh.

Confirm

head -1 script.sh
readlink -f /bin/sh

#!/bin/sh or a sh call, with /bin/sh pointing to dash

Fix

  • Use #!/usr/bin/env bash and run the script directly or with bash script.sh.
  • In a Dockerfile, add SHELL ["/bin/bash", "-o", "pipefail", "-c"] before RUN lines that need bash.
  • In a Makefile, set SHELL := /bin/bash. GNU Make 4.3 on Ubuntu 24.04 ran a recipe with [[ ]] under /bin/sh, printed [[: not found and carried on; with SHELL := /bin/bash the same recipe worked.
#!/usr/bin/env bash
set -euo pipefail

Reproduced on Ubuntu 24.04: the same file ran under bash.

The script has to run under sh

Occasional

Minimal images without bash, or tools that always use sh.

Confirm

command -v bash || echo no bash

no bash: bash is not installed here

Fix

  • Use the POSIX forms: . ./file instead of source, [ ] with quoted variables instead of [[ ]], name() { … } without function, and positional parameters or separate variables instead of arrays.
  • Make pipefail conditional: (set -o pipefail 2>/dev/null) && set -o pipefail.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

On Ubuntu and Debian, sh is a smaller shell called dash. It runs plain POSIX shell, not bash. A script started with sh script.sh, with #!/bin/sh, or from a tool that uses sh, loses bash's extras. Some fail loudly; source and [[ only print a message and carry on.

Words used on this page

dash
Debian's /bin/sh: POSIX-only, no bash extensions.
. (dot)
The POSIX way to read a file into the current shell; source is the bash name for it.
What is actually happening

Reproduced on 23 October 2026 on Ubuntu 24.04, where /bin/sh is dash. set -euo pipefail under sh stopped with p.sh: 1: set: Illegal option -o pipefail, exit 2, and ARR=(a b c) or function greet() stopped with Syntax error: "(" unexpected.

Two failed quietly. source ./env.sh printed source: not found and the script carried on with the variable empty, exit 0. [[ $A == prod ]] printed [[: not found and took the else branch, exit 0.

The POSIX forms all worked under dash: . ./env.sh, [ "$A" = prod ], greet() { … } without the function keyword, and (set -o pipefail 2>/dev/null) && set -o pipefail where pipefail is optional.

How to tell this is your problem
WhereWhat you see
set -euo pipefail under shscript.sh: 1: set: Illegal option -o pipefail
source under shscript.sh: 1: source: not found
[[ ]] under shscript.sh: 2: [[: not found
Arrays or function keyword under shscript.sh: 1: Syntax error: "(" unexpected

How to know it is actually fixed

  • The script runs to the end under the shell it is meant for, with variables from the sourced file set.

Stopping it happening again

  • Give every script an explicit bash or sh shebang, and keep sh scripts POSIX.
  • Run shellcheck in CI; it checks a #!/bin/sh script against POSIX sh.

Security considerations

  • source: not found does not stop the script. A deploy that sources its settings this way under sh runs on with empty variables, so treat this message as a failure.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it