Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: July 6, 2024
sed (Stream Editor) is a tool that lets us transform and process text content. In the Linux userland, it’s one of the go-to tools for power users to manipulate text data quickly. However, some sed variants behave differently under certain use cases.
In this tutorial, we’ll discuss the differences between the two most used sed variants: GNU sed and BSD sed. We’ll learn the essential differences between the two with hands-on examples.
BSD sed is used on UNIX-based operating systems like FreeBSD, OpenBSD, NetBSD, and macOS. In addition, the macOS sed is derived from the FreeBSD sed.
In this section, we go through the differences between the GNU sed and BSD sed flavors. Most of the differences should also apply to other BSD variants of sed as well.
ERE extends the Basic Regular Expression (BRE) by allowing additional pattern-matching abilities. In ERE, we don’t need to escape certain characters which carry out special operations.
In BSD sed, we enable the ERE option with -E:
$ sed -E 's/(apple|banana)/REMOVED/g' fruits.txt
REMOVED
REMOVED
orange
mango
tomato
guava
Before GNU sed 4.2, we enable the same option using the -r option:
$ gsed -r 's/(apple|banana)/REMOVED/g' fruits.txt
REMOVED
REMOVED
orange
mango
tomato
guava
However, in GNU sed 4.2+, we can use -E as well, which is an alias for -r. In the same way, in the newer versions of BSD sed (except OpenBSD sed), -r is an alias for -E.
-i lets us make modifications in place in a file. For instance, in GNU sed, we simply specify -i to replace text without having to create a backup file:
$ gsed -i 's/^apple$/avocado/' fruits.txt && cat fruits.txt
avocado
banana
orange
mango
tomato
guava
Conversely, -i in BSD sed always expects an argument. The argument is a backup file extension for the specified input file:
$ /usr/bin/sed -i '.bak' 's/^avocado$/apple/' fruits.txt && ls -lH
total 16
-rw-r--r--@ 1 hey staff 39 Jan 21 17:40 fruits.txt
-rw-r--r--@ 1 hey staff 39 Jan 21 17:39 fruits.txt.bak
In this case, “.bak” is appended to the backup filename. However, if we don’t need to create the backup file, we simply supply an empty string:
$ sed -i '' 's/^avocado$/apple/' fruits.txt
In addition, if we want to achieve cross-compatibility, we need to supply -e alongside -i:
$ sed -i -e 's/^banana$/lime/' fruits.txt
The -e option specifies the expression. It should work the same in both GNU sed and BSD sed except FreeBSD sed. In FreeBSD sed, -i still expects an argument even if we pass it the -e option. So, once we run the command with FreeBSD sed, it creates a backup file with -e appended to its filename.
GNU sed can interpret and process escape sequences like \t, \s, \n, \w, and so on. These sequences have special meanings. For instance, \t specifies the tab character:
$ echo "apple&lime" | gsed 's/&/\t/'
apple lime
On the other hand, BSD sed has limited support for interpreting escape sequences:
$ echo "apple&lime" | sed 's/&/\t/'
apple\tlime
It only interprets \n (newline character) in the pattern and replacement parts of the expression. macOS sed is an exception in this case.
In GNU sed, we can separate the expressions with a newline when surrounded by braces:
$ echo "apple" | gsed '{ s/apple/lemon/
s/lemon/lime/ }'
lime
In contrast, it’s not permissive in BSD sed:
$ echo "apple" | sed '{ s/apple/lemon/
s/lemon/lime/ }'
sed: 2: "{ s/apple/lemon/
s/Hi/Hey/ }": bad flag in substitute command: '}'
The i, a, and c commands specify how to add text:
In BSD sed, we need to add a backslash and a newline after the i, a, and c commands:
$ echo "apple" | sed '1i\
lime'
lime
apple
Alternatively, we can achieve the same result in a single-line expression:
$ echo "apple" | sed $'1i\\\nlime\n'
lime
apple
In GNU sed, we don’t need anything like that:
$ echo "apple" | gsed '1ilime'
lime
apple
GNU sed automatically inserts a newline after the text is inserted with the i, a, and c commands.
In regular expressions, modifiers change the behavior of pattern matching. The I modifier disables case sensitivity and the M modifier does multi-line mode matching.
In older macOS sed, the I and M modifiers aren’t supported. However, they’re supported by FreeBSD sed as well as GNU sed:
$ echo "Apple" | gsed -E '/apple/I s/a/A/'
Apple
Similarly, we can use the M modifier in GNU sed as well:
$ echo -e "apple\nlime" | gsed -n '/^apple$/M{N; s/\n/ /p;}'
apple lime
macOS sed doesn’t support -s, -u, and -z options:
While they work in GNU sed, we’ll encounter an error when we specify one of these options in BSD sed:
$ sed -s 's/file/replaced/' file1.txt file2.txt
sed: illegal option -- s
In BSD sed, -a makes the w command append to a file:
$ echo "plum" | sed -a 'w fruits.txt' && cat fruits.txt
apple
banana
orange
mango
tomato
guava
plum
But, it’s not supported by macOS sed. However, we can achieve the same result with the append to file redirect operator >>.
In addition to the core differences, there are extra features that are solely implemented by GNU sed:
While we can achieve the same result with BSD sed, it’s much more difficult compared to the built-ins provided in GNU sed. For that reason, we should use alternatives like awk or Perl.
Sometimes, it’s harder to keep track of the differences between the variants. Fortunately, we can install GNU sed on macOS. For that purpose, we can use Homebrew:
$ brew install gnu-sed
Once installed, we can use it by prefixing it with g:
$ gsed --version
gsed (GNU sed) 4.9
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
In this article, we learned the core differences between the GNU sed and BSD sed. In addition to the core differences, we also briefly discussed the additional minor differences that are specific to GNU sed.
Finally, we saw how to install and use GNU sed on macOS.