Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked

Test if multiple variables are defined

by stroke (Acolyte)
on Jun 25, 2013 at 13:47 UTC ( #1040601=perlquestion: print w/replies, xml ) Need Help??
stroke has asked for the wisdom of the Perl Monks concerning the following question:

I have the following line, which removes any whitespace at the start and end of some variables

s/^\s+|\s+$//g for ( $ipaddress, $prefix, $interface, $device, $locati +on, $comment );

But, i get a common error: "Use of uninitialized value $_ in substitution (s///)" as sometimes some of these variables are undefined.

I've seen a few ways to resolve the error, but not sure of the way I can do it with the least amount of code. I guess I can do:

if(defined $ipaddress) { // } if(defined $prefix) { // } etc.

But there must be a shorter way?

Replies are listed 'Best First'.
Re: Test if multiple variables are defined
by Eily (Prior) on Jun 25, 2013 at 13:53 UTC

    Two short ways would be:

    defined and s/^\s+|\s+$//g for ( $ipaddress, $prefix, $interface, $dev +ice, $location, $comment );

    defined ? s/^\s+|\s+$//g : 1 for ( $ipaddress, $prefix, $interface, $d +evice, $location, $comment );

    In the first case you use the fact that and does not execute the second operand if the first one is false. In the second case, test ? instr1 : instr 2; is syntactic sugar for if (test) { instr1; } else { instr2; }. You do have to write a second instruction though, and since Perl doesn't provide a no-op instruction (but you can make one), the 1 in my example can't be omitted.

      Do either of these perform the check on all the variables and then only execute if they are all defined? Example: $ipaddress is defined, $prefix is defined, $interface is undef. I still want to run the substitution on $ipaddress, $prefix, so effectively have the variables defined state unrelated.

      Hence, I think I may need to do it separately?

        Here is the longer version of the same code:

        for ( $ipaddress, $prefix, $interface, $device, $location, $comment ) { defined and s/^\s+|\s+$//g; # or defined ? s/^\s+|\s+$//g : 1; It does the same thing # or even if (defined) { s/^\s+|\s+$//g } ## Edit : and there is # s/^\s+|\s+$//g if defined; ## That one is quite nice. ## Let's just say there's more than one way to do it. }
        So yeah, the instructions inside the block are run on each element, and not the array as a whole.

        If you have to ask though, I'd advise you to go for the long version because it will run just as fast, it's just another way of writing the same thing. Here is another way to do it, which I find easy to read thanks to the next keyword.

        STRING: for( $ipaddress, $prefix, $interface, $device, $location, $com +ment ) { next STRING unless defined; # We skip any element that is not define +d s/^\s+|\s+$//g; }

Re: Test if multiple variables are defined
by LanX (Bishop) on Jun 25, 2013 at 14:24 UTC
    you are free to disable specific warnings locally:

    perl -we 'my $a; { no warnings qw/uninitialized/; $a =~ s/x/y/;  }'

    Sometimes this is the clearest solution!

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: Test if multiple variables are defined
by AnomalousMonk (Chancellor) on Jun 25, 2013 at 17:50 UTC

    One More Way to Do It: grep:

    >perl -wMstrict -le "my ($foo, $bar); my ($no_ws, $some_ws, $lotsa_ws) = ('no_ws', ' some_ws ', ' lotsa ws '); printf qq{'$_' } for $no_ws, $some_ws, $lotsa_ws; print ''; ;; s{ \A \s+ | \s+ \z }''xmsg for grep defined, $no_ws, $foo, $some_ws, $bar, $lotsa_ws; printf qq{'$_' } for $no_ws, $some_ws, $lotsa_ws; " 'no_ws' ' some_ws ' ' lotsa ws ' 'no_ws' 'some_ws' 'lotsa ws'

    Update: However,  grep as used above has the disadvantage of creating an output list that may be the same size as the input list; Perl then has twice as much data to deal with.

    The advantage of a looping statement like
        defined and s/^\s+|\s+$//g for LIST;
    is that the elements of  LIST are operated on in place and no new list is created.

Re: Test if multiple variables are defined
by Laurent_R (Canon) on Jun 25, 2013 at 21:50 UTC

    What about a simple grep like this?

    s/^\s+|\s+$//g grep {defined $_} for ( $ipaddress, $prefix, $interface, $device, $locati;

    The syntax above might not be perfect, there may be a comma or something else needed, but it conveys the idea, I think.


    I typed the above without having a chance to test and I stupidly misplaced my "for". I really meant to write:

    s/^\s+|\s+$//g for grep {defined $_} ( $ipaddress, $prefix, $interface, $device, $location);

    That probably makes much more sense (the "for" is totally useless between the grep block and the list, but, as placed now, it should give the grep results one by one aliased to $_ to the s/// statement), but that still does not work properly (for some reason, I have trouble using for and foreach with map or grep, it often does not really work the way I expect). So, having now a chance to test, I can change it somewhat to get it working the way I wanted:

    my @result = map {s/^\s+|\s+$//g; $_}  grep {defined $_} ( $ipaddress, $prefix, $interface, $device, $location);

    or simply

    map {s/^\s+|\s+$//g; $_}  grep {defined $_} ( $ipaddress, $prefix, $interface, $device, $location);

    However, using map in this context is probably not be the best, because it is not completely obvious to the eye that the elements of the original list are modified by the command.

      What about a simple grep like this?

      s/^\s+|\s+$//g grep {defined $_} for ( $ipaddress, $prefix, $interface, $device, $locati;

      That wouldn't work, s/// either works on the string binded to it with =~ (or !~) or on $_ otherwise, not on a list parameter. Look at AnomalousMonk's answer for the correct syntax.

        Yes, you are right and I definitely know it. As noted in my update above, I really meant to write:

        s/^\s+|\s+$//g for grep {defined $_} ( $ipaddress, $prefix, $interface, $device, $location);

        But, as I also said in my update, that does not work as I expected, the "for" does not seem to deliver to the s/// statement the elements supplied by the grep. I got it working quite easily using a map instead of a for, but it no longer has the relative cleverness I was looking for.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1040601]
Approved by hdb
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (7)
As of 2018-06-20 16:19 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (116 votes). Check out past polls.