2

I am trying to get the version of a npm package, and use it to replace the value in another file. Specifically, I want to get the package.json version and use that to replace the wordpress style.css version.

I'm able to successfully get the version number using npm pkg get version | sed 's/"//g', but I've tried every variation to get it to put in the output of that into the s of another piped sed. (I've tried -e -r -i and all combinations therein, I've tried $1, \1, ${echo $1}, etc...)

npm pkg get version | sed 's/"//g' | sed "s/1.0.0/$1/g" dist/style.css (in the final version I would use -i in the second sed)

In the style.css file, I want to replace 1.0.0 with the output of npm pkg get version | sed 's/"//g'

Sparticuz
  • 23
  • 3
  • *'echo | sed file'* can't work, you cannot process both inputs together. either process pipe or file. what is `$1` and how did you set value? – alecxs Sep 13 '21 at 16:58

1 Answers1

2

You can't pipe variables to sed. It doesn't matter how you're writing the back-reference – there is literally no feature in sed that would actually produce the back-reference in the way that you're trying to do.

(sed only uses stdin to take the actual input text, and if you're specifying a file to edit (style.css), then stdin is not being used at all.)

When using $ inside a double-quoted string, however, it's not expanded by sed itself – it's expanded by your shell, well before sed runs. For back-references that's not good (one would use \$ to cause an actual $ to be passed to the command); but in your case, that's exactly the shell feature you need.

Instead of trying to connect both with a pipe (the 2nd sed will not accept anything piped into it), use two separate pipelines and the shell's variable substitutions:

  1. Store the result from npm in a shell variable using $(command) or `command`:

    version=$(npm pkg get version | sed 's/"//g')
    

    (It would be slightly more "technically correct" to use jq -r . for unquoting npm's JSON output, but this is just a version string, so your sed will be good enough.)

  2. Include the variable as part of the sed command:

    sed -i "s/1\.0\.0/$version/g" dist/style.css
    

    Beware that this is expanded literally, e.g. if the version was 4.2beta, then the actual command that'll be run is sed -i "s/1\.0\.0/4.2beta/g" – this is important if sed's / separator may occur inside the variable. (In your case, it hopefully won't.)

  3. It's possible to merge both steps, e.g. sed -i "s/old/$(npm get | sed)/g" file, but that soon becomes nigh-unreadable, so don't do that.

  4. If you have node.js anyway, think about writing a node.js script that does the editing instead of using fragile shell tools.

u1686_grawity
  • 426,297
  • 64
  • 894
  • 966
  • *printf -v version '%q' "$(npm pkg get version | sed 's/"//g')"* should escape all regex characters – alecxs Sep 13 '21 at 17:08
  • 1
    Not all of them; e.g. it wouldn't escape `.`, and depending on which regex syntax you're using, it would _needlessly_ escape some of them (e.g. in BRE, plain `(` is not special, but escaped `\(` actually is). In this case though that's not needed because the variable is only used as replacement string, not as the match regex, so only backreferences (`&` and backslash) and the sed command separator (in this case `/`) need escaping. – u1686_grawity Sep 13 '21 at 17:20
  • Thank you for the information. While I totally agree with your #3 (I very much dislike nested code that is difficult to parse, especially regex...it's the reason I write in Typescript), it is the solution I ended up using. The reason being is that I needed a one-liner to put into an npm script that could get run automatically on build. I suppose writing a js file that could be run would have also worked, but that (as well as `jq`) is an added dependency. `sed -i "s/1.0.0/$(npm pkg get version | sed 's/"//g')/g" dist/style.css` (EDIT: the versioning for this project is always going to be #.#.#) – Sparticuz Sep 13 '21 at 18:37