Fix 'extra characters at the end of L command' in sed -i on macOS

beginner๐ŸŽ macOS2026-06-01| macOS (all versions), BSD sed (built-in), Terminal / bash / zsh

Error Message

sed: 1: "filename": extra characters at the end of L command
#sed#terminal#bash#macos-bsd

TL;DR

macOS ships with BSD sed, not GNU sed. BSD sed requires an explicit backup extension after -i. Pass an empty string to skip backup creation:

# macOS fix: add '' after -i
sed -i '' 's/foo/bar/g' filename.txt

Or install GNU sed via Homebrew and use gsed with the Linux syntax you already know.

The full error

sed: 1: "filename": extra characters at the end of L command

You run a sed -i command that works perfectly on Linux, switch to a Mac, and it explodes. The error mentions an "L command" even though you're not using one. Cryptic โ€” but the root cause makes sense once you know there are two incompatible flavors of sed.

Root cause

GNU sed and BSD sed handle the -i flag differently:

  • GNU sed (Linux, most CI servers): -i accepts an optional suffix. Write sed -i 's/โ€ฆ/โ€ฆ/' file and GNU sed treats the suffix as omitted, editing in-place with no backup.
  • BSD sed (macOS built-in): -i requires a suffix โ€” always. Write sed -i 's/โ€ฆ/โ€ฆ/' file and BSD sed grabs your substitution pattern as the backup extension. It then opens your target file and tries to execute its contents as a sed script. The first characters of your file get misread as sed commands โ€” often landing on something BSD sed interprets as an l (lowercase L) command with garbage after it.

That bizarre "extra characters at the end of L command" message means: I opened your file as a sed script and couldn't parse it. Apple has shipped BSD sed on every macOS version and has never included GNU sed by default.

Confirm which version you have:

sed --version 2>&1 | head -1
# GNU: prints "sed (GNU sed) 4.x"
# macOS BSD: prints "sed: illegal option -- -" or exits non-zero

Fix 1 โ€” Add an empty string after -i (macOS native)

BSD sed needs a suffix argument. An empty string '' tells it to edit in-place with no backup:

# Before (breaks on macOS)
sed -i 's/old/new/g' config.txt

# After (works on macOS)
sed -i '' 's/old/new/g' config.txt

The empty string must be a separate token. Quotes touching -i produce unpredictable results across shells:

# Wrong โ€” behavior varies by shell
sed -i'' 's/old/new/g' config.txt

# Correct โ€” explicit space before ''
sed -i '' 's/old/new/g' config.txt

Want a backup copy before editing? Pass an extension instead of the empty string:

# Creates config.txt.bak before modifying config.txt
sed -i '.bak' 's/old/new/g' config.txt

Fix 2 โ€” Install GNU sed via Homebrew

Constantly porting Linux scripts to macOS gets old fast. Installing GNU sed keeps the syntax consistent across both platforms:

brew install gnu-sed

Homebrew installs it as gsed to avoid shadowing the system binary. Use it directly:

gsed -i 's/old/new/g' config.txt

Prefer typing plain sed? Add the GNU tools directory to your PATH in ~/.zshrc or ~/.bashrc:

export PATH="$(brew --prefix)/opt/gnu-sed/libexec/gnubin:$PATH"

After sourcing your profile, sed --version shows the GNU version. Your Linux scripts run unmodified from that point on.

Fix 3 โ€” Write cross-platform scripts

No Homebrew? Detect the OS at runtime. A wrapper function handles multi-argument calls cleanly and avoids the quoting pitfalls of storing the command in a variable:

#!/usr/bin/env bash

sed_inplace() {
  if [[ "$(uname)" == "Darwin" ]]; then
    sed -i '' "$@"
  else
    sed -i "$@"
  fi
}

sed_inplace 's/version=.*/version=2.0/' package.properties

This pattern works in Makefiles, deploy scripts, and CI YAML โ€” anywhere you can't guarantee which platform will run the code.

Verification

Confirm the edit landed correctly after applying the fix:

# Run the fixed command
sed -i '' 's/foo/bar/g' test.txt

# Check the result
grep 'bar' test.txt

# Confirm no unexpected backup files were created
ls -la test.txt*

Substitution shows up in grep output, no stray backup file unless you wanted one โ€” you're done.

Common follow-up issues

  • Script works locally but breaks in CI: Most CI containers (GitHub Actions, CircleCI) run Linux with GNU sed. Add the sed_inplace wrapper above so both environments stay in sync.
  • sed -i '' works in shell but not in a Makefile: Make's default shell is /bin/sh, where quote handling differs. Add SHELL := /bin/bash at the top of the Makefile, or escape carefully: sed -i $'\'' $'\'' 's/โ€ฆ/โ€ฆ/' file.
  • Python or Ruby calling subprocess: Pass -i and '' as separate list elements โ€” not a single string. subprocess.run(['sed', '-i', '', 's/a/b/', 'file']) works; collapsing them into one element does not.

Related Error Notes