9

For the below xml ,I need to replace <studentStatus> for <studentName>CLASSA</studentName> to <studentStatus>failed</studentStatus>.

<studentFile>
    <student>
        <studentName>CLASSA</studentName>
        <studentStatus>Success</studentStatus>
        <studentActions>
            <studentAction>
                <studentType>Juniour</studentType>
                <studentStatus>Completed</studentStatus>
                <studentMsg/>
            </studentAction>
            <studentAction>
                <studentType>HighSchool</studentType>
                <studentStatus>Completed</studentStatus>
                <studentMsg/>
            </studentAction>
        </studentActions>
    </student>
    <student>
        <studentName>CLASSB</studentName>
        <studentStatus>Success</studentStatus>
        <studentActions>
            <studentAction>
                <studentType>Senior</studentType>
                <studentStatus>Completed</studentStatus>
            </studentAction>
            <studentAction>
                <studentType>Middle</studentType>
                <studentStatus>Completed</studentStatus>
            </studentAction>                         
        </studentActions>
    </student>
</studentFile>

What I got so far,

xmllint -xpath "/studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType="Juniour"]/studentStatus" myxml.xml

now i got the status of the student as Completed , now this value should be changed to Failed . Only for <studentType>Juniour</studentType>. How should I edit the xml inorder to get it as ,

<studentFile>
    <student>
        <studentName>CLASSA</studentName>
        <studentStatus>Success</studentStatus>
        <studentActions>
            <studentAction>
                <studentType>Juniour</studentType>
                <studentStatus>Failed</studentStatus>
                <studentMsg/>
            </studentAction>
            <studentAction>
                <studentType>HighSchool</studentType>
                <studentStatus>Completed</studentStatus>
                <studentMsg/>
            </studentAction>
        </studentActions>
    </student>
    <student>
        <studentName>CLASSB</studentName>
        <studentStatus>Success</studentStatus>
        <studentActions>
            <studentAction>
                <studentType>Senior</studentType>
                <studentStatus>Completed</studentStatus>
            </studentAction>
            <studentAction>
                <studentType>Middle</studentType>
                <studentStatus>Completed</studentStatus>
            </studentAction>                         
        </studentActions>
    </student>
</studentFile>

Can this be done using sed. I know there are tools like xsltproc but not sure if this is installed in all nodes in our cluster .

Any help will be appreciated. Thanks in advance!

8
  • 1
    "Can this be done using sed." See: stackoverflow.com/a/1732454/3016153 (applies to XML just as well). Commented May 14, 2017 at 18:29
  • 1
    Do you have access to xmlstarlet? Commented May 14, 2017 at 18:30
  • Pretty sure python comes with a xml module on most systems, could try that. Commented May 14, 2017 at 18:39
  • Your file is no valid XML. At least one </student> is missing. Commented May 14, 2017 at 18:40
  • @Cyrus edited now . I checked for xmlstarlet. xmlstarlet -bash: xmlstarlet: command not found Commented May 14, 2017 at 18:43

5 Answers 5

23

Update value with xmllint in file.xml:

xmllint --shell file.xml << EOF
cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus
set failed
save
EOF

or without here document:

echo -e "cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus\nset failed\nsave" | xmllint --shell file.xml

Update: With bash and XML in a variable:

xml=$(xmllint --shell <(echo "$xml") << EOF
cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus
set failed
save -
EOF
)

or without here document:

xml=$(echo -e "cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus\nset failed\nsave -" | xmllint --shell <(echo "$xml"))
Sign up to request clarification or add additional context in comments.

5 Comments

if my xml is in a variable , how can this command be modified?
Hi @Cyrus , I tried adding xml=$(xmllint --shell <(echo "$xml") << EOF cd /studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus set failed save - EOF ) to my script but it says: unexpected EOF while looking for matching `)' , should i enclose them in quotes ?
Hi Cyrus, For an xml tag that looks like < studentStatus/> , this command is not working . xml=$(echo -e "cd /studentFile/student[studentName='CLASSA']/studentActions/st‌​udentAction[studentT‌​ype='Juniour']/stude‌​ntStatus\nset failed\nsave -" | xmllint --shell <(echo "$xml")) ,it says /studentFile/student[studentName='CLASSA']/studentActions/st‌​udentAction[studentT‌​ype='Juniour']/stude‌​ntStatus is an empty Node Set, Can this command be modified in any way? Any help is appreciated!
Unfortunately, I can not reproduce this.
Just found a workaround for this, i stored the new xml node schema in a variable , update it using xmllint and then replaced it via sed. Thanks Cyrus for the help
3

xlmlint, as the name implies, is for parsing and validating XML, not editing it. If you can install xmlstarlet on your cluster, you can do the following:

xmlstarlet ed --update "/studentFile/student[studentName='CLASSA']/studentActions/studentAction[studentType='Juniour']/studentStatus" --value "Failed" *file*

Comments

3

In case if xmlstarlet (a command line toolkit to query/edit/check/transform XML documents) is accessible:

xmlstarlet ed -u "//studentAction/studentStatus[preceding-sibling::studentType[1][text() = 'Juniour'] \
           and ancestor::student/studentName[text() = 'CLASSA']]" -v failed students.xml

The above will output the initial XML document with needed replacement


The command details:

ed -u - edit/update mode

//studentAction/studentStatus - xpath expression to select studentStatus element which has:

  • preceding-sibling::studentType[1][text() = 'Juniour'] - preceding sibling element studentType with value Juniour
  • ancestor::student/studentName[text() = 'CLASSA'] - nearest element studentName with value CLASSA

4 Comments

Not sure the point of this answer, they say in the comments it isn't...
@123, that's pretty sad. Missed that comment while preparing the anser
The comment they just added makes it seem like it probably will be a useful answer, so not too sad
@123, the sad fact is that someone's unable to install such useful tools
1

Update your xml file through xmllint with --shell.

$ xmllint --shell pom.xml
$ cd //*[local-name()='project']/*[local-name()='parent']/*[local-name()='version']
$ set 1.0.0
$ save
$ quit

You can combile bash with:

xml=$(xmllint --shell pom.xml <<EOF
cd //*[local-name()='project']/*[local-name()='parent']/*[local-name()='version']
set 1.0.0
save -
EOF
)

Comments

0

You can try my Xembly command-line tool:

$ xembly --xml file.xml 'XPATH "/studentFile/student[studentName=\'CLASSA\']/studentActions/studentAction[studentType=\'Juniour\']/studentStatus"; SET "failed";'

The full syntax of Xembly is here.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.