sravs448 has asked for the wisdom of the Perl Monks concerning the following question:

I have the below code
use strict; use warnings; my ($xlink,@xlinkpaths); my $stream = "streamname"; my @xlinks = (`cmd to get xlinks`) ; foreach $xlink (@xlinks) { @xlinkpaths = } my @exclpaths = ( "wefwfewf/fewfff/", "btrtbrtb/gbrgbrg/rttty/", );
The output in  @xlinks will be as below:
incl -s streamname /./abc/def xlink /./abc/def from streamname incl -s streamname /./xyz/qwen/defs/asd xlink /./xyzz/qwen/defs/asd from streamname
1)Format Querry: How to get the text after "/./" in the line that starts with "incl -s" into array @xlinkpaths. The final @xlinkpaths should look like
@xlinkpaths = ( "abc/def/", "xyzz/qwen/defs/asd", );
2) Appending to already existing array Querry: How to add them to the already existing array @exclpaths, so that the final @exclpaths should look like
my @exclpaths = ( "abc/def/", "xyzz/qwen/defs/asd", "wefwfewf/fewfff/", "btrtbrtb/gbrgbrg/rttty/", );

Replies are listed 'Best First'.
Re: Perl: Split/format the output and append it to an array
by shmem (Chancellor) on Sep 11, 2014 at 22:17 UTC
    0) Zero first.
    I have the below code

    What is my $stream = streamname; meant to do? "streamname" is a bare word, and without strict it will be interpreted as a literal string. What for?

    my $xlink,@xlinkpaths;

    This doesn't do what you want. To give my a list, you have to wrap the list in parens:

    perl -Mstrict -ce 'my $xlink,@xlinkpaths' Global symbol "@xlinkpaths" requires explicit package name at -e line +1. -e had compilation errors. perl -Mstrict -ce 'my($xlink,@xlinkpaths)' -e syntax OK

    but in

    my @xlinks = (`cmd to get xlinks`) ;

    the parens are gratuitous.

    foreach $xlink (@xlinks) { @xlinkpaths = }

    Missing right hand side of assignment. Ok, you ask for that in

    1)

    First, you don't want to assign to @xlinkpaths, but append to it. Use push. Then, to get the text after /./, you could use a regular expression:

    foreach $xlink (@xlinks) { push @xlinkpaths, $xlink =~ m{incl -s streamname /./(.+)}; }

    Look up the match operator m// (used as m{} here) in perlop.
    The match operator returns 1 upon success; if used in list context (this is the case with push) it returns 1, if no captures are used (a capture is what is caught by parens, in the above code (.+), otherwise it returns the captures.
    Captures are stored in $1, $2, $3, ...

    The above code could have been written as

    foreach $xlink (@xlinks) { $xlink =~ m{incl -s streamname /./(.+)}; push @xlinkpaths, $1; }

    which gives you the possibility to not include empty matches via a statement modifier:

    push @xlinkpaths, $1 if $1;

    2)

    As you have already learned by now, use push to append to an existing array. Use unshift to prepend to an existing array. Your final @xlinkpaths looks like prepending.

    unshift @exclpaths, @xlinkpaths;

    Your code - after cleanup and inserting mising bits - could look like

    use strict; use warnings; my( $xlink,@xlinkpaths ); my $stream = streamname; my @xlinks = (`cmd to get xlinks`) ; foreach $xlink (@xlinks) { push @xlinkpaths, $xlink =~ m{incl -s streamname /./(.+)}; } my @exclpaths = ( "wefwfewf/fewfff/", "btrtbrtb/gbrgbrg/rttty/", ); unshift @exclpaths, @xlinkpaths;

    and appears to do what you want.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      The first part worked fine. But before prepend(or)after the array, i would like to have the @xlinkpaths contents also in the same pattern as @exclpaths. That is
      1.every value should end with '/'
      2. should be enclosed within quotes ""
      3. Should have comma ',' at end
      How can I achieve this? I want to maintain the same exisitng format in @exclpaths array
        1.every value should end with '/'
        2. should be enclosed within quotes ""
        3. Should have comma ',' at end

        See the concatenation operator '.' in perlop, Additive Operators, and substr.

        Read perlop. All of it.

        I want to maintain the same exisitng format in @exclpaths array

        The format in your @exclpaths is its representation in code. Inside the array, the strings are not enclosed within double quotes, and they don't have a comma at the end:

        my @exclpaths = ( "wefwfewf/fewfff/", "btrtbrtb/gbrgbrg/rttty/", ); print $_,"\n" for @exclpaths; __END__ wefwfewf/fewfff/ btrtbrtb/gbrgbrg/rttty/

        If you want decorations, just add them:

        my @exclpaths = ( "wefwfewf/fewfff/", "btrtbrtb/gbrgbrg/rttty/", ); print "\"$_\",\n" for @exclpaths; __END__ "wefwfewf/fewfff/", "btrtbrtb/gbrgbrg/rttty/",
        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: Perl: Split/format the output and append it to an array
by GrandFather (Saint) on Sep 11, 2014 at 21:50 UTC

    First, do yourself a big favour and add strictures (use strict; use warnings;) to every script you write.

    The answer to both questions is the same: use push.

    Perl is the programming world's equivalent of English
Re: Perl: Split/format the output and append it to an array
by Eily (Monsignor) on Sep 11, 2014 at 21:43 UTC

    For the first part, you can use map (which takes a list of elements and returns a new one computed from elements of the first) and regexes

    @xlinkpaths = map { if (/xlink (.+?) /) # if xlink has been found, and characters +captured { $1; } # yield the captured data else { (); } # yield 0 elements } @xlinks; # Do this for all elements of @xlinks
    I'm aware that it's not the exact solution to your problem, you'll have to try to understand enough for you to correct it.

    To concatenate two arrays (in the order you gave), just do: @exclpaths = (@xlinkpaths, @exclpaths);, or unshift @exclpaths, @xlinkpaths;, whatever looks clearer to you (if your lists are not really big, both are equivalent). See unshift or push.

    In my $xlink,@xlinkpaths; the variable @xlinkpaths is not affected by the my, which will give you an error if you use strict (using strict will tell you where most of your mistakes are, so you should do it).

      For the most part if you find yourself writing significant code in a map, use a for loop instead. Your 7 line confusing map requiring multiple comments becomes:

      for my $xlink (@xlinks) { push @xlinkpaths, $1 if $xlink =~ /xlink (.+?) /; }

      Although you could instead write it:

      @xlinkpaths = map {/xlink (.+?) / ? $1 : ()} @xlinks;
      Perl is the programming world's equivalent of English
        or even
        @xlinkpaths = map /xlink (.+?)/, @xlinks;

        you think you're getting undefs (but you're not in this case)? simply prepend  grep defined,  map ...

        Well, yes, you are right. Actually I started with the short version that Anonymous Monk gave, and I thought it would be confusing for someone not used to reading that sort of things. I should have gone for your first solution when I wanted to make it more verbose.

      In the interest of balancing clarity and robustness with map, I would suggest the following as an example.

      map { /xlink (.+?) /; $1 ? $1 : (); } @xlinks

      Of course the trinary operator here is not needed. However, oen problem I have seen with map is accidently clobbering the return values if I am not careful. Therefore if I have a conditional in the return, I try to use a trinary operator at the very end.

      BTW, I don't mind putting significant code in a map block. However one does have to be careful about it. I think if the code gets too much, one is better off usually putting the code in a function and calling that rather than going with a for loop.