1

I want to keep a list of substitutions in a command file:

subs.sed

s/hello/foo/g
s/world/bar/g

I run it like this sed -i -f subs.sed file.txt

file.txt turns from hello world into foo bar.

I would like to stop this from happening though: if file.txt is helloworld, I wouldn't want any of the two substitutions above to happen. The output currently would be foobar but I would like the output to be helloworld instead.

I could manually specify word boundaries in the command file:

s/\<hello\>/foo/g
s/\<world\>/bar/g

But I would prefer to keep this file as human-readable as possible, and not obscure it with this kind of verbosity.

Is there a command-line option I can enable sed itself to match only whole words? Of course, if there's another way to edit the command line (run sed on the command file before running sed with it? But I'm worried about parsing complex replacements) that is as bulletproof as it can be that would be great too.

This is GNU sed on Ubuntu 22.04

3
  • 1
    What operating system are you using? We need to know what sed implementation you have. Although I doubt any of them have such an option since the s/// operator is only a tiny part of what sed can do so it wouldn't make much sense to have a command line option that only affected it this way.
    – terdon
    Commented Jan 16, 2023 at 19:49
  • Please add that to your question. I have no idea what "Github Actions" are or what their "ubuntu-latest" would be, but if you are actually running an Ubuntu system, you should have GNU sed.
    – terdon
    Commented Jan 16, 2023 at 22:11
  • Adding word boundaries only handles false matches of substrings, you'd still have metacharacters to deal with (see is-it-possible-to-escape-regex-metacharacters-reliably-with-sed).
    – Ed Morton
    Commented Jan 18, 2023 at 13:20

1 Answer 1

1

First of all, if you are running GNU sed, the default on Linux systems, you can also simplify your sed using \b instead of \> and \< which might make it more legible for you:

$ cat subs.sed 
s/\bhello\b/foo/g
s/\bworld\b/bar/g

That said, I don't think you can do what you describe, but here's a workaround: keep your file as is, but add a preprocessing step:

$ sed -e 's|/|/\\<|' -e 's|/|\\>/|2' subs.sed 
s/\<hello\>/foo/g
s/\<world\>/bar/g

Here, we are passing two commands to sed. The first one will replace the first occurrence of / with /\< and the second will replace the second / with /\>. We need \\> and \\< because \ is the escape character, so we need to escape it by adding another \ to treat it as a literal backslash. Then, the 2 at the end of the second command means "do this on the second match of the line". This is easier to explain by example:

$ echo "......" | sed 's/./A/'
A.....
$ echo "......" | sed 's/./A/2'
.A....
$ echo "......" | sed 's/./A/3'
..A...
$ echo "......" | sed 's/./A/4'
...A..

So, with that command in hand, you can make a little alias that runs your actual substitutions, as long as you are using a shell that understands <() for Process Substitution:

$ sed -f <(sed -e 's|/|/\\<|' -e 's|/|\\>/|2' subs.sed) file.txt 
foo you
the bar
helloworld

And, to make your life a bit easier, you can add this line to your shell's initialization file (~/.bashrc for example) to make an alias:

alias mysub="sed -i -f <(sed -e 's|/|/\\<|' -e 's|/|\\>/|2' /path/to/subs.sed)"

Open a new terminal and you can now run mysub file and get the expected output.

5
  • Pretty cool! I didn't know about the number "flags". To match the second / but not if it is preceded by a backslash, can I still use sed or would I be better to use another tool for the preprocessing (like this suggests stackoverflow.com/questions/26110266/…)
    – minseong
    Commented Jan 16, 2023 at 22:33
  • 1
    @theonlygusti no, sed doesn't do lookbehind/lookahead assertions. You can use perl which is actually where the s/// operator originally came from, I think, and also has the -i, but combined with the far more powerful PCRE regular expression language which does support lookarounds. Personally, I would do this whole thing in perl directly.
    – terdon
    Commented Jan 16, 2023 at 22:35
  • ed was doing s/// for about 20 years before perl was invented, sed about 15 years before perl.
    – Ed Morton
    Commented Jan 18, 2023 at 13:28
  • 1
    Ah, good thing I hedged a bit then! Thanks @EdMorton. Did the -i option originate from perl?
    – terdon
    Commented Jan 18, 2023 at 14:18
  • 1
    @terdon probably, perl does seem to be the source for most unnecessary constructs :-).
    – Ed Morton
    Commented Jan 18, 2023 at 14:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .