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, 2022 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, 2022 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, 2022 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, 2022 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, 2022 at 8:14
0

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

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .