editing LDIF files with perl

There are lots of ways to edit LDIF files with perl. You can treat them as a regular file and make changes with “perl -pi -e '/some/change/i' filename.ldif“. You can read them into @ARGV and loop over them with a regex like:

@ARGV = glob("filename.ldif") or die $!;
$^I = "~";
while () {
    s/,dc=example,dc=com/,dc=example,dc=comnchangetype: delete/gi
    print;
}

Or you can read it in with Net::LDAP::LDIF (my debugged version), make a change with a Net::LDAP::Entry method and send the resulting lines to a filehandle:

my $dn = $entry->dn;
$newdn = "newexample,ou=people,dc=example,dc=com";
$entry->dn($newdn);
$ldifw->write_entry($entry);

Oh. Wait. Did you say that the LDIF entry you were trying to manipulate already had a “changetype: add” directive? Well, that’s a problem. Because without any other options, Net::LDAP::LDIF will remove the changetype directive when it does a write_entry. The dn of the entry will be changed, but the “changetype: add” will be missing.

You could do a

print FH $ldif->current_lines();

to write out the existing lines for the entry in the input LDIF, but they would still be the same as the original*.

The answer is to set the “change” option to “1” when you set up your constructor for writing a new LDIF that will eventually replace the file you’re using as input:

$ldifw->Net::LDAP::LDIF("file.ldif", 'w', change => '1');

According to the doc, when this option is set the module will:

Write entry changes to the LDIF file instead of the entries itself. I.e. write LDAP operations acting on the entries to the file instead of the entries contents.

If you do as much heavy duty manipulation of directory entries like me, this option can really save a lot of time and effort.

*The current_lines method can be helpful when you want to filter out entries that match some condition, like:

$dn = $entry->dn;
$changetype = $entry->changetype;
if($changetype eq 'delete') {
    print "not deleting $dnn";
}
else {
    print FH $ldif->current_lines();
}

Postscript:

In the first draft of this post I made a glaring error that came back to bite me. My example code for the “change => ‘1′” option originally read “update => ‘1′”, something that doesn’t exist and would therefore be ignored by the module. Having made this error in this article, I actually copied it out into a script I was writing and kept coming up with an LDIF file bereft of LDAP directives (no changetypes, adds, replaces, or deletes). I finally realized my mistake when comparing the code I had written to an earlier script where I’d gotten it right.

In the world of IT typos may not kill, but they sure result in much loss of sleep.