0

I am having some trouble using sed to replace lines in files. What i want is to switch out all occurrences of #include <path/to/my/file.h> with #include "file.h" using regex.

when I test it with echo it gives me the output I'm looking for:

echo "#include <path/to/my/file.h>" | sed -E 's/^(#include.*[\/<])([^\/\<]*\.h)(>)$/#include \"\2\"/g'

But when I run it with a file containing #includes, it changes nothing. I feel like I have tried everything I can find online and nothing makes any difference

Here is the line I use when I try changing lines in a file:

sed -E -i '' 's/^(#include.*[\/<])([^\/\<]*\.h)(>)$/#include \"\2\"/g' /path/to/my/file.h

edit: Fixed by removing ^ and $ at the begining and end of the regex statement

Tomas Clausen
  • 23
  • 1
  • 7
  • What is the error message? – Cyrus Jan 04 '20 at 12:17
  • Replace `‘’` with `''`. – Cyrus Jan 04 '20 at 12:17
  • I don't get any error message, it just runs without changing any of the #includes in the file – Tomas Clausen Jan 04 '20 at 12:21
  • the `''` is already like that – Tomas Clausen Jan 04 '20 at 12:30
  • 1
    I think you should remove '' – Philippe Jan 04 '20 at 13:13
  • 2
    Can you share your source files (the #include) so that we can reproduce ? – dash-o Jan 04 '20 at 15:35
  • What happens when you `cat /path/to/my/file.h | sed -E 's/^(#include.*[\/<])([^\/\<]*\.h)(>)$/#include \"\2\"/g'` ? – Brenda J. Butler Jan 05 '20 at 07:04
  • @BrendaJ.Butler tried it, makes no difference – Tomas Clausen Jan 05 '20 at 11:54
  • @dash-o you can find one of my headers here: https://www.dropbox.com/s/yzsdd4uwes0cufv/Afi.h?dl=0 – Tomas Clausen Jan 05 '20 at 11:54
  • @TomasClausen, so when you echo '#include " | sed -E 's/^(#include.*[\/<])([^\/\<]*\.h)(>)$/#include \"\2\"/g' the transformation happens, but when you cat the file and pipe to sed, the transformation doesn't happen? When I tried it just now with the file in filedrop the #include lines did get transformed. That test does not get saved back to the file though ... you see it on stdout. – Brenda J. Butler Jan 05 '20 at 12:00
  • @BrendaJ.Butler exactly. `#include " | sed -E 's/^(#include.*[\/<])([^\/\<]*\.h)(>)$/#include \"\2\"/g'` gives me `#include "file.h"`, but cat-pibe-sed changes nothing for me. What system are you running? I am new to sed, but i read somewhere that there are different versions of sed depending on operating system.. I am working on a mac myself. – Tomas Clausen Jan 05 '20 at 12:14
  • I work on a Linux system. So, I wonder if the newlines in your file are different from what sed expects? od (octal dump) or hexdump can show you: `od -cx afi.h | head -30` – Brenda J. Butler Jan 05 '20 at 12:27
  • @BrendaJ.Butler i just tried to run `sed -E -i '' 's/Dmi/substituteWord/g' /path/to/my/file.h` and this changes all occurrences of Dmi in the linked file. This is getting very strange, considering that the regex and substitution works fine when its with the echo out... – Tomas Clausen Jan 05 '20 at 12:42
  • 1
    If your newlines are tripping you up, you can try to remove the ^ at the beginning and the $ at the end of your sed substitution left-hand-side: `sed -E 's/(#include.*[\/<])([^\/\<]*\.h)(>)/#include \"\2\"/g'` It should not cause extra lines to be affected, unless you have data strings with #include in them. – Brenda J. Butler Jan 05 '20 at 12:43
  • Also you can compare `echo '#include ' | od -cx` with the file piped to od -cx, and see if the newlines look different. If you have checked out the file(s) from a code repo, and it dumped out the files in a format suited for Windows or Linux, that could explain the non-matching when using ^ or $ to anchor to the beginning or end of line. – Brenda J. Butler Jan 05 '20 at 12:57
  • @BrendaJ.Butler the trick with not looking for newlines fixed it for me. Thanks for all the ideas! – Tomas Clausen Jan 09 '20 at 11:28

3 Answers3

1

Based on: In-place edits with sed on OS X

Note that MacOS does not support '-i' with NO suffix (a.k.a 'edit in place'). Per referenced question, try using a temporary suffix (.orig in this example)

sed -E -i '.orig' 's/^(#include.*[\/<])([^\/\<]*\.h)(>)$/#include \"\2\"/g' /path/to/my/file.h
rm -f /path/to/my/file.h.orig
dash-o
  • 13,723
  • 1
  • 10
  • 37
  • The -i option worked for OP when they did a different substitution: sed -E -i '' 's/Dmi/substituteWord/g' /path/to/my/file.h The problem is that his original substitution is not matching. – Brenda J. Butler Jan 05 '20 at 20:37
0

Try this simple sed replacement:

echo "#include <path/to/my/file.h>" |sed -r 's|<[^>]+file.h>|"file.h"|'

output

#include "file.h"

explanation:

sed -r use sed with extended RegExp

's|source-pattern|target-pattern| substitue command use | as separator (instead of normal /)

<[^>]+file.h> RegExp matching <[any char not >](more than once)file.h>

"file.h" Target RegExp match.

Dudi Boy
  • 4,551
  • 1
  • 15
  • 30
0

It seems the line-beginning or line-ending anchors in OPs regex were not matching ... possibly because the source file did not follow the MacOS standard on line endings. MacOS (prior to OS X) uses \r while Windows uses \r\n and linux and OS X uses only \n. So, once OP removed the line-beginning or line-ending anchors in the regex, their expression matched and the replacement happened.

OPs regex without line begin/end anchors:

sed -E 's/(#include.*[\/<])([^\/\<]*\.h)(>)/#include \"\2\"/g'

The risk is that if there are other lines with #include <path/to/some/file.h> in them, those lines will also be replaced when they shouldn't. That's not too likely though.

One way the OP could tell if that might be the problem is to send some samples of text with and without the problem lines to a hexdump or octal dump program. We know that the output of echo '#include <path/to/my/file.h>' does not have any weird newlines in it (because sed with the line ending anchors worked on it), and we can examine what it does have for line endings with:

echo '#include <path/to/my/file.h>' | od -cx

And we can compare those line endings to the ones in:

cat afi.h | od -cx | head -30
Brenda J. Butler
  • 1,475
  • 11
  • 20