Skip to content

/errors/sed/unknown-option-to-s

sed: -e expression #1, char N: unknown option to `s'

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · sed (GNU sed) 4.9updated
tool
sed
exit code
1
versions
GNU sed 4.x
os
linux, macos, windows

unknown option to `s' means sed finished reading s/pattern/replacement/ earlier than you intended and found unexpected characters where the flags (g, i, p, a number) belong. The cause is nearly always a / inside the pattern or the replacement, such as a file path. Use a different delimiter: sed 's|/usr/local/bin|/opt/bin|'. Any character after s becomes the delimiter.

Reproduce it

bash
echo 'path=/usr/local/bin' > app.conf
sed 's/path=/usr/local/bin/path=/opt/bin/' app.conf
echo "exit status: $?"

Output

sed: -e expression #1, char 13: unknown option to `s'
exit status: 1

sed reads s/path=/usr/ as the complete command and then sees local/... where flags belong. The character number in the message points at the first character it could not use as a flag (here the l of local).

Fix: another delimiter

bash
echo 'path=/usr/local/bin' > app.conf
sed 's|/usr/local/bin|/opt/bin|' app.conf
sed 's#/usr/local#/opt#' app.conf
sed 's/\/usr\/local/\/opt/' app.conf

Output

path=/opt/bin
path=/opt/bin
path=/opt/bin

All three are equivalent. | and # are common choices; pick one that does not appear in the data.

Variables in the replacement

bash
echo 'root=/var/www' > site.conf
NEW_ROOT=/srv/site
sed "s/root=.*/root=$NEW_ROOT/" site.conf
echo "exit status: $?"
sed "s|root=.*|root=$NEW_ROOT|" site.conf

Output

sed: -e expression #1, char 17: unknown option to `s'
exit status: 1
root=/srv/site

The shell expands $NEW_ROOT before sed sees the expression, so the slashes in its value break the command. Use a delimiter that cannot appear in the value. If the value can contain anything, escape it first: printf '%s' "$NEW_ROOT" | sed 's/[|&\\]/\\&/g' escapes the | delimiter, & and backslashes for use in the replacement.

Flags sed accepts after the third delimiter

Everything after the closing delimiter must be a flag (or ;, a newline or } ending the command). GNU sed accepts these:

FlagMeaning
gReplace every match on the line, not only the first
N (a number)Replace only the Nth match; combined with g, the Nth and all later ones
pPrint the pattern space if a substitution was made (useful with -n)
w FILEWrite the line to FILE if a substitution was made; must be the last flag
i or IMatch case-insensitively (GNU extension)
m or MMulti-line mode for ^ and $ (GNU extension)
eRun the pattern space as a shell command and replace it with the output (GNU extension)

Any other letter there produces the error, which is what happens when a path leaks into the flag position: in s/path=/usr/local/... the would-be flag is l. POSIX only defines g, a number, p and w; the other flags are extensions, so check the target sed before relying on them in portable scripts.

Other ways to trigger it

bash
echo 'a' | sed 's/a/b/x'
echo 'a' | sed 's/a/b/ s/b/c/'
echo 'a' | sed 's/a/b/; s/b/c/'
echo 'a' | sed -e 's/a/b/' -e 's/b/c/'

Output

sed: -e expression #1, char 7: unknown option to `s'
sed: -e expression #1, char 8: unknown option to `s'
c
c

The first command has a typo in the flags. The second joins two commands with a space, so sed reads s of the second command as a flag. A ; or a second -e separates them correctly.

How to confirm the fix

Run the corrected expression without -i first and compare its output with the input; once it prints what you expect, add -i (on BSD and macOS, -i ''). An exit status of 0 and the changed line in the output mean the command parsed correctly.

Prevention

In scripts, choose the delimiter from the data, not from habit: paths and URLs almost always contain /, while | rarely appears. When the replacement comes from user input or a variable, escape it before building the expression, or use a tool that takes the replacement as data instead of code, such as awk -v new="$NEW_ROOT" or a short Python script.

FAQ

Which characters can I use as the s delimiter?

Any single character except a backslash or a newline. Pick one that cannot appear in the pattern or the replacement; | , # and : are common choices for paths.

What does char N in the message mean?

It is the position in the sed expression, counted from 1, where sed stopped parsing. For this error it points at the first character after the third delimiter that is not a valid flag.

Does the -i option cause this error?

No. -i edits the file in place but does not change how the s command is parsed; the same expression fails with or without -i.

Sources