2

I have an xml file that look something like this

<Process label="TRAM_FIT_TM_SERVER" machine="&HOSTNAME_TRAM;">
        <Description>Monitor that TM SERVER stays up</Description>
        <Enabled>true</Enabled>
        <ProcessCheck method="ps">
            <ProcName>.*bin/tmSrv -serverMode=tm</ProcName>
        </ProcessCheck>
        <Cmd>cd /ctec/apps/fotms/6.2/scripts/; ./tradeFlow.sh fitdev start tm > &LOGLOC;/fotms/logs/starttm.log</Cmd>
        <KillCmd>pkill -u &USER; -f 'bin/tmSrv -serverMode=tm'</KillCmd>
        <Count>1</Count>
        <User>&USER;</User>
            &EMAIL_SUPPORT;
            &TRAM_SCHEDULE;
</Process>

There are about 40 odd of the processes, all with the same exact layout. I am able to read through the file in a while loop, stop at this specific Process using its Process label. Then I am able to grab the Enabled line, which is what I need to change like so.

while read line
    do
       if [[ "$line" == *"TRAM_FIT_TM_SERVER"* ]]
       then
           echo ...
           var_checker=2
       fi
       if [[ "$line" == *"Enabled"* ]]
       then
           if [ "$var_checker" == 2 ]
           then
               #change value to false
               #sed -i 's/true/false/g' $line
               var_checker=1
               echo "Changed trade server"
               break 3
           fi
       fi
done <fit.core_tram.procmon.xml

My question is, how do I change the Enabled value of this process, and ONLY for this process to false. I need to use sed or grep if possible, and I unfortunately can't just do sed -i 's/true/false/g' filename due to the fact there being multiple occurrences of that exact setup. Any help would be appreciated

  • 1
    "How do I use `sed` to [do some kind of XML manipulation]?" is a question that's asked and answered over and over and over in our knowledge base. The answers generally come down to "don't". – Charles Duffy Jul 01 '16 at 14:52
  • Okay that's not particularly helpful but thanks. What would be other options for me to achieve the same result? I know of xmllint but not how it would be implemented here –  Jul 01 '16 at 14:56
  • See my answer. Or the answers on any of the many duplicative questions. http://stackoverflow.com/questions/13369933/replace-dynamic-content-in-xml-file, f'rinstance – Charles Duffy Jul 01 '16 at 14:58
  • ...or http://stackoverflow.com/questions/30011637/replacing-a-value-in-a-xml-file-for-a-particular-tag – Charles Duffy Jul 01 '16 at 14:58
  • BTW, right this moment your XML doesn't parse because it includes references to entities (like `&HOSTNAME_TRAM;`, `&USER;`, etc) which it doesn't define. Any chance of expanding the example to be more complete? – Charles Duffy Jul 01 '16 at 15:06
  • By the way -- `[ "$var_checker" == 2 ]` should be either `[ "$var_checker" = 2 ]` or `[[ $var_checker == 2 ]]` -- the POSIX standard for `test` only defines `=` as a string comparison operator, so `==` is relying on a local extension. See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html – Charles Duffy Jul 01 '16 at 15:27

1 Answers1

4

sed is the wrong tool for this job; use an XML-aware tool such as XMLStarlet:

xmlstarlet ed \
  -u '//Process[@label="TRAM_FIT_TM_SERVER"]/Enabled' \
  -v false \
  <in.xml >out.xml

This will modify the value for Enabled for only a process with the exact label TRAM_FIT_TM_SERVER to false.


If you can't install XMLStarlet, consider taking advantage of the ubiquity of Python:

# note that since we're using ElementTree, not lxml.etree, this isn't "real" XPath
# ...however, it's good enough for your expression here.
#
# Switch to "import lxml.etree as etree" if you have the Python lxml package installed
# and want a more flexible syntax.
edit_value() {
  local xpath=$1
  local value=$2
  python -c '
import sys
import xml.etree.ElementTree as etree

xpath=sys.argv[1]
value=sys.argv[2]

root = etree.parse(sys.stdin)
for el in root.findall(xpath):
  el.text = value
root.write(sys.stdout)
' "$xpath" "$value"
}

...thereafter used as:

edit_value '//Process[@label="TRAM_FIT_TM_SERVER"]/Enabled' false \
  <in.xml >out.xml

That said, to test either of these answers you need a version of your document that's complete enough to parse -- the original, as given, uses entities that it doesn't define. My answers above are tested against the following:

<!DOCTYPE opdef [
<!ENTITY HOSTNAME_TRAM "hostname">
<!ENTITY LOGLOC "logloc">
<!ENTITY USER "user">
<!ENTITY EMAIL_SUPPORT "support@example.com">
<!ENTITY TRAM_SCHEDULE "schedule">
]>

<root>
<Process label="TRAM_FIT_TM_SERVER" machine="&HOSTNAME_TRAM;">
        <Description>Monitor that TM SERVER stays up</Description>
        <Enabled>true</Enabled>
        <ProcessCheck method="ps">
            <ProcName>.*bin/tmSrv -serverMode=tm</ProcName>
        </ProcessCheck>
        <Cmd>cd /ctec/apps/fotms/6.2/scripts/; ./tradeFlow.sh fitdev start tm > &LOGLOC;/fotms/logs/starttm.log</Cmd>
        <KillCmd>pkill -u &USER; -f 'bin/tmSrv -serverMode=tm'</KillCmd>
        <Count>1</Count>
        <User>&USER;</User>
            &EMAIL_SUPPORT;
            &TRAM_SCHEDULE;
</Process>
</root>
Charles Duffy
  • 235,655
  • 34
  • 305
  • 356