Extracting CN's with LDAP? - perl

I have this code
#!/usr/bin/perl
use warnings;
use strict;
use Net::LDAP;
use Data::Dumper;
my $dn="CN=...";
my $password="xxx";
my $ldap = Net::LDAP->new('example.com') or die "$#";
my $mesg = $ldap->bind($dn, password=>$password);
if ($mesg->code) { die "uuuu $mesg"; }
$mesg = $ldap->search(base => "dc=test,dc=example,dc=com", filter => "(name=LIST)",);
my $ref = $mesg->entry->get_value("member", asref => 1);
print Dumper $ref;
foreach my $string (#{$ref}) {
$string =~ /CN=(.+?),.*/;
print $1 . "\n";
}
which outputs the CN's using regular expressions:
aaaa
bbbb
cccc
...
Using Dumper can I see the structure
$VAR1 = [
'CN=aaaa,OU=test,DC=test,DC=example,DC=com',
'CN=bbbb,OU=test,DC=test,DC=example,DC=com',
'CN=cccc,OU=test,DC=test,DC=example,DC=com',
So I am wondering if there is a more "LDAP" way to extract these CN's, instead of using regular expressions?
Update:
Based on Javs answer this is the solution.
my $ref = $mesg->entry->get_value("member", asref => 1);
foreach my $string (#{$ref}) {
print ldap_explode_dn($string)->[0]{CN} . "\n";
}

You can:
use Net::LDAP::Util qw(ldap_explode_dn);
and use it on your attribute like this:
ldap_explode_dn($mesg->entry->get_value('member'));
to get this array of hashes:
$VAR1 = [
{
'CN' => 'aaaa'
},
{
'OU' => 'test'
},
{
'DC' => 'test'
},
{
'DC' => 'example'
},
{
'DC' => 'com'
}
];

You do realize that CN is usually an attribute in LDAP directories?
Why not just query for the attribute CN for all returned objects? Then no parsing required.

Related

Convert comma separated values to key value pair Perl

I have an array of states in the format
('AL','Alabama','AK','Alaska','AR','Arkansas'...)
which I want formatted like:
[{'AL' => 'Alabama'},...]
This is primarily so that I can more easily loop through using the HTML::Template module (https://metacpan.org/pod/HTML::Template#TMPL_LOOP)
I'm fairly new to perl, so unsure about how to do this sort of action and can't find something similar enough.
Wouldn't the following make more sense for HTML::Template?
states => [ { id => 'AL', name => 'Alabama' }, ... ]
This would allow you to use the following template:
<TMPL_LOOP NAME=states>
<TMPL_VAR NAME=name> (<TMPL_VAR NAME=id>)
</TMPL_LOOP>
To achieve that, you can use the following:
use List::Util 1.29 qw( pairmap );
states => [ pairmap { +{ id => $a, name => $b } } #states ]
That said, you're probably generating HTML.
<select name="state">
<TMPL_LOOP NAME=states>
<option value="<TMPL_VAR NAME=id_html>"><TMPL_VAR NAME=name_html></option>
</TMPL_LOOP>
</select>
To achieve that, you can use the following:
use List::Util 1.29 qw( pairmap );
{
my %escapes = (
'&' => '&',
'<' => '<',
'>' => '>',
'"' => '"',
"'" => ''',
);
sub text_to_html(_) { $_[0] =~ s/([&<>"'])/$escapes{$1}/rg }
}
states => [ pairmap { +{ id_html => $a, name_html => $b } } map text_to_html, #states ]
use List::Util 1.29;
#state_hashes = List::Util::pairmap { +{ $a => $b } } #states;
Unless you need to keep this hash around for later use I think that simply looping through the elements two at a time would be simpler. You can accomplish this type of looping easily with splice:
my #states = ('AL','Alabama','AK','Alaska','AR','Arkansas'...);
while (my ($code, $name) = splice(#states, 0, 2)) {
# operations here
}
Alternatively, you can use this same approach to create the data structure you want:
my #states = ('AL','Alabama','AK','Alaska','AR','Arkansas'...);
my #state_hashes = ();
while (my ($code, $name) = splice(#states, 0, 2)) {
push #state_hashes, { $code => $name };
}
# do w/e you want with #state_hashes
Note: splice will remove elements from #states
bundle_by from List::UtilsBy can easily create this format:
use strict;
use warnings;
use List::UtilsBy 'bundle_by';
my #states = ('AL', 'Alabama', 'AK', 'Alaska', 'AR', 'Arkansas', ... );
my #hashes = bundle_by { +{#_} } 2, #states;
map solution with a few perlish things
my #states = ('AL','Alabama','AK','Alaska','AR','Arkansas','VT','Vermont');
my %states;
map { $states{$states[$_]} = $states[$_+1] unless $_%2 } 0..$#states;

get nbest key-value pairs hash table in Perl

I have this script that use a hash table:
#!/usr/bin/env perl
use strict; use warnings;
my $hash = {
'cat' => {
"félin" => '0.500000',
'chat' => '0.600000',
'chatterie' => '0.300000'
'chien' => '0.01000'
},
'rabbit' => {
'lapin' => '0.600000'
},
'canteen' => {
"ménagère" => '0.400000',
'cantine' => '0.600000'
}
};
my $text = "I love my cat and my rabbit canteen !\n";
foreach my $word (split "\s+", $text) {
print $word;
exists $hash->{$word}
and print "[" . join(";", keys %{ $hash->{$word} }) . "]";
print " ";
}
For now, I have this output:
I love my cat[chat;félin;chatterie;chien] and my rabbit[lapin] canteen[cantine;ménagère] !
I need to have the nbest key value according to the frequencies (stored in my hash). For example, I want to have the 3 best translations according to the frequencies like this:
I love my cat[chat;félin;chatterie] and my rabbit[lapin] canteen[cantine;ménagère] !
How can I change my code to take into account the frequencies of each values and also to print the nbest values ?
Thanks for your help.
The tidiest way to do this is to write a subroutine that returns the N most frequent translations for a given word. I have written best_n in the program below to do that. It uses rev_nsort_by from List::UtilsBy to do the sort succinctly. It isn't a core module, and so may well need to be installed.
I have also used an executable substitution to modify the string in-place.
use utf8;
use strict;
use warnings;
use List::UtilsBy qw/ rev_nsort_by /;
my $hash = {
'cat' => {
'félin' => '0.500000',
'chat' => '0.600000',
'chatterie' => '0.300000',
'chien' => '0.01000',
},
'rabbit' => {
'lapin' => '0.600000',
},
'canteen' => {
'ménagère' => '0.400000',
'cantine' => '0.600000',
}
};
my $text = "I love my cat and my rabbit canteen !\n";
$text =~ s{(\S+)}{
$hash->{$1} ? sprintf '[%s]', join(';', best_n($1, 3)) : $1;
}ge;
print $text;
sub best_n {
my ($word, $n) = #_;
my $item = $hash->{$word};
my #xlate = rev_nsort_by { $item->{$_} } keys %$item;
$n = $n > #xlate ? $#xlate : $n - 1;
#xlate[0..$n];
}
output
I love my [chat;félin;chatterie] and my [lapin] [cantine;ménagère] !

Read config hash-like data into perl hash

I have a config file config.txt like
{sim}{time}{end}=63.1152e6;
{sim}{output}{times}=[2.592e6,31.5576e6,63.1152e6];
{sim}{fluid}{comps}=[ ['H2O','H_2O'], ['CO2','CO_2'],['NACL','NaCl'] ];
I would like to read this into a perl hash,
my %h=read_config('config.txt');
I have checked out module Config::Hash , but it does not offer the same input file format.
Can roll your own. Uses Data::Diver for traversing the hash, but could do that manually as well.
use strict;
use warnings;
use Data::Diver qw(DiveVal);
my %hash;
while (<DATA>) {
chomp;
my ($key, $val) = split /\s*=\s*/, $_, 2;
my #keys = $key =~ m/[^{}]+/g;
my $value = eval $val;
die "Error in line $., '$val': $#" if $#;
DiveVal(\%hash, #keys) = $value;
}
use Data::Dump;
dd \%hash;
__DATA__
{sim}{time}{end}=63.1152e6;
{sim}{output}{times}=[2.592e6,31.5576e6,63.1152e6];
{sim}{fluid}{comps}=[ ['H2O','H_2O'], ['CO2','CO_2'],['NACL','NaCl'] ];
Outputs:
{
sim => {
fluid => { comps => [["H2O", "H_2O"], ["CO2", "CO_2"], ["NACL", "NaCl"]] },
output => { times => [2592000, 31557600, 63115200] },
time => { end => 63115200 },
},
}
Would be better if you could come up with a way to not utilize eval, but not knowing your data, I can't accurately suggest an alternative.
Better Alternative, use a JSON or YAML
If you're picking the data format yourself, I'd advise using JSON or YAML for saving and loading your config data.
use strict;
use warnings;
use JSON;
my %config = (
sim => {
fluid => { comps => [["H2O", "H_2O"], ["CO2", "CO_2"], ["NACL", "NaCl"]] },
output => { times => [2592000, 31557600, 63115200] },
time => { end => 63115200 },
},
);
my $string = encode_json \%config;
## Save the string to a file, and then load below:
my $loaded_config = decode_json $string;
use Data::Dump;
dd $loaded_config;

Hash doesn't print in Perl

I have a hash:
while( my( $key, $value ) = each %sorted_features ){
print "$key: $value\n";
}
but I cannot obtain the correct value for $value. It gives me:
intron: ARRAY(0x3430440)
source: ARRAY(0x34303b0)
exon: ARRAY(0x34303f8)
sig_peptide: ARRAY(0x33f0a48)
mat_peptide: ARRAY(0x3430008)
Why is it?
Your values are array references. You need to do something like
while( my( $key, $value ) = each %sorted_features ) {
print "$key: #$value\n";
}
In other words, dereference the reference. If you are unsure what your data looks like, a good idea is to use the Data::Dumper module:
use Data::Dumper;
print Dumper \%sorted_features;
You will see something like:
$VAR1 = {
'intron' => [
1,
2,
3
]
};
Where { denotes the start of a hash reference, and [ an array reference.
You can use also Data::Dumper::Pertidy which runs the output of Data::Dump through Perltidy.
#!/usr/bin/perl -w
use strict;
use Data::Dumper::Perltidy;
my $data = [{title=>'This is a test header'},{data_range=>
[0,0,3, 9]},{format => 'bold' }];
print Dumper $data;
Prints:
$VAR1 = [
{ 'title' => 'This is a test header' },
{ 'data_range' => [ 0, 0, 3, 9 ] },
{ 'format' => 'bold' }
];
Your hash values are array references. You need to write additional code to display the contents of these arrays, but if you are just debugging then it is probably simpler to use Data::Dumper like this
use Data::Dumper;
$Data::Dumper::Useqq = 1;
print Dumper \%sorted_features;
And, by the way, the name %sorted_features of your hash worries me. hashes are inherently unsorted, and the order that each retrieves the elements is essentially random.

How can I flatten the arguments to my subroutine into an array?

Consider following script:
use strict;
use Data::Dumper;
my #arr=('1A','2A');
my $arr_ref=['1','2'];
sub routine1
{
my #arr=#_;
print Dumper(\#arr);
}
routine1(#arr,'one_A');
sub routine2
{
my $arr_ref=[#_];
print Dumper($arr_ref);
}
routine2($arr_ref,'one');
routine1 is using #arr and routine2 is using $arr_ref.
routine1 prints the following:
$VAR1 = [
'1A',
'2A',
'one_A'
];
routine2 prints following:
$VAR1 = [
[
'1',
'2'
],
'one'
];
I want to continue using #_ and arr_ref in routine2 but want to come up with below output:
$VAR1 = [
'1',
'2'
'one'
];
Can someone suggest the way out?
Using the function ref you can see if a scalar is a reference (and if so, which type). In a simplistic case where only array references will be passed you can simply use this to flatten the inputs.
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
sub test {
my #arr = map { ref() ? #$_ : $_ } #_;
print Dumper \#arr;
}
test( ['a', 'b'], 1 );
As a side benefit, this code will die with a message if a reference to another type is passed, since you attempt to deference as an array. If you need to handle more, you will need to check the reference type. This starts to build in complexity quickly.
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
sub test {
my #arr = map {
my $type = ref;
if ( ! $type ) {
$_;
} elsif ( $type eq 'ARRAY' ) {
#$_;
} elsif ( $type eq 'HASH' ) {
%$_;
} else {
()
}
} #_;
print Dumper \#arr;
}
test( ['a', 'b'], { p => 'q' }, 1 );
By returning an empty list for other reference types I silently ignore all other reference types. Or perhaps you would rather force stringification on other reference types.
...
} else {
"$_";
}
...
test( ['a','b'], sub{}, bless({},'MyClass'), 1 );
Of couse which of these handlings to use depends on you use case.
Just wrote this the other day at work.
sub flatten {
return map { ref($_) ? flatten(#{$_}) : ($_) } #_;
}
This program shows a subroutine flatten that will flatten a mixed list of simple data and array references, nested to any level.
use strict;
use warnings;
use Data::Dump;
my #arr = qw/ 1A 2A /;
my $arr_ref = [1, 2];
sub flatten;
routine1(#arr, 'one_A');
routine2($arr_ref, 'one');
sub routine1 {
my #arr=#_;
dd \#arr;
}
sub routine2 {
my $arr_ref = [flatten #_];
dd $arr_ref;
}
sub flatten {
my $i = 0;
while ($i < #_) {
my $item = $_[$i];
if (ref $item eq 'ARRAY') {
splice #_, $i, 1, #$item;
}
else {
++$i;
}
}
#_;
}
output
["1A", "2A", "one_A"]
[1, 2, "one"]