1

I am struggling to find a way to replace all the lines with the following information in *.tex files for example:

text{fig/titel/Logo}text

to

text{fig/titel/Logo.png}text

I have tried:

egrep --include=*.tex -r ".*{fig/.*" *

and it gives me the lines where I want to make the changes. But how do I proceed further with the sed command for example?

I have used find and sed, but never used grep and sed. Can anyone please help?

2 Answers2

1

When using sed you don't need grep, because sed can do pattern searches at the same time.

In this case it's a pretty simple expression; look for the string {fig/ and then append a png

Now the search expression needs to be a little more clever, since we want to stop at the closing } character, but that's pretty normal. We should also take into account that there may be more than one per line.

So we end up with

sed 's!{\(fig/[^}]*\)}!{\1.png}!g' 

For example:

$ cat x
untouched lines
text{fig/titel/Logo}text
more lines
text{fig/somethingelse/image}text
text{fig/stuff}text and {fig/morestuff}text

$ sed 's!{\(fig/[^}]*\)}!{\1.png}!g' x
untouched lines
text{fig/titel/Logo.png}text
more lines
text{fig/somethingelse/image.png}text
text{fig/stuff.png}text and {fig/morestuff.png}text

We can see that the {fig/...} entries now have png appended and other stuff is untouched.

  • But when using GNU sed -i, you may want to actually only modify the files that need to be modified, so using grep and sed may actually make sense. – Stéphane Chazelas Aug 19 '16 at 15:04
  • Thanks, is there something called a dry-run or interactive mode while running the above? – infoclogged Aug 19 '16 at 15:04
  • Should the x replaced by *.tex, since I only want to modify the tex files? – infoclogged Aug 19 '16 at 15:07
  • When using sed in this way it doesn't change the file, so it is effectively dry-run mode. You would add the -i flag to do an "in-place" change and update the file. eg sed -i 's!......!g mytext.tex` – Stephen Harris Aug 19 '16 at 15:07
  • Looks like it is working. But, can you please tell me if I can do it recursively in all files and directories? – infoclogged Aug 19 '16 at 15:18
  • 1
    The sed command is designed to work on a single file at a time. You would need to wrap it (eg in a loop, or via xargs) to make it work on multiple files. How you do the loop does the recursion; eg something like find . -name '*.tex' -exec sed -i.bak 's!....!g' {} \; – Stephen Harris Aug 19 '16 at 15:21
1

Assuming GNU tools (which you seem to be using):

grep -r --include='*.tex' -lZ '{fig/.*}' . |
  xargs -r0 sed -i.back 's:\({fig/[^}]*\)}:\1.png}:g'

grep to find the list of files to update, sed to do the update only for those files.

Or if you only want to add the .png if it's not there already, GNU grep has a -P option for PCRE regexp with its (?<!...) negative look-behind operator, but not GNU sed yet (though ssed in the same family does), so switching to perl here:

grep -Pr --include='*.tex' -lZ '\{fig/[^}]*(?<!\.png)\}' . |
  xargs -r0 perl -pi.back -e 's:(\{fig/[^}]*(?<!\.png))\}:$1.png}:g'

With -i.back, the original file is saved with the .back extension.

  • I actually tried this before. The error is sed: -e expression #1, char 19: unknown option to `s'. I have to say, that the grep results are strange, because all the files where the expression is found are one after another in one row without any space between the two found files. – infoclogged Aug 19 '16 at 15:14
  • 1
    You probably tried with s/.../.../ instead of s:...:...: and so had an issue with the embedded / in {fig/. With -lZ, the file names are NUL delimited so it can be safely handled by xargs (with -0). The NUL character is invisible when sent to a terminal. – Stéphane Chazelas Aug 19 '16 at 15:17
  • Oops, I have a new problem. Some of the lines are already with extensions png. Ofoucrse, now all of them have been appended with an added .png :D. Is there a way to just do the substitution only if there isnt a png in the end.. I know basics of regex, but these are quite complicated expressions. – infoclogged Aug 19 '16 at 15:28