AKS
AKS

Reputation: 17326

replace multi regex or words by sed one liner

Linux RH 5.11

GNU sed version 4.1.5

I have the following file where I want to replace the value of + with another (version) value for all lines where the KEY value starts with either ABC, DEF or XYZ by running one command (using sed group words capability while doing word wrap/regex based matches).

linux_user@linux_server123 [ ~ ] 18:37:18 :9152> cp ~/my-file.json ~/BKUP-my-file.json; cat ~/my-file.json 
{
  "versions": {
    "ABC_PROJECT_Product": "+",
    "IGNORE1_PROJECT_Product": "1.8.0.1371",
    "DEF_PROJECT_Product": "+",
    "XYZ_PROJECT_Product": "+",
    "IGNORE2_PROJECT_Product": "1.1.0.830",

    "ABC_PROJECTGlobal": "+",
    "DEF_PROJECTGlobal": "+",
    "IGNORE2_PROJECTGlobal": "1.1.0.830",

    "ABC_PROJECTGlobalSSD": "+",
    "DEF_PROJECTGlobalSSD": "+",

    "ABC_PROJECT_ProductSSD": "+",
    "IGNORE3_PROJECT_ProductSSD": "1.0.0.4913",
    "DEF_PROJECT_ProductSSD": "+",

    "ABC_PROJECTLocalREBS": "+",
    "IGNORE4_PROJECTLocalREBS": "1.1.0.865",

    "ABC_PROJECT_ProductODNS": "+",
    "IGNORE3_PROJECT_ProductODNS": "1.0.0.4913",
    "DEF_PROJECT_ProductODNS": "+",

    "ABC_PROJECT_ProductIDNS": "+",
    "DEF_PROJECT_ProductIDNS": "+",
    "IGNORE2_PROJECT_ProductIDNSS": "1.1.0.830",

    "ABC_PROJECTGlobalIDNS": "+",
    "DEF_PROJECTGlobalIDNS": "+",

    "ABC_PROJECTGlobalODNS": "+",
    "DEF_PROJECTGlobalODNS": "+",

    "ABC_PROJECTLocalSpecial": "+",
    "IGNORE4_PROJECTLocalSpecial": "1.1.0.865",

    "ABC_PROJECT_ProductSpecial": "+",
    "IGNORE5_PROJECT_ProductSpecial": "2.1.0.683",
    "DEF_PROJECT_ProductSpecial": "+",

    "ABC_PROJECTGlobalSpecial": "+"
  }
}

.

cp ~/BKUP-my-file.json ~/my-file.json; 
sed_regex="\(ABC\|DEF\|XYZ\)"; 
sed -i "s/\(.*\"${sed_regex}_PROJECT.*\".*:.*\"\).*\(\".*\)/\11.22.333.4444\2/" ~/my-file.json; 
sed -n "/.*\(ABC\|DEF\|XYZ\)_PROJECT.*/p" ~/my-file.json 
    "ABC_PROJECT_Product": "1.22.333.4444ABC
    "DEF_PROJECT_Product": "1.22.333.4444DEF
    "XYZ_PROJECT_Product": "1.22.333.4444XYZ
    "ABC_PROJECTGlobal": "1.22.333.4444ABC
    "DEF_PROJECTGlobal": "1.22.333.4444DEF
    "ABC_PROJECTGlobalSSD": "1.22.333.4444ABC
    "DEF_PROJECTGlobalSSD": "1.22.333.4444DEF
    "ABC_PROJECT_ProductSSD": "1.22.333.4444ABC
    "DEF_PROJECT_ProductSSD": "1.22.333.4444DEF
    "ABC_PROJECTLocalREBS": "1.22.333.4444ABC
    "ABC_PROJECT_ProductODNS": "1.22.333.4444ABC
    "DEF_PROJECT_ProductODNS": "1.22.333.4444DEF
    "ABC_PROJECT_ProductIDNS": "1.22.333.4444ABC
    "DEF_PROJECT_ProductIDNS": "1.22.333.4444DEF
    "ABC_PROJECTGlobalIDNS": "1.22.333.4444ABC
    "DEF_PROJECTGlobalIDNS": "1.22.333.4444DEF
    "ABC_PROJECTGlobalODNS": "1.22.333.4444ABC
    "DEF_PROJECTGlobalODNS": "1.22.333.4444DEF
    "ABC_PROJECTLocalSpecial": "1.22.333.4444ABC
    "ABC_PROJECT_ProductSpecial": "1.22.333.4444ABC
    "DEF_PROJECT_ProductSpecial": "1.22.333.4444DEF
    "ABC_PROJECTGlobalSpecial": "1.22.333.4444ABC

Question 1:

Why sed is putting ABC or DEF or XYZ in place of the actual \2 value, which I understand should be either: ", or just: " i.e. line entry in JSON object (with/without any space/tabs) in that line?

Question 2: How can I get "ABC_PROJECT_Product": "+", value as:

"ABC_PROJECT_Product": "1.22.333.4444",

Question 3: Any why the heck using this sed fixes question 1 & 2 both (apart from making it faster duh).

sed -i "/.*\"${sed_regex}_PROJECT.*\"/ s/\(.*\".*\".*:.*\"\).*\(\".*\)/\11.22.333.4444\2/" ~/my-file.json;

May be awk can do this easily?

Upvotes: 0

Views: 290

Answers (2)

Ed Morton
Ed Morton

Reputation: 203324

@cdub answered your 3 questions but is this what you're trying to do?

$ sed -E '/"(ABC|DEF|GHI)_PROJECT/ s/[+]/11.22.333.4444/' file
{
  "versions": {
    "ABC_PROJECT_Product": "11.22.333.4444",
    "IGNORE1_PROJECT_Product": "1.8.0.1371",
    "DEF_PROJECT_Product": "11.22.333.4444",
    "XYZ_PROJECT_Product": "+",
    "IGNORE2_PROJECT_Product": "1.1.0.830",

    "ABC_PROJECTGlobal": "11.22.333.4444",
    "DEF_PROJECTGlobal": "11.22.333.4444",
    "IGNORE2_PROJECTGlobal": "1.1.0.830",

    "ABC_PROJECTGlobalSSD": "11.22.333.4444",
    "DEF_PROJECTGlobalSSD": "11.22.333.4444",

    "ABC_PROJECT_ProductSSD": "11.22.333.4444",
    "IGNORE3_PROJECT_ProductSSD": "1.0.0.4913",
    "DEF_PROJECT_ProductSSD": "11.22.333.4444",

    "ABC_PROJECTLocalREBS": "11.22.333.4444",
    "IGNORE4_PROJECTLocalREBS": "1.1.0.865",

    "ABC_PROJECT_ProductODNS": "11.22.333.4444",
    "IGNORE3_PROJECT_ProductODNS": "1.0.0.4913",
    "DEF_PROJECT_ProductODNS": "11.22.333.4444",

    "ABC_PROJECT_ProductIDNS": "11.22.333.4444",
    "DEF_PROJECT_ProductIDNS": "11.22.333.4444",
    "IGNORE2_PROJECT_ProductIDNSS": "1.1.0.830",

    "ABC_PROJECTGlobalIDNS": "11.22.333.4444",
    "DEF_PROJECTGlobalIDNS": "11.22.333.4444",

    "ABC_PROJECTGlobalODNS": "11.22.333.4444",
    "DEF_PROJECTGlobalODNS": "11.22.333.4444",

    "ABC_PROJECTLocalSpecial": "11.22.333.4444",
    "IGNORE4_PROJECTLocalSpecial": "1.1.0.865",

    "ABC_PROJECT_ProductSpecial": "11.22.333.4444",
    "IGNORE5_PROJECT_ProductSpecial": "2.1.0.683",
    "DEF_PROJECT_ProductSpecial": "11.22.333.4444",

    "ABC_PROJECTGlobalSpecial": "11.22.333.4444"
  }
}

The above requires GNU (or OSX/BSD) sed as you're already using for -E. Alternatively this will work with any awk in any shell on every UNIX box:

awk '/"(ABC|DEF|XYZ)_PROJECT/{sub(/[+]/,"11.22.333.4444")} 1' file

Upvotes: 1

cdub
cdub

Reputation: 2297

Question #1:

The sed string has a nested grouping, so \2 refers to \(ABC\|DEF\|XYZ\). That's why we see ABC, DEF, etc. at the end of each line. We can more clearly see the nesting by re-writing the sed string with the sed_regex variable substituted:

sed "s/\(.*\"\(ABC\|DEF\|XYZ\)_PROJECT.*\".*:.*\"\).*\(\".*\)/\11.22.333.4444\2/"

Question#2:

Something like this could work:

sed_regex="\(ABC\|DEF\|XYZ\)"; sed "s/$sed_regex\(_PROJECT.*\)\("+"\)/\1\211.22.333.4444/g"

Question #3:

The revised sed string in the OP doesn't use a nested grouping and the regex appears to properly match the pattern.

Upvotes: 1

Related Questions