2

I want to search through a bunch of MD and HTML files for a line that begins with "id" and replace any instances of a string "old" with the string "new". I had this working from the command line with the following.

find \( -name '*.md' -o -name '*.html' \) -exec sed -i '/id:/s/old/new/g' '{} \;

However, I need to run the command from a Makefile. I have never done anything with make before. When I drop this same command into the Makefile and try to execute it from there, it fails. That's when I realized how little I know about make because I naively thought if it worked from the command line it would work from make. I was wrong. So I was looking in this Makefile for some examples of sed commands that do something similar and I came up with the following. This does not error out but it also does not do anything to my files. So, I am at a loss. Any help is appreciated!

switch_old_for_new:
    find \( -name '*.md' -o '*.html' \) -exec sed -i 's#^\(id: \)$(OLD)#\1$(NEW)#' '{}' \; 

NOTE: as you can probably see, I need to be able to pass in two actual values for "old" and "new" from the command line, so I also need to have variables in the sed. So I would execute it like this:

make switch_old_for_new OLD=old NEW=new
ehmon
  • 131
  • 8
  • 2
    variables are not expanded within single quotes, see https://stackoverflow.com/questions/7680504/sed-substitution-with-bash-variables ... also, I think in makefile, you need to use `$$` instead of `$` – Sundeep Jan 25 '18 at 03:51
  • 2
    also, since you are passing variables, you might not know their contents, which could clash with sed metacharacters.. see https://stackoverflow.com/questions/29613304/is-it-possible-to-escape-regex-metacharacters-reliably-with-sed – Sundeep Jan 25 '18 at 03:52
  • 2
    You changed `id:` to `\(id: \)` which doesn't need the parentheses and also requires a space after the colon. You factored it into the substitution instead of matching it anywhere on a line. This could be insignificant, but it also could not. – tripleee Jan 25 '18 at 05:47
  • 1
    The comment regarding quoting (first comment) is not correct. The author is trying to use _make_ variables inside their recipe, not shell variables (see the example make command line which sets the make variables). So they do NOT want to escape the `$`. Also, make doesn't care one whit about quotes so it's fine to use single-quotes here instead of double quotes. Your point about sed metacharacters is valid although perhaps the author knows what the possible content will be. – MadScientist Jan 25 '18 at 17:06
  • 1
    It would be very helpful if, instead of the generic "it fails", you provided information on what exactly happened. Did you get error messages? What were they? Did you see the command printed by make? Was there any other output? – MadScientist Jan 25 '18 at 17:08
  • Thanks for the thoughts! It happens that I do know the contents of the sed but that's a good point still because I might not. Also good info about the double dollar signs as I had seen some mentions that you needed them but @MadScientist explains this distinction very well. Thanks I am all good with the answer below! – ehmon Jan 26 '18 at 02:21

1 Answers1

2

It seems it was late and you ran out of coffee when copying the command line to make ;) The only thing that was fishy in your first example was a superfluous ' right before {}. All other things run unchanged in make. In a recipe the \ has no special meaning to make, that is, if make finds it in a tabulator-preceded line after a target: then it should really run verbatim to the solo command line. The only notable exception is a \ right before the line-break, i.e. something like:

target:
    echo a very long \
    line with a \+newline in it

In this case make will take the \(newline) as indication that it shall pass the current line together with next line (and all subsequent \(newline) concatenated) to the shell in one call instead of separate shell calls for each recipe line in the default case. (Note: only the tab but not the \(newline) will be deleted from the string given to the shell - you need to trick around with variables a bit if that \(newline gets in the way.) Also, all types of quoting characters '," and also the back-tick (which SO won't allow me to write in syntax font) as well as glob-characters *,? don't invoke any kind of special behaviour - they are passed to the shell as they are. So your make file could look like:

switch_old_for_new:
    find . \( -name '*.md' -o -name '*.html' \) -exec sed -i '/id:/s/$(OLD)/$(NEW)/g' {} \;
Vroomfondel
  • 2,156
  • 1
  • 12
  • 24
  • 1
    I had the actual command on another laptop so I manually typed it and made an error in omitting the second ' character. The actual command did have it. It was late though and I was missing coffee LOL thanks!! – ehmon Jan 26 '18 at 02:23