Replacing multiple strings with some strings containing "." and affinity to other string(s) using sed

Viewed 58

I've spent an hour each day searching for ways to use sed for replacing below sample data as practice. Hope someone could guide and correct me on using sed properly to achieve the right output.

sample.txt

User's name is adam
Account's name is adam_acc
User's name is adam.eve
Account's name is adam.eve_acc

Output trying to achieve:

User's name is user_1
Account's name is user_3
User's name is user_2
Account's name is user_4

Attempt tried:

$ sed 's/\<adam\>/user_1/g; 
s/\<adam\.eve\>/user_2/g; 
s/\<adam\_acc\>/user_3/g;
s/\<adam\.eve_acc\>/user_4/g' sample.txt

Attempt's output:

User's name is user_1
Account's name is user_3
User's name is user_1.eve
Account's name is user_1.eve_acc

Below is the bash version I am currently using:

GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)

Material which I had read for more info:

https://www.gnu.org/software/sed/manual/html_node/Regular-Expressions.html

https://www.gnu.org/software/sed/manual/html_node/sed-regular-expressions.html#sed-regular-expressions
3 Answers

Move the longer patterns to the beginning of sed command:

sed 's/\<adam\.eve_acc\>/user_4/g; s/\<adam_acc\>/user_3/g; s/\<adam\.eve\>/user_2/g; s/\<adam\>/user_1/g;' file

Else, \<adam\> finds adam in adam.eve and adam.eve_acc and replaces them before s/\<adam\.eve_acc\>/user_4/g comes into play.

Note there is no need to escape the _ char.

See an online demo:

s="User's name is adam
Account's name is adam_acc
User's name is adam.eve
Account's name is adam.eve_acc"
sed 's/\<adam\.eve_acc\>/user_4/g; s/\<adam_acc\>/user_3/g; s/\<adam\.eve\>/user_2/g; s/\<adam\>/user_1/g;' <<< "$s"

Output:

User's name is user_1
Account's name is user_3
User's name is user_2
Account's name is user_4

With awk you could try following once. Written and tested with your shown samples.

awk '
{
  for(i=1;i<=NF;i++){
    if($i~/^adam$/)         { $i="user_1" }
    if($i~/^adam_acc$/)     { $i="user_3" }
    if($i~/^adam\.eve$/)    { $i="user_2" }
    if($i~/^adam\.eve_acc$/){ $i="user_4" }
  }
}
1' Input_file

I have used regex to evaluate above values in fields you could directly use == also with values(to save escaping part etc)

Explanation: Adding detailed explanation for above.

awk '                                        ##Starting awk program from here.
{
  for(i=1;i<=NF;i++){                        ##Traversing through all fields here.
    if($i~/^adam$/)         { $i="user_1" }  ##Checking if field is adam then set it to user_1 here.
    if($i~/^adam_acc$/)     { $i="user_3" }  ##Checking if field is adam_acc then set it to user_3 here.
    if($i~/^adam\.eve$/)    { $i="user_2" }  ##Checking if field is adam.eve then set it to user_2 here.
    if($i~/^adam\.eve_acc$/){ $i="user_4" }  ##Checking if field is adam\.eve_acc then set it to user_4 here.
  }
}
1                                            ##Printing current line here.
' Input_file                                 ##Mentioning Input_file name here.

Looks like you're trying to mask bunch of user names. Rather than hardcoding those names and their replacement placeholder it would be much better to keep this mapping in a separate file like this:

cat mapping.txt

adam user_1
adam_acc user_3
adam.eve user_2
adam.eve_acc user_4

Then use this simple awk to get the job done:

awk 'NR==FNR {map[$1]=$2; next} $NF in map {$NF=map[$NF]} 1' mapping.txt sample.txt

User's name is user_1
Account's name is user_3
User's name is user_2
Account's name is user_4

Edited: 'NR==NR' to 'NR==FNR'

Related