Need help modifying a file for application license assignments
Hello all--
I don't have a lot of Perl experience, so any help would be appreciated!
Here's what I'm trying to do:
We use an application called MirrorFolder to back up our laptops. We have a text file with machine names and the corresponding licese code for that machine:
myMachine getsThisCode
yourMachine getsThisOtherCode
hisMachine ohLookAnotherCode
...
In the file we have some codes that are available for use, like this:
unassigned someOtherCode
Now, the unassigned codes are mixed in with the assigned codes:
myMachine getsThisCode
unassigned someOtherCode <--the available license
yourMachine getsThisOtherCode
hisMachine ohLookAnotherCode
We use MS Depolyment Toolkit to install the OS and applications (we have about 8 different loads for different job functions). When a laptop is deployed MDT installs MirrorFolder.
The machine that's being deployed needs to be in the license file or the installation fails.
In orer to streamline things, I'm trying to get a script going that will run before MirrorFolder is installed and assign a license for that machine.
The way my script runs now, the code is appended to the list while leaving the available code in the file. Ideally, it should replace the word 'unassigned' with the machine name.
How it is now:
myMachine getsThisCode
unassigned someOtherCode <--the available license
yourMachine getsThisOtherCode
deployMe someOtherCode <--the available license appended
How it should be:
myMachine getsThisCode
deployMe someOtherCode <--the available license correctly assigned (the word 'unassigned' replaced with the machine name)
yourMachine getsThisOtherCode
After all that, here's my code:
Code:
#!/usr/bin/perl
$strSearchFor = "unassigned";
$setCount = 0;
$file = "k:/MirrorFolder4.1/testlicense.txt";
$backupFile = "k:/MirrorFolder4.1/testlicense.bak";
#make a backup of the license file just in case
open (LICENSE, "+<$file" ) or die "Cannot open file."; #open the license.txt file
open (LICENSE_BAK, ">$backupFile") or die "Cannot open file"; #open the license.bak file
@lfile = <LICENSE>;
foreach $backupline (@lfile){
print LICENSE_BAK ($backupline);
}
close (LICENSE);
close (LICENSE_BAK);
undef (@lfile);
#re-open the license.txt file to make the changes
open (LICENSE, "+<$file" ) or die "Cannot open file.";
foreach $line (<LICENSE>){
while ($setCount == 0){
#print ($line);
if ($line =~ m/$strSearchFor/){
print ($line);
$line =~ s/$strSearchFor/$ENV{'ComputerName'}/i;
$setCount = 1;
#print ($setCount);
print LICENSE ($line);
}
}
}
Thanks for sticking it out to the end of the post
Any help would be great!
Thanks,
Joe
Last edited by FishMonger; 11-07-2011 at 08:01 PM..
First, use File::Copy to create the backup file and I'd include a timestamp as part of the filename to maintain a history.
Load your text file into a hash or a HoA (hash of arrays) or a HoH (hash of hashes) where the keys are the hostnames and the values are the licenses. This will make it very easy to separate the assigned and unassigned licenses. Then take one of the unassigned licenses and move it to the assigned group. Then sort the hash and output its data to the file.
Your substitution regex is using $ENV{'ComputerName'} and implies that the script will be run on the laptop instead of the deployment server, which doesn't make sense. Where is the laptop hostname coming from?
Hi FishMonger, thanks for the hints. Now I get to learn how to use hashes (which is not a bad thing at all).
The script will be called via another script (script-ception? sorry ) that will run on the laptop. The machine name is determined very early in the deployment process (one of the first few steps after OS installation)
In the build process, MirrorFolder is installed towards the end (there are some dependencies and a lot of CAD and design software that are more important, so they get installed first)
Oh, and thanks for cleaning up my post... I typed it out once in the editor, then lost it so I re-did it in Notepad++ and copied it, so the CODE tags I put in didn't do much.
#!/usr/bin/perl
use File::Copy;
$strSearchFor = "unassigned";
$setCount = 0;
$file = "k:/Packages/Packages/MirrorFolder4.1/testlicense.txt";
$backupFile = "k:/Packages/Packages/MirrorFolder4.1/testlicense.txt.bak";
$newFile = "k:/Packages/Packages/MirrorFolder4.1/testlicensetext.txt.";
copy($file,$backupFile) or die "copy failed: $!";
open (LICENSE, "+<$file" ) or die "Cannot open file.";
my %hash;
while (<LICENSE>){
my ($key, $val) = split /" "/;
$hash{$key} .= exists $hash{$key} ? "$val" : $val;
}
print "hash done...";
$_ = <STDIN>;
foreach $key (%hash){
print "foreach...";
$_ = <STDIN>;
while($setCount == 0){
print "while...";
$_ = <STDIN>;
if ($key{%hash} =~ m/$strSearchFor/){
print "found it...";
$_ = <STDIN>;
$key =~ s/$strSearchFor/$ENV{'ComputerName'}/i;
print "substitued...";
$_ = <STDIN>;
$setCount = 1;
print "setcount 1...";
$_ = <STDIN>;
open (NEWLICENSE, ">$newFile") or die "sorry.";
#print ($setCount);
print NEWLICENSE (%hash);
close (NEWLICENSE);
}
}
}
close (LICENSE);
I added some prints and <STDIN>s to see where I was in the code, and I can get to 'print "while..."; ' and the script 'sticks' so to speak. It seems like the 'if...' loop never gets evaluated...
I'm very stuck. Any ideas?
Please?
Don't make me get on my knees... It'll help with banging my head on my desk, but still...
Last edited by joeDetroit; 11-12-2011 at 04:12 AM..
I think I've figured out the major problem- The my ($key, $val) = split; isn't splitting the data in the file into 2 columns, i.e. the $key has the machine name and the license, and the $val is empty.
I decide to try to break it down into smaller chunks to see where I was going wrong, so I did this:
Code:
#!/usr/bin/perl
use File::Copy;
$strSearchFor = "UNASSIGNED";
$setCount = 0;
$file = "k:/Packages/Packages/MirrorFolder4.1/testlicense.txt";
$backupFile = "k:/Packages/Packages/MirrorFolder4.1/testlicense.txt.bak";
copy($file,$backupFile) or die "copy failed: $!";
#open the license.txt file to make the changes
open (LICENSE, "+<$file" ) or die "Cannot open file.";
my %hash;
while (<LICENSE>){
my ($key, $val) = split /" "/;
$hash{$key} .= exists $hash{$key} ? "$val" : $val;
}
close (LICENSE);
print "hash done, file closed...";
$_ = <STDIN>;
foreach $key (sort keys %hash){
print "key- " . $key . "value- " . $val . "\n";}
What I get is this:
key- myMachine getsThisCode value-
key- yourMachine getsThisOtherCode value-
key- hisMachine ohLookAnotherCode value-
I just tried split /' '/ and split /"' '"/ (double quoted single quotes) with same result- an empty $val.
I also threw in use warnings; and got
Use of uninitialized value $val in concatenation (.) or string at k:/Packages/Packages/MirrorFolder4.1/test.pl line 42.
$val is (I thought) declared here: my ($key, $val)
Since you haven't posted a sample of the file you're parsing or given a clear and accurate description of its format, it's impossible for us to say why your parsing is failing.
Start by adding these 2 pragmas (which should be in every Perl script you write) and do the best you can on fixing the problems that they point out.
Code:
use strict;
use warnings;
Then post your script and a sample of the file that you're parsing and the errors and warnings that your script generates.
Since the file in question has license keys, I cannot (and will not) post it.
I did say in the first post that the file has machine names and keys separated by a space:
Code:
myMachine myLicense
yourMachine yourLicense
I also said in my last post that I put in use warnings and got the following:
Use of uninitialized value $val in concatenation (.) or string at k:/Packages/Packages/MirrorFolder4.1/test.pl line 42.
This is the part of the script that seems to be failing, specifically the my ($key, $val) = split /" "/; line:
Code:
#open the license.txt file to make the changes
open (LICENSE, "+<$file" ) or die "Cannot open file.";
my %hash;
while (<LICENSE>){
my ($key, $val) = split /" "/;
$hash{$key} .= exists $hash{$key} ? "$val" : $val;
}
I know I should be using strict and warnings. I left them out initially since I was using perl -w to run the script, which generates warnings.
or if the concatenation isn't needed (i.e., if each hostname only appears once):
Code:
$hash{$key} = $val;
If a hostname appears more than once and/or has multiple license keys, then you should use a hash-of-arrays.
You should be using the 3 arg form of open and a lexical var for the filehandle. The die statement should include what you're trying to open and the reason it failed.
Instead of implicitly using $_ in the while loop, you should be using a named var and you should chomp the line to remove the line terminator.
#!/usr/bin/perl
use warnings;
use strict;
use File::Copy;
use Data::Dumper;
my $path = 'k:/MirrorFolder4.1';
my $file = "$path/testlicense.txt";
my $backup = "$path/testlicense.bak";
my %licenses;
#make a backup of the license file just in case
#copy($file, $backup) or die "failed to copy '$file' to '$backup' $!";
open my $license_fh, '<', $file or die "failed to open '$file' $!";
LICENSE:
while ( my $line = <$license_fh> ) {
chomp $line;
next LICENSE if $line =~ /^\s*$/;
# this will need to be adjusted depending on your actual field separator
my ($hostname, $license) = split / /, $line, 2;
unless ($hostname and $license) {
print Dumper $line;
next LICENSE;
}
push @{ $licenses{$hostname} }, $license;
}
print Dumper \%licenses;
You can obfuscate the license keys in the post. The important factor is knowing the exact format of the line, which you have not made clear.
Are the fields separated by a single space and nothing else?
Yes, the machine name and license are separated with a single space, one machine/license key per line. As these are site licenses, there may be more than one machine assigned the same license key:
If a hostname appears more than once and/or has multiple license keys, then you should use a hash-of-arrays.
Each hostname is in the file one time, however each key may appear many times.
Quote:
You should be using the 3 arg form of open and a lexical var for the filehandle. The die statement should include what you're trying to open and the reason it failed.
Is there a performance benefit to this? I see the point (especially the 'die' statement), just curious...
Thanks,
joe
Last edited by joeDetroit; 11-16-2011 at 06:33 PM..
Using the 3 arg form of open is not performance related. There are several reasons, but the key one is security. The 2 arg form is less secure because it opens the door for possible code injection. For example, in the 2 arg form if the filename is being supplied by the user instead of being hard coded, it's passed to and interpolated by the shell. In such a case, the user could add malicious code after the filename and it would/could be executed by the shell.
One advantage of the lexical var is that it's block scoped and perl will do an implicit close if needed and another advantage is that since it's a lexical var, it doesn't pollute the symbol table like the bareword filehandles do.
Since the hostnames aren't duplicated, then a simple hash will suffice instead of the hash of arrays. Before we make that change, I'd like to know the results of code I provided.