Linux - GeneralThis Linux forum is for general Linux questions and discussion.
If it is Linux Related and doesn't seem to fit in any other forum then this is the place.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Don't use sed.
You will get answers, most likely involving adding lines to the hold buffer until you hit the end line (</ADD_USER), then do the match and delete. If you just want the job done, use awk or perl or whatever - something that has better logic.
Is the element really broken like that? It seems to be missing the closing less than ( > ) sign. If it's really not there, then you'll probably have to try "awk" as suggested. But if your file is well-formed XML then you can use some really easy XML parsers like XML::TreeBuilder or XML::TreeBuilder::XPath in Perl.
xml.
(and yes, the syntax is valid)
i have been succesfully using xmllint (part of libxml2) for things like this. the magic word to search for is xpath.
Well, I have been looking at perl, but still not quite there. The xml files is the output of hponcfg which queries the local integrated lights out (iLO) interface on the HP server. I want to be able to add/remove users from iLO without having to go in to the graphical interface. A more complete listing of the xml is:
Code:
<!-- HPONCFG VERSION = "4.4.0" -->
<RIBLC VERSION="2.1">
<LOGIN USER_LOGIN="Administrator" PASSWORD="password">
<DIR_INFO MODE="write">
<MOD_DIR_CONFIG>
<DIR_AUTHENTICATION_ENABLED VALUE = "N"/>
<SEVERAL MORE DIR LINES/>
</MOD_DIR_CONFIG>
</DIR_INFO>
<RIB_INFO MODE="write">
<MOD_NETWORK_SETTINGS>
<SPEED_AUTOSELECT VALUE = "Y"/>
<NIC_SPEED VALUE = "10"/>
<SEVERAL MORE NETWORK LINES/>
</MOD_NETWORK_SETTINGS>
</RIB_INFO>
<USER_INFO MODE="write">
<ADD_USER
USERNAME = "Ralph Delp"
USER_LOGIN = rpdelp
PASSWORD = "%user_password%">
<ADMIN_PRIV value = "Y"/>
<REMOTE_CONS_PRIV value = "Y"/>
<RESET_SERVER_PRIV value = "Y"/>
<VIRTUAL_MEDIA_PRIV value = "Y"/>
<CONFIG_ILO_PRIV value = "Y"/>
</ADD_USER>
<ADD_USER
USERNAME = "Root User"
USER_LOGIN = "root"
PASSWORD = "%user_password%">
<ADMIN_PRIV value = "Y"/>
<REMOTE_CONS_PRIV value = "Y"/>
<RESET_SERVER_PRIV value = "Y"/>
<VIRTUAL_MEDIA_PRIV value = "Y"/>
<CONFIG_ILO_PRIV value = "Y"/>
</ADD_USER>
</USER_INFO>
</LOGIN>
</RIBCL>
I am trying to delete elements based on the attribute values. In this case USERNAME. You edit out what you want to keep, and then you can re-submit what you want to delete to iLO with the edited xml file. So, if I want to preserve Ralph's account, I need to remove it from the xml file before sending it back and telling hponcfg to delete what is in the sent xml file. My perl is having a little problem. I can print all of the USERNAME values, but I can't seem to match against it. I have:
Code:
#!/usr/bin/perl
use strict;
use warnings;
use XML::LibXML;
die "Usage: $0 filename <name to delete>\n"
unless ( @ARGV > 2 );
my $xml_file = shift;
my $bad_name = shift;
print "Searching for bad name: $bad_name\n";
my $xml = XML::LibXML->new;
my $dom = $xml->parse_file( $xml_file );
foreach my $user ( $dom->findnodes ( '/RIBCL/LOGIN/USER_INFO/ADD_USER' )) {
my ($name) = $user->findnodes( './@USER_NAME');
print $name->to_literal, "\n";
if ( "$name->to_literal" eq "$bad_name" ) {
my ($addu) = $name->parentNode;
print "Removing element: ", $addu->to_literal, "\n";
}
}
Which does print out all of the user names, but never matches. Instead of the 'eq' I tried:
Code:
if ( "$name->to_literal" =~ /"$bad_name"/ ) {
but that did not work either. I thought maybe there might be some leading or trailing spaces. So, I printed with a leading and trailing "X" but found not spaces. Any ideas on why I can't make a match?
#!/usr/bin/perl
use strict;
use warnings;
use XML::LibXML;
die "Usage: $0 filename <name to delete>\n"
unless ( @ARGV > 2 );
my $xml_file = shift;
my $bad_name = shift;
chomp($bad_name);
print "Searching for bad name: $bad_name\n";
my $xml = XML::LibXML->new;
my $dom = $xml->parse_file( $xml_file );
my $root = $dom->getDocumentElement;
# Retrieve array of all users
my @users = $root->getElementsByTagName('ADD_USER');
foreach my $user ( @users ) {
my $username = $user->getAttribute('USER_NAME');
if ( "$username" eq "$bad_name" ) {
print "Username = $username \n";
print "user node type is: ", $user->nodeType, "\n";
my ($addu) = $user->parentNode;
print "addu node type is: ", $addu->nodeType, "\n";
print "Removing element.\n";
$addu->removeChild( $user );
print $dom->toString, "\n";
}
}
The user node type and addu node type are both 1 which is an element. My presumption is that $addu->removeChild( $user ) should remove the corresponding $user element from $root, correct? I then wanted to print out the new document with the removed element, but all I get is a lot of blank lines. So, two questions:
1. Do you think I have correctly removed the element?
2. How to print out the result?
I was using the wrong variable to print out and the wrong method. This is getting me the result, but with an extra blank line. Very close!
Thanks!
Last edited by bradvan; 08-18-2016 at 08:53 AM.
Reason: Figured it out
I can now delete multiple users at a time and no blank lines in the output. Thanks for all of the suggestions! Here is my final code:
Code:
#!/usr/bin/perl
use strict;
use warnings;
use XML::LibXML;
die "Usage: $0 filename <name to delete>\n"
unless ( @ARGV > 1 );
my $xml_file = shift;
my @bad_names = @ARGV;
my $output;
my $xml = XML::LibXML->new;
my $dom = $xml->parse_file( $xml_file );
my $root = $dom->getDocumentElement;
# Retrieve array of all users
my @users = $root->getElementsByTagName('ADD_USER');
foreach my $bad_name ( @bad_names ) {
print "Searching for bad name: $bad_name\n";
foreach my $user ( @users ) {
my $username = $user->getAttribute('USER_NAME');
if ( "$username" eq "$bad_name" ) {
my ($addu) = $user->parentNode;
print "Removing element for user name: ", $username, "\n";
$addu->removeChild( $user );
$output = $dom->toString(0);
$outout =~ s/(?<=\n)\s*\n//g;
}
}
}
open( my $FH, '>', "newilo") or die "Could not open file newilo $!";
print $FH $output, "\n";
close $FH;
Thanks for that. One problem is that it is not installed on any of my servers. I did find it in the EPEL repository and was trying to work with it, but I was getting a little confused by what I was finding on the Internet versus what I found in the rpm. In my rpm there are several files under /usr/share/doc, a man page and one binary, /usr/bin/xmlstarlet. All of the examples I've seen and even the one you gave show using the command 'xml.' I'm a little confused. Shouldn't it be xmlstartlet ed -d ...?
I recall being a little confused when I first started using it, but I made myself notes on the basic operations I use so I have not had to look back at the docs for a while.
I would suggest that you review or grab a local copy of the online documentation from here - it is much more complete than the man page and has some really good examples of all operations.
If you are not familiar with XPath notation then you might want to do a little searching for that as well - XPath is the notation used to navigate your XML documents and specify internal elements. For example, the expression I used in my last post was...
Code:
xml ed -d "//ADD_USER[@USERNAME='Ralph Delp']" test.xml
... which means "//"= any path to an element named "ADD_USER" which has an attribute named "USERNAME" with a value of "Ralph Delp". Learning the basics of XPath is easy but essential.
And yes, the command is "xml", not xmlstarlet.
If you need a little more help please post back here, it is well worth learning your way around it and I and others will be glad to help!
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.