Find unique string within a file, up a line and append?

Tag: linux , bash , sed , awk Author: shc382195453 Date: 2012-12-03

Can someone please help me with this scenario? I'm looking for a SED or AWK command that I can use to find a unique string within a config file (Linux), go up a line and append a string to the end of that line?

For example:

config file:

define hostgroup{
hostgroup_name http-urls ; The name of the hostgroup
alias HTTP URLs ; Long name of the group
members domain1.com, domain2.com, domain3.com,
#MyUniqueString
}

In the above example, I'd like to use SED or AWK to find #MyUniqeString, go up a line that starts with members and append "domain4.com" at the end of the line.

I found this question below but I need to search the text file first for the string, and go one line above.

Bash script: Appending text at the last character of specific line of a file

Any suggestions?

Best Answer

try this one-liner:

awk '{a[NR]=$0}/#MyUniqueString/{a[NR-1]=a[NR-1]"domain4.com"}END{for(i=1;i<=NR;i++)print a[i]}' configFile

test

kent$  cat test.txt 
define hostgroup{
hostgroup_name http-urls ; The name of the hostgroup
alias HTTP URLs ; Long name of the group
members domain1.com, domain2.com, domain3.com,
#MyUniqueString
}

kent$  awk '{a[NR]=$0}/#MyUniqueString/{a[NR-1]=a[NR-1]"domain4.com"}END{for(i=1;i<=NR;i++)print a[i]}' test.txt 
define hostgroup{
hostgroup_name http-urls ; The name of the hostgroup
alias HTTP URLs ; Long name of the group
members domain1.com, domain2.com, domain3.com,domain4.com
#MyUniqueString
}

comments:

Thank you for this - I tried some of the others and this one best fits the question and works as you've illustrated.

Other Answer1

You can do this effectively with ed:

ed yourfile <<-'EOF'
    /#MyUniqueString/ # Find the matching line
    - # Go up a line
    a # Append text
    domain4.com
    . # Stop appending
    .-1,.j # Join the line above with the appended line
    w # Write the line
EOF

comments:

Sorry, didn't work.
Oh, it works. The comments are for your benefit, not for pasting.
Apologies, your solution does work. Thank you for posting.

Other Answer2

here's another sed solution using backreferences:

sed '{N; N; s/\(.*\)\n\(#MyUniqueString\)/\1domain4.com\n\2/g}' config.file

comments:

Tried this one too, didn't work for me.
that's odd... it works for your sample data. oh well, as long as you got the result you wanted somehow.

Other Answer3

Smaller, using sed:

sed -e ':a;N;/\n#MyUniqueString/{s/\n/ domain4.com\n/};H;s/\n.*$//;p;g;s/^.*\n//;ta;' config.file

This could be run as:

sed -e '
   :a;
    N;
    /\n#MyUniqueString/{
        s/\n/ domain4.com\n/
    };
    H;
    s/\n.*$//;
    p;
    g;
    s/^.*\n//;
    ta;
   ' config.file

comments:

Sorry, this one didn't work for me.
What's your environment? (I'm running GNU sed version 4.2.1 on Debian GNU/Linux 6.0.5)

Other Answer4

This awk solution only requires the previous line is remembered

awk '
    /#MyUniqueString/ {prev = prev "domain4.com"} 
    NR > 1 {print prev}
    {prev=$0}
    END {print prev}
'

Other Answer5

Why not pure bash ?

As there is *no fork, this is very quick (if from bash):

readarray configFile < ./config.file
for ((i=${#configFile};i--;));do
    [[ "${configFile[i]}" =~ "#MyUniqueString" ]] && break
  done
configFile[i-1]+=" domain4.com"
printf "%s\n" "${configFile[@]//$'\n'/}"

could be run by:

time {
    readarray configFile < ./config.file
    for ((i=${#configFile};i--;));do
        [[ "${configFile[i]}" =~ "#MyUniqueString" ]] && break
      done
    configFile[i-1]+=" domain4.com"
    printf "%s\n" "${configFile[@]//$'\n'/}"
}

This give:

define hostgroup{
hostgroup_name http-urls ; The name of the hostgroup
alias HTTP URLs ; Long name of the group
members domain1.com, domain2.com, domain3.com, domain4.com
#MyUniqueString
}

real    0m0.001s
user    0m0.000s
sys     0m0.000s

when

time sed ':a;N;/\n#MyUniqueString/{s/\n/ domain4.com\n/};H;s/\n.*$//;p;g;s/^.*\n//;ta;' config.file 
define hostgroup{
hostgroup_name http-urls ; The name of the hostgroup
alias HTTP URLs ; Long name of the group
members domain1.com, domain2.com, domain3.com, domain4.com
#MyUniqueString
}

real    0m0.010s
user    0m0.000s
sys     0m0.008s

and

time awk '{a[NR]=$0}/#MyUniqueString/{a[NR-1]=a[NR-1]" domain4.com"}END{for(i=1;i<=NR;i++)print a[i]}' config.file 
define hostgroup{
hostgroup_name http-urls ; The name of the hostgroup
alias HTTP URLs ; Long name of the group
members domain1.com, domain2.com, domain3.com, domain4.com
#MyUniqueString
}

real    0m0.009s
user    0m0.004s
sys     0m0.000s