4

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)

2
  • 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)? Commented Aug 15, 2016 at 10:57
  • b.*z (0 or more characters). Dint know about b.*z regexp Commented Aug 15, 2016 at 11:03

4 Answers 4

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

You could also use awk:

$ awk 'BEGIN { OFS=FS="|" } $3 == "baz" { $2 = "bar" }; 1' file
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

or matching ^b.*z$ in the 3rd field,

$ awk 'BEGIN { OFS=FS="|" } $3 ~ /^b.*z$/ { $2 = "bar" }; 1' file
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

... or Miller (mlr), here reading the input as a header-less CSV file that uses | as field delimiters:

$ mlr --csv -N --fs pipe put '$3 == "baz" { $2 = "bar" }' file
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

or,

$ mlr --csv -N --fs pipe put '$3 =~ "^b.*z$" { $2 = "bar" }' file
mangoes|foo|faa
oranges|foo|faa
chocolates|bar|baz

The benefit of using awk or Miller is that it's easier and safer to match a pattern against an isolated field. Miller has the added benefit of understanding CSV quoting rules.

3
  • 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. Commented Aug 15, 2016 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. Commented Aug 15, 2016 at 11:00
  • @terdon No worries, I added extra comments instead. Commented Aug 15, 2016 at 11:01
5

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
4
  • Thanks for the reply... but i am getting an illegal statement error while executing the awk command. Commented Aug 15, 2016 at 10:55
  • @jasmin on what operating system? Commented Aug 15, 2016 at 10:56
  • Windows- using mobaxterm. Commented Aug 15, 2016 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. Commented Aug 15, 2016 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 
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.

2
  • 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.. Commented Aug 16, 2016 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. Commented Aug 16, 2016 at 17:22

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.