0

I want to add time stamp inside the file only after matching specific string:

timestamp format: 03_2016

file contains "$channel_name Policy" string, I want to replace this string with "$channel_name Policy_03_2016"

content of file which I want to add timestamp:

package_path=/LSB/Packages/Policy
channel_path=/LSB/Channels/$parent_channel_name/$channel_name Policy
erratum_path=/LSB/Errata/$erratum_type Policies/$erratum_name
errata_path =/LSB/Errata/$parent_channel_name/$channel_name Advisory Roll-Up Policy
package_path=/LSB/Packages/Unapproved/$channel_name

Required file format:

package_path=/LSB/Packages/Policy
channel_path=/LSB/Channels/$parent_channel_name/$channel_name Policy_03_2016
erratum_path=/LSB/Errata/$erratum_type Policies/$erratum_name
errata_path =/LSB/Errata/$parent_channel_name/$channel_name Advisory Roll-Up Policy 
package_path=/LSB/Packages/Unapproved/$channel_name

Right now I'm using following command to add timestamp to file:

sed -i "s/$channel_name Policy/$channel_name Policy_$(date +%m_%Y)/g" filename

but this command is replacing all occurrences of Policy with Policy_03_2016 like this:

package_path=/LSB/Packages/Policy_03_2016
channel_path=/LSB/Channels/$parent_channel_name/$channel_name Policy_03_2016
erratum_path=/LSB/Errata/$erratum_type Policies/$erratum_name
errata_path =/LSB/Errata/$parent_channel_name/$channel_name Advisory Roll-Up Policy_03_2016
package_path=/LSB/Packages/Unapproved/$channel_name 

Is there any solution for this question?

1
  • 3
    Escape the $ in $channel (e.g. s/\$channel...) otherwise it will expand to nothing and your command will resolve to s/ Policy/ Policy_03_2016/g Commented Mar 24, 2016 at 11:37

2 Answers 2

2

$ is special to the shell within double quotes as it's used for parameter expansion (like in $var, $1, $channel_name), arithmetic expansion (like $((1+1))) and command substitution (like $(cmd) which you are actually using here).

$ is also special in regular expressions (like on the left-hand side of the s/regex/replacement/ sed command) as it's the operator that means "end of the subject", unless you use -E/-r to enable extended regular expressions, it's not special unless the $ is at the end of the regexp itself or followed by \) (or \| where supported). Still, it's a good habit to escape it.

Both the shell inside double quotes, and sed in its regexps support \ as an escaping operator to remove the special meaning of characters. \ can also escape itself in both cases.

So, here, you could do:

sed -i "s/\\\$channel_name Policy/\$channel_name Policy_$(date +%m_%Y)/g" filename

That is, \\\$ to escape \ itself and \$ for the shell so that \$ is passed to sed where \ escapes $ again. On the right hand side \$ is enough as $ is not special in the replacement part of the s/regexp/replacement/ command.

Though not POSIX, this would also work in any shell that I know:

sed -i "s/[$]channel_name Policy/&_$(date +%m_%Y)/g" filename

[$] is another way to escape $ in a regexp. [...] matches on a set of characters, here comprising only one: $. While for the shell [$] is unspecified by POSIX, I don't know of any shell that doesn't leave it as is. You'd need [\$] for that shell code to be POSIX compliant though.

We're also using & in the replacement which stands for the string that was matched by the regexp.

Another option would be to use single quotes for most of the string (inside which $ is not special), and double quotes for the command substitution, still escaping the $ in the regexp even though it's not strictly necessary:

sed -i 's/\$channel_name Policy/&_'"$(date +%m_%Y)/g" filename

Note that you need $(...) to be double quoted as otherwise split+glob would be applied on it.

1

As commented by 'don_crissti', you have to escape the dollar sign otherwise it tries to get a variable content (which is empty in your case).

Alternatively it would be safer if you use a different separator than '/' as you also have slashes in your file. However it is not an issue in your case as long you do not use them in the replacement expression.

My working solution:

sed -i 's!\$channel_name Policy!&_'$(date +%m_%Y)'!g' policies.txt

The second possible improvement is the usage of the & in the replacement as it is exactly the searched expression plus some extra.

And third, in my test I was only able to make the command $(date +%m_%Y) working when outside of the sed expression (wrapped by single quotes).

I tried in GNU sed (the above line) and with OSX sed as well but none of them where able to interpret properly the $(command).

1
  • Note that $(date +%m_%Y) ends up being unquoted here, so split+glob applies to it. That means it would fail in contexts where $IFS contains _ or decimal digits for instance. Commented Jun 26, 2020 at 8:55

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.