How can I check if an environmental variable is set? - string

I have the following code in perl
my %Opt =
(
boards_txt => "$ENV{'ARDUINO_DIR'}/hardware/arduino/boards.txt",
);
In this you can see that the env variable ARDUINO_DIR is append. Some users might not have this variable set. If that is the case, then I want to hardcode a path.
Question: How can I check if the env variable is set or not?

The correct answers have been given, but I wanted to add that you might make use of the rather handy defined-or assignment operator //=:
my $dir = $ENV{'ARDUINO_DIR'};
$dir //= "/other/path";
Or, as RobEarl points out in the comment:
my $dir = $ENV{'ARDUINO_DIR'} // "/other/path";
This is the logical equivalent of
my $dir;
if (defined $ENV{'ARDUINO_DIR'}) {
$dir = $ENV{'ARDUINO_DIR'};
} else {
$dir = "/other/path";
}
As mob points out, the defined-or operator requires perl v5.10. For those who still have not upgraded to that version it is also possible to use the || operator:
my $dir = $ENV{'ARDUINO_DIR'} || "/other/path";
The caveat being that this will overwrite values that are interpreted as false, which may in some context be considered proper values, such as the empty string or zero. In this case, however, it is unlikely that 0 or the empty string are valid paths.

You are already using the %ENV hash. It contains all environment variables, so you could do something like:
if (defined $ENV{'ARDUINO_DIR'}) { $prefix = $ENV{'ARDUINO_DIR'} }
else { $prefix = '/path/to/arduino/dir/' }
my $path_to_txt = $prefix . 'boards.txt';
I suggest you use File::Spec for working with paths.

You can check for existence of a hash key with exists:
perl -le 'print "fnord!" if exists $ENV{"ARDUINO_DIR"}'

Related

In PowerShell how do I load multiple values into a variable?

I have a requirement at work to check several registry key values and I want to automate this process using PowerShell. One of the registry keys that I check has 3 values and I am not able to successfully check it using my PowerShell script.
I do not believe I am correctly loading the $value variable so that it can be compared to the value of $path.
$path = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\LanManServer).NullSessionPipe
$value = “netlogon samr lsarpc”
if ($path -ne $value) {
Write-Host “Value is incorrect or missing.”
} else {
Write–Host “Config is correct.”
}
I expect the output to be
Config is correct.
Instead I get
Value is incorrect or missing.
Using Regedit I can see that the key has the correct values.
NullSessionPipe apparently is a REG_MULTI_SZ, meaning that the data is returned as an array of strings. You could do "$path" -ne $value to mangle the array into a flat string, but that would imply that the order of the substrings is identical in both strings. A better approach is to compare arrays via Compare-Object.
$path = (Get-ItemProperty HKLM:\...).NullSessionPipe
$value = 'netlogon', 'samr', 'lsarpc'
if (Compare-Object $path $value) {
'arrays differ'
} else {
'arrays are equal'
}
#Ansgar, After some experimenting I was able to get the script to work. I deleted the blank spaces and added a carriage return after netlogon and samr. It works perfectly!!!
$path = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\LanManServer).NullSessionPipe
$value = 'netlogon',
' samr',
' lsarpc'
if (Compare-Object $path $value) {
'arrays differ'
} else {
'arrays are equal'
}

bash script function scope

function generateFileList {
for entry in "$ORIGINATION_PATH"/*
do
entry=${entry%.*} # retain the part before the dot
entry=${entry##*/} # retain the part after the last slash
if [ $(contains "${FILENAME[#]}" $entry) == "n" ]; then
FILENAME[$fn_counter]=$entry
fn_counter=(expr $fn_counter + 1)
echo $entry "added to filelist"
echo ${FILENAME[$fn_counter]}
fi
done
NUMBER_OF_FILES=$(expr ${#FILENAME[#]} + 1)}
I have this function .My $ORIGINATION_PATH has many files in it. However, when I call this function my $FILENAME array gets populated only with one entry.Why? Inside the function everything seems fine, and it seems that $FILENAME array gets all the values it needs to get, but when I check outside the function I only get one value in the $FILENAME aray
Problems with your code and suggestions for improvement:
You should initialize ${FILENAME[#]} to an empty array (either in the function itself if you always want the function to generate a new list of files from scratch, or before calling the function if you want to be able to build up a composite list of files by calling the function repeatedly on different base directories).
You should initialize $fn_counter to zero before starting the loop. Or, for the composite build-up idea, to the number of elements currently in ${FILENAME[#]}. Actually, another, perhaps preferable solution, would be to remove the $fn_counter variable entirely and replace it with ${#FILENAME[#]}, since it should always be equal to that value.
In the line fn_counter=(expr $fn_counter + 1), you're assigning $fn_counter to an array, rather than incrementing it. This is because you forgot the dollar before the open parenthesis. If you ran fn_counter=$(expr $fn_counter + 1) then it would work. But there's a better way to increment a numeric variable: let ++fn_counter.
You don't have to dollar-prefix variables in arithmetic expressions. So, for example, we can say ${FILENAME[fn_counter]} instead of ${FILENAME[$fn_counter]}.
You're trying to echo the element of ${FILENAME[#]} that was just added in the current iteration, but indexing it with $fn_counter after it was incremented, which is incorrect. You can solve this by subtracting 1 from it, i.e. echo "${FILENAME[fn_counter-1]}". Or, if removing $fn_counter, echo "${FILENAME[${#FILENAME[#]}-1]}".
When assigning $NUMBER_OF_FILES, I don't know why you're adding 1 to ${#FILENAME[#]}. The number of elements in the ${FILENAME[#]} array should be equal to the number of files, without requiring an increment, no? I recommend removing this variable entirely, since the value can be accessed directly as ${#FILENAME[#]}.
I recommend you pass inputs as arguments (e.g. pass $ORIGINATION_PATH as an argument) and use the local keyword to reduce the likelihood of variable clashes between functions. Globals are the default in bash, which creates dangerous possibilities for different functions to step on each others' toes. For example, imagine if the contains function (assuming it's a shell function) assigned a value to the global $entry variable.
I recommend always using the [[ command rather than [, as it's more powerful, and it's good to be consistent.
As written, your script won't work correctly on an empty directory. You could test in advance if the directory is empty (e.g. [[ -n "$(find "$ORIGINATION_PATH" -maxdepth 0 -empty)" ]]). Another solution is to set nullglob. Another solution is to skip glob words that don't actually exist (e.g. if [[ ! -e "$entry" ]]; then continue; fi;).
Always double-quote variable expansions to protect against word splitting, which takes place after variable expansion. For example, the contains call should be contains "${FILENAME[#]}" "$entry" (notice the double-quoting around $entry). The only exceptions are (1) when assigning a string variable to a string variable, i.e. new=$old, in which case you don't have to quote it, and (2) when expanding a numeric variable, which is guaranteed not to be corrupted by word splitting.
Here's a working solution, filling in the missing pieces:
function contains {
local target="${#:$#:1}";
local -a array=("${#:1:$#-1}");
local elem='';
for elem in "${array[#]}"; do
if [[ "$elem" == "$target" ]]; then
echo 'y';
return;
fi;
done;
echo 'n';
} ## end contains()
function generateFileList {
local path="$1";
local entry='';
for entry in "$path"/*; do
if [[ ! -e "$entry" ]]; then continue; fi;
entry=${entry%.*}; ## retain the part before the dot
entry=${entry##*/}; ## retain the part after the last slash
if [[ "$(contains "${FILENAME[#]}" "$entry")" == 'n' ]]; then
FILENAME[${#FILENAME[#]}]=$entry;
echo "$entry added to filelist";
echo "${FILENAME[${#FILENAME[#]}-1]}";
fi;
done;
} ## end generateFileList()
ORIGINATION_PATH='...';
FILENAME=(); ## build up result on global ${FILENAME[#]} var
generateFileList "$ORIGINATION_PATH";
echo "\${#FILENAME[#]} == ${#FILENAME[#]}";
echo "\${FILENAME[#]} == (${FILENAME[#]})";

how to combine directory path in perl

I am having a perl script in which i am giving path to directory as input.
Directory has xml files inside it.
In my code i am iterating through all the xml files and creating absolute path for all xml files. Code is working fine.
#!/usr/bin/perl
use File::Spec;
$num_args = $#ARGV + 1;
if ($num_args != 1) {
print "\nUsage: $0 <input directory>\n";
exit;
}
my $dirPath = $ARGV[0];
opendir(DIR, $dirPath);
my #docs = grep(/\.xml$/,readdir(DIR));
foreach my $file (#docs)
{
my $abs_path = join("",$dir,$file);
print "absolute path is $abs_path";
}
Question i have here is,
joining $dirPath and $file with no separator which means that $dirPath must end in a "/". So is there any way or built in function in perl which take cares of this condition and replaces the join method.
All i want is not to worry about the separator "/". Even if script is called with path as "/test/dir_to_process" or "/test/dir_to_process/", i should be able to produce the correct absolute path to all xml files present without worrying about the separator.
Let me know if anyone has any suggestions.
Please take heed of the advice you are given. It is ridiculous to keep asking questions when comments and answers to previous posts are being ignored.
You must always use strict and use warnings at the top of every Perl program you write, and declare every variable using my. It isn't hard to do, and you will be reprimanded if you post code that doesn't have these measures in place.
You use the File::Spec module in your program but never make use of it. It is often easier to use File::Spec::Functions instead, which exports the methods provided by File::Spec so that there is no need to use the object-oriented call style.
catfile will correctly join a file (or directory) name to a path, doing the right thing if path separators are incorrect. This rewrite of your program works fine.
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec::Functions 'catfile';
if (#ARGV != 1) {
print "\nUsage: $0 <input directory>\n";
exit;
}
my ($dir_path) = #ARGV;
my $xml_pattern = catfile($dir_path, '*.xml');
while ( my $xml_file = glob($xml_pattern) ) {
print "Absolute path is $xml_file\n";
}
The answer is in the documentation for File::Spec, e.g., catfile:
$path = File::Spec->catfile( #directories, $filename );
or catpath:
$full_path = File::Spec->catpath( $volume, $directory, $file );
This will add the trailing slash if not there:
$dirPath =~ s!/*$!/!;

How explicitly resolve variables in a perl string?

In my perl script I want to have both versions of $config directory:
my $config='$home/client/config';
and
my $config_resolved="$home/client/config";
But I want to get $config_resolved from $config, i.e. something like this:
my $config_resolved=resolve_vars($config);
How can I do such thing in perl?
From the Perl FAQ (which every Perl programmer should read at least once):
How can I expand variables in text strings?
(contributed by brian d foy)
If you can avoid it, don't, or if you can
use a templating system, such as Text::Template or Template Toolkit,
do that instead. You might even be able to get the job done with
sprintf or printf:
my $string = sprintf 'Say hello to %s and %s', $foo, $bar;
However, for the one-off simple case where I don't want to pull out a
full templating system, I'll use a string that has two Perl scalar
variables in it. In this example, I want to expand $foo and $bar to
their variable's values:
my $foo = 'Fred';
my $bar = 'Barney';
$string = 'Say hello to $foo and $bar';
One way I can do this involves the substitution operator and a double /e flag. The
first /e evaluates $1 on the replacement side and turns it into $foo. The
second /e starts with $foo and replaces it with its value. $foo,
then, turns into 'Fred', and that's finally what's left in the string:
$string =~ s/(\$\w+)/$1/eeg; # 'Say hello to Fred and Barney'
The /e will also silently ignore violations of strict, replacing undefined
variable names with the empty string. Since I'm using the /e flag
(twice even!), I have all of the same security problems I have with
eval in its string form. If there's something odd in $foo, perhaps
something like #{[ system "rm -rf /" ]}, then I could get myself in
trouble.
To get around the security problem, I could also pull the
values from a hash instead of evaluating variable names. Using a
single /e, I can check the hash to ensure the value exists, and if it
doesn't, I can replace the missing value with a marker, in this case
??? to signal that I missed something:
my $string = 'This has $foo and $bar';
my %Replacements = (
foo => 'Fred',
);
# $string =~ s/\$(\w+)/$Replacements{$1}/g;
$string =~ s/\$(\w+)/
exists $Replacements{$1} ? $Replacements{$1} : '???'
/eg;
print $string;
I use eval for this.
So, you must replace all scalars (their names) with their values.
$config = 'stringone';
$boo = '$config/any/string';
$boo =~ s/(\$\w+)/eval($1)/eg;
print $boo;
Because you are using my to declare it as private variable, you might as well use a /ee modifier. This can find variables declared to be in local scope:
$boo =~ s/(\$\w+)/$1/eeg;
This is most tidily and safely done by the double-eval modifier on s///.
In the program below, the first /e evaluates the string $1 to get $home, while the second evaluates $home to get the variable's value HOME.
use strict;
my $home = 'HOME';
my $config = '$home/client/config';
my $config_resolved = resolve_vars($config);
print $config_resolved, "\n";
sub resolve_vars {
(my $str = shift) =~ s/(\$\w+)/$1/eeg;
return $str;
}
output
HOME/client/config

perl code behaving strange

I have written a code to get the url of a website and then search for a string and then compare that string(actually a number) with a hardcoded number
#!/usr/bin/perl
use LWP::Simple;
my $oldversion =36;
$pageURL="http://www.google.com/isos/preFCS5.3/LATESTGOODCVP/";
my $simplePage=get($pageURL);
my $newPage = "$simplePage";
my $str = (split("href=\"CVP-LATEST-5.3.0.",$newPage ))[1];
my $version = substr("$str",0,2);
print $version; // HERE IT PRINT 37 WHICH IS CORRECT
if($version =! $oldVersion )
{
print $version; // BUT HERE IT PRINTS 1 WHICH IS WRONG. HOW IS IT CHANGING ?
##-- fetch the zip and save it as perlhowto.zip
my $status = getstore("http://www.google.com/isos/preFCS5.3/LATESTGOODCVP/CVP-LATEST-5.3.0.$version.iso", "CVP-LATEST-5.3.0.$version.iso");
}
else
{
print("Currently new version\n");
}
Why is it changing the value ? its not able to download the file becuase of that.
You mean !=, not =!, which is an assignment of a negation.
Also, split always uses a regex (except for the very special case of a string that has a single space), so those .s in 5.3.0. will match any non-newline. You probably want to \-escape them.
You may be interested in the uscan script in the debian devtools package.
You have got your "not equals" operator backwards. It should be != rather than =!.
By using =! you are in effect saying "set $version to the negated value of $oldversion".
Here is the offending line
if($version =! $oldVersion ) # Should be if($version != $oldVersion )
Also notice that by using the != operator you are telling perl that $version and $oldversion contain numbers. For string comparisons you should use the ne operator, which assumes that these variables contain strings.
if($version ne $oldVersion ) # String inequality
Here is the documentation for equality operators -
http://perldoc.perl.org/perlop.html#Equality-Operators
It's because you are assigning to $version the value !$oldVersion in this "test":
if($version =! $oldVersion )
And $oldVersion is nothing--but $oldversion is 37. You are assigning $version the boolean negation of an undefined variable. Undefined is boolean false, and so the negation is boolean true or 1.
If you read very much on perl, you're bound to come across the advice to use strict and warnings. Had you done that, it would have told you, among other things:
Global symbol "$oldVersion" requires explicit package name at - line 21.
This means that you didn't declare $oldVersion as lexical (my) or package-level (our) in this package, so if you want to use it, please include the package where you're getting it. In a vast majority of cases, a seasoned Perl programmer will recognize this as "Ugh, I didn't declare $oldVersion!" and the reason is that you declared $oldversion.
Your use of split doesn't make a lot of sense here. What you really want are the two digits following the CVP-LATEST-5.3.0. string. You're also not really doing anything by assigning one variable to another with the addition of quotes ($newPage = "$simplePage").
And, of course, as others have pointed out, the comparison is != not =!.
I'd rewrite this as:
use strict;
use warnings;
use LWP::Simple;
my $oldVersion = 36;
my $url = 'http://www.google.com/isos/preFCS5.3/LATESTGOODCVP/';
my $newPage = get($url)
or die "Cannot retrieve contents from $url\n";
if ( $newPage =~ /href=\"CVP-LATEST-5\.3\.0\.(\d\d)/ ) {
my $version = $1;
if ( $version != $oldVersion ) {
my $status = getstore($url . "CVP-LATEST-5.3.0.$version.iso",
"CVP-LATEST-5.3.0.$version.iso");
} else {
print "Already at most recent version\n";
}
} else {
die "Cannot find version tag in contents from $url\n";
}

Resources