2

I have a file like below:

some
arbitrary number of
leading
lines
a prefix followed by wmd v0.0.0-20220406135915-ce5e3ee6c6bf
some
trailing
lines

This is only an example of what the file could look like. The unvarying parts are that, for the line I am interested in:

  • There is always wmd v0.0.0-, followed by
  • 14 digits, followed by
  • a hyphen, followed by
  • 12 alphanumeric characters

How can I write a sed command that will allow me to replace the 20220406135915-ce5e3ee6c6bf portion with the value in a shell variable new_text?

In other words, if new_text had the value 99999999999999-aaaaaaaaaaaa, I want to find the <whatever goes here> part of the sed command that would produce the following output:

$ sed -e "s/wmd v0.0.0-<whatever goes here>/wmd v0.0.0-$new_text/" my-file.txt
some
arbitrary number of
leading
lines
a prefix followed by wmd v0.0.0-99999999999999-aaaaaaaaaaaa
some
trailing
lines
2
  • Would using .* for <whatever goes here> be too generic? Your example input doesn't reveal whether it could possibly match somewhere else.
    – Kusalananda
    May 13 at 19:37
  • @Kusalananda, .* might not be too generic, but I prefer to be less generic just in case. Plus, I wanted to understand how to specify quantifiers on character classes with sed. May 13 at 19:43

2 Answers 2

5

You can use the \{..\} quantifiers to specify how many times a character class should match.

sed -e "s/wmd v0\.0\.0-[0-9]\{14\}-[0-9a-f]\{12\}/wmd v0.0.0-$new_text/"
#               ~  ~   ~~~~~~~~~~~~~~~~~~~~~~~~~~

Also note that a dot has a special meaning in regular expressions. Backslash it to match literally.

Also note that if $new_text contained a slash or some other characters special to sed, the command can break.

3
  • 1
    For lovers of mnemonics, [[:xdigit:]] will match a hexadecimal digit, lower or upper case.
    – Kusalananda
    May 13 at 19:50
  • 1
    and for those who don't love backslashes, most seds support sed -E for extended regexes, which would allow using {14} and {12} without the backslashes
    – ilkkachu
    May 13 at 22:10
  • Bash parameter expansion can launder $new_text to make it suitable as a replacement string, e.g. with replacement=${new_text//'\\/\\\\}; replacement=${replacement//&/\\&}; replacement=${replacement//\//\\/}. For plain POSIX shell, we need a command substitution: replacement=$(echo $new_text|sed 's,[\\/&],\\&,g') May 14 at 8:14
1

Using Raku (formerly known as Perl_6)

~$ raku -pe 's/ "wmd v0.0.0-" <( \d**14 \- \w**12 )> /99999999999999-aaaaaaaaaaaa/'  my-file.txt

#OR

~$ raku -pe 's[ "wmd v0.0.0-" <( \d**14 \- \w**12 )> ] = "99999999999999-aaaaaaaaaaaa";'  my-file.txt

Sample Input:

some
arbitrary number of
leading
lines
a prefix followed by wmd v0.0.0-20220406135915-ce5e3ee6c6bf
some
trailing
lines

Sample Output:

some
arbitrary number of
leading
lines
a prefix followed by wmd v0.0.0-99999999999999-aaaaaaaaaaaa
some
trailing
lines

The above regex uses 4 regex atoms to identify the match, "wmd v0.0.0-", \d**14, \-, and \w**12. Of course, if you have a problem with quotes, "wmd v0.0.0-" can be written wmd \s v0 \. 0 \. 0 \- (i.e. all non-alnum characters are backslash-escaped).

Capture markers <( … )> are used to drop everything outside of the \d**14 \- \w**12, which gets replaced in the right half of the s/// operator.

Note, in the above code \w is shorthand for <alnum>, which includes _ underscore. If you don't want to accept _ underscore (indeed, potentially 12 consecutive _ underscore characters), use something like <[0..9a..z]>**12 instead.

Finally, you can simplify your substitutions by abstracting the replacement into a variable, like so:

~$ raku -pe 'my $new = "99999999999999-aaaaaaaaaaaa"; \
             s/ "wmd v0.0.0-" <( \d**14 \- <[0..9a..z]>**12 )> /$new/;'  my-file.txt

https:raku.org

2
  • While I am not dismissing raku, answers using more common Linux utilities might be more helpful. I've been using perl since at least perl 4 so perl solutions are acceptable but allowing for versions that are generally included in mainstream distributions would be more helpful.
    – doneal24
    May 13 at 21:43
  • @doneal24 I've put up answers in the U&L Forum including both Perl and Raku solutions. In this particular case I felt most sure of my Raku answer. Now, I'll let Perl experts chime in with a nice Perl solution :-). May 13 at 21:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.