3

I have a food.txt file as follows :-

mangoes|foo|faa  
oranges|foo|faa  
chocolates|foo|baz  

I am trying to replace foo with bar if condition baz is met. (would like to use a regular expression here b*z )
Currently using the sed command below, but this does not directly modify the existing file. I am also not able to use a regular expression (b*z).

sed '/baz/s/foo/bar/g' food.txt

Please suggest a way other than "sed" to modify the existing file directly.

PS : I tried sed -i but I would like to use some other command than sed.
I am using mobaxterm (OS - Windows)

  • 1
    Do you mean b*z (0 or more b followed by a z) or b.*z (a b followed by 0 or more characters and then a z)? – terdon Aug 15 '16 at 10:57
  • b.*z (0 or more characters). Dint know about b.*z regexp – jasmin Aug 15 '16 at 11:03
2

The actual best tool for automated text edits is ex.

Although it should be possible to call ex directly, I have found in MobaXterm that I have to call vim -e instead.

So, the best way to do this automated edit in MobaXterm (and which will also work on other *nix systems) is:

printf '%s\n' 'g/b.*z/s/foo/bar/g' x | vim -es food.txt

To be fully POSIX compliant, it is only necessary to alter it to:

printf '%s\n' 'g/b.*z/s/foo/bar/g' x | ex -s food.txt

However, calling the ex command may not work correctly on MobaXterm (it doesn't on my installation.) Try the vim -es version of the command if the ex version fails.

  • Thank you. ex -s is working for me on Mobaxterm. Can u please explain the code. I understand that %s stands for search but not able to grasp the rest of it.. – jasmin Aug 16 '16 at 10:38
  • @jasmin, ironically enough, the %s doesn't stand for search but for "string." :) I'll add an explanation soon, but in the meantime if you start with this answer and also read the links you will learn a fair bit more about ex. – Wildcard Aug 16 '16 at 17:22
7

I see nothing wrong with using sed in this case. It's the right tool for the job.

Your command works well (on the given data):

$ sed '/baz/s/foo/bar/g' food.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

Using a regular expression to match any string beginning with |b and ending with z at the end of the line (instead of baz anywhere on the line):

$ sed '/|b.*z$/s/foo/bar/g' food.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

To make the change to the file (with GNU sed):

$ sed -i '/|b.*z$/s/foo/bar/g' food.txt

or (with any sed implementation):

$ sed '/|b.*z$/s/foo/bar/g' food.txt >tmpfile && mv tmpfile food.txt
  • 2
    @terdon Thanks for the edit. The reason I didn't use sed -i was that it works very differently between GNU and BSD sed. Just saying sed -i script will use script as a backup suffix with BSD sed. Doing sed ... && mv is safer and more portable. – Kusalananda Aug 15 '16 at 10:58
  • Ah, fair point, that's very true. In any case, it turns out the OP is using mobaxterm so I have no idea what sed flavor will be available. Feel free to roll my edit back. – terdon Aug 15 '16 at 11:00
  • @terdon No worries, I added extra comments instead. – Kusalananda Aug 15 '16 at 11:01
  • 1
    @Daenyth, no it's not. On GNU sed you will get sed: can't read (pattern): No such file or directory The only way to use -i portably between GNU and BSD sed is to specify a non-empty extension after -i with no space between the i and the extension, e.g. -i.bak. – Wildcard Aug 15 '16 at 20:00
4

Using awk with gsub():

awk '/baz$/ {gsub("foo", "bar")};1' food.txt
  • Use any Regex pattern to match instead of /baz$/, if you want

  • if the pattern matches, do gsub() to substitute desired strings


For inpace editing, Recent version of GNU awk (>=4.1.0) has inplace modification option:

awk -i inplace '/baz$/ {gsub("foo", "bar")};1' food.txt

Otherwise you can use sponge from GNU moreutils or use a temporary file:

awk '/baz$/ {gsub("foo", "bar")};1' food.txt >temp_food.txt && \
      mv temp_food.txt food.txt

Example:

$ cat file.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|foo|baz

$ awk '/baz$/ {gsub("foo", "bar")};1' file.txt
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz
  • Thanks for the reply... but i am getting an illegal statement error while executing the awk command. – jasmin Aug 15 '16 at 10:55
  • @jasmin on what operating system? – terdon Aug 15 '16 at 10:56
  • Windows- using mobaxterm. – jasmin Aug 15 '16 at 10:58
  • @jasmin um. Please edit your question and add that information then. That sort of changes everything since only specific tools and specific versions of those tools will be available to you. – terdon Aug 15 '16 at 10:59
3

While I have no idea why you don't want to use sed -i which does exactly what you need, another option would be perl:

$ perl -pe 's/foo/bar/g if /b*z/' food.txt 
mangoes|foo|faa  
oranges|foo|faa  
chocolates|bar|baz

The -pe means "print every line of the input file after applying the script to it.

And you can use -i to edit the file in place:

perl -i -pe 's/foo/bar/g if /b*z/' food.txt 

Also, note that the regular expression b*z means "match 0 or more b followed by a z. It will work here because b*z matches bar by ignoring b and a and just matching z. In other words, it will match any z since any z will be an example of 0 b followed by a z. I think what you probably mean to use is b.*z (a b followed by 0 or more characters and then a z):

perl -i -pe 's/foo/bar/g if /b.*z/' food.txt 

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.