2

I want to interchange 2 words from a file .

eg: In the below file I want to replace one with two and two with one

This is a test file one and two
test line again two
last line two one

I know I can achieve this with :

 sed 's/one/\n/g;s/two/one/g;s/\n/two/g' test.txt

But in the above approach I am assuming \n is not appearing in the line . So what will be the best way to proceed ?.

freedev
  • 25,946
  • 8
  • 108
  • 125
Mathews Jose
  • 399
  • 6
  • 18

4 Answers4

3

There will never [0] be a newline in the pattern space, as sed reads line by line.

It's however worth noticing that \n in the replacement part is not strictly supported with POSIX sed. In the pattern part \n is supported.

Reference: s/pattern/replacement/

[0] You can actually have multiply lines in the pattern space by using append commands like Next and Get. But you have to be explicit, so one could argue that newlines is the best choice for swapping values.

Andreas Louv
  • 46,145
  • 13
  • 104
  • 123
2

You don't need a regEx type replacement while using sed, just use the classic temporary string swap for replacement. (tmp=a;a=b;b=tmp;), works on any instances of the pattern on each line, assuming for an input file like that. To make sure the the temporary random tmp does not occur in your file, create a random string and assign it to tmp variable

$ tmp="$(openssl rand -base64 32)"
$ tmp="${tmp//[^[:alnum:]]/}"

and stripping off any non-alphanumeric characters which could harm normal sed operation.

$ cat file
This is a test file two and one two one two two two
test line again one two one two
last line one two two two

The sed logic takes care of all replacements

sed "s/two/$tmp/g;s/one/two/g;s/$tmp/one/g" file
This is a test file one and two one two one one one
test line again two one two one
last line two one one one
Inian
  • 80,270
  • 14
  • 142
  • 161
  • I guess this is same as what op has tired already. Also there is a possibility that `tmp` occur in the file. How will we come up with a temporary string which is not there in the file? – nu11p01n73R Dec 14 '16 at 07:17
  • @nu11p01n73R: Good point, have to use a random string which is supposedly not present in the file. – Inian Dec 14 '16 at 07:19
  • 1
    @nu11p01n73R: Add a logic to generate a random string. – Inian Dec 14 '16 at 07:35
2

You can do it using awk to loop over the entries in the line:

awk '{
        for (i=1;i<=NF;i++){
          printf "%s ", ($i=="one" ? "two" : $i=="two" ? "one" : $i); 
        };
        printf "\n";
      }'  file

In fact, as noted by user andlrc, this can be simplified further by

  1. Reassigning to the $i variables.
  2. Using the default operation, i.e. printing the current line, which can be triggered by any true value, e.g. 1.

Result:

awk '{for(i=1;i<=NF;i++){ $i=($i=="one"?"two": $i=="two"?"one": $i);}; 1}'  file
kalj
  • 1,432
  • 2
  • 13
  • 30
  • Is it more efficient than the sed way of doing in the other answers ? – Mathews Jose Dec 14 '16 at 10:14
  • 1
    This is not a bad solution. To avoid trailing space on each line one can reassign the fields: `awk '{ for (i=1;i<=NF;i++){ $i = ($i=="one" ? "two" : $i=="two" ? "one" : $i); } }; 1' file` – Andreas Louv Dec 14 '16 at 10:25
  • @andlrc, that was very nice. In that case, I guess the `$i` are references? You learn something new every day. What's up with the trailing `1`? It does seem to make it print the (modified) lines. – kalj Dec 14 '16 at 10:33
  • 1
    @kalj The trailing one is a shorthand for: `1 { print $0 }`. And since `1` is a truly value each line will be printed. – Andreas Louv Dec 14 '16 at 11:56
  • Interesting. I will modify the answer with this. Thanks! – kalj Dec 14 '16 at 12:56
1

sed works on a line-by line basis. You can make sed consider multiple lines by first getting multiple lines in the buffer which is considered for stream editing.

Search for sed replace '\n', or have a look at this

You may need to lookup pattern space, advanced sed and labels.

My general go-to sed guide is on this link

Community
  • 1
  • 1
tabs_over_spaces
  • 352
  • 1
  • 3
  • 14