I finally understand the tao of slices. After reading through Effective Perl Programming this weekend, I have figured out a problem that was holding me back. While I am sure that the majority of you have had this epiphany long ago, I feel the need to beat my chest and howl about it...

It began, while I was trying to read in, modify and write out shadow file entries:

open(TEMPFILE, ">${TempPath}/temp_shad") || die; open(SHADFILE, "${TempPath}/$passwdfile") || die; while (<SHADFILE>) { chomp; (@entry) = split(/:/,$_); $newentry = join(":", @entry); print TEMPFILE "$newentry\n";

This didn't work, because, as you know (if you are a *nix SA), the shadow file looks like so: foo:asDflkj123./d:6445::::::

So when I wrote it back out,this is what I got:

foo:asDflkj123./d:6445

In comes the concept of slicing <*trumpet sounds*>. Thru the slice, I was able to define those empty fields, while not having to worry about assigning bogus values that I would have to delete. Also, I didn't have to deal w/ trying to assign each field to an individually defined variable and the long lines of code that followed:

($user,$passwd,$mod,$f1,$f2,$f3,$f4,$f5) = split(/:/,$_); $newentry = "$user" . ":" . "$passwd" . ":" . "$mod" . ":" . "$f1" . " +:" . "$f2" . ":" . "$f3" . ":" . "$f4" . ":" . "$f5)\n";
The above code works, but geez, I don't want to have to type all of that each time I need to modify the shadow file.

That code became this:

(@entry[0..8]) = split(/:/,$_); $newentry = join(":", @entry[0..8]);
In short, it made a potentially arduous task, very simple and easier to read.

Now, back to the regularly scheduled program...

Enlightened,

bb

update: fixed # of colons in shadow file entry, thanks davorg

Replies are listed 'Best First'.
Re: I am now slice-aware
by davorg (Chancellor) on Apr 17, 2001 at 18:46 UTC

    Whilst array (and hash) are, indeed, most cool, your current problem could have been solved by investigating the rarely used third parameter to split.

    @entry = split(/:/, $_, 8); $newentry = join(":", @entry);

    Update: And if you're wondering why I only split into 8 elements when the original code used 9 (0 .. 8), I counted the ':' characters in the original data and saw that there were only 8 elements :)

    --
    <http://www.dave.org.uk>

    "Perl makes the fun jobs fun
    and the boring jobs bearable" - me

      Or, as tye enlightened me earlier, you can always use -1 if you have no idea about the number of entries you want, as -1 prevents split from discarding trailing empty fields:      $newentry = join(":", split(/:/, $_, -1));

        Any negative number would do. From perldoc -f split:

        "If LIMIT is specified and positive, splits into no more than that many fields (though it may split into fewer). If LIMIT is unspecified or zero, trailing null fields are stripped (which potential users of pop() would do well to remember). If LIMIT is negative, it is treated as if an arbitrarily large LIMIT had been specified."

        --
        <http://www.dave.org.uk>

        "Perl makes the fun jobs fun
        and the boring jobs bearable" - me

      Another possibility is, and I say this also to frankus below, to set the limit to -1. From perlfunc (5.6):
      into fewer). If LIMIT is unspecified or zero, trailing null fields are stripped (which potential users of `pop' would do well to remember). If LIMIT is negative, it is treated as if an arbitrarily large LIMIT had been specified.
      In other words, -1 would allow you to fetch any empty field, regardless of the number of fields, regardless of the placing of these fields (trailing are normally neglected, heading not).

      Oh yeah, for the supersplit fans among you, this behaviour can be achieved by adding the number to your argument list. Neat, isn't it?

      Hope this helps,

      Jeroen
      "We are not alone"(FZ)

      davorg,

      Don't forget the there is an element at the end, that is not bound by a colon (ergo 9 elements).

      uucp:NP:6445::::::

      When you use only 8 elements, you miss the last ":". I know I also tried 8.

      bb

        Oh, I didn't forget. The example in your original post was foo:asDflkj123./d:6445::::: which has (counts) seven colons - hence eight fields :)

        --
        <http://www.dave.org.uk>

        "Perl makes the fun jobs fun
        and the boring jobs bearable" - me

Re: I am now slice-aware
by frankus (Priest) on Apr 17, 2001 at 18:52 UTC

    Slices do rock and so does "Effective Perl Programming", what confuses me is why is the code:

    (@entry[0..8]) = split(/:/,$_); $newentry = join(":", @entry[0..8]);
    Do you need the brackets around the @entry? What am I missing?

    Why isn't it:

    @entry = split /:/; # Automagically uses $_. $newentry = join (":", @entry[0..8]);
    or on a dark day:$newentry = join(':',(split/:/)[0..8]);
    What am I missing? Please help.

    --
    
    Brother Frankus. updated to remove stupid assumption about arrays.

      You're missing the fact that split /:/ throws away any empty elements at the end of the string.

      Try this:

      $_ = '1:2:3:::::'; my @a = split /:/; print scalar @a; # prints 3
      --
      <http://www.dave.org.uk>

      "Perl makes the fun jobs fun
      and the boring jobs bearable" - me

        Just GREAT davorg!, now I have to go and amend my existing code. Wadda way to starta da week, if you want to snigger at my clumsier way:
        @foo=split/:/;<br> for ($#foo..$required_amt){push @foo, undef}
        Thanks to birdbrane for sharing :0).

        --
        
        Brother Frankus.
      Hmmm, probably because I didn't think about doing it that way. I just assumed that I had to define the 9 elements (yes, in the /etc/shadow file, there are always 9 elements) before I could join them. Of course,

      $newentry = join(':',(split/:/)[0..8]);

      is much cooler. Thanks for the input.

      Of course, davorg is right, I forgot that split has a third field that I could have done the same thing. Oh well.

      Not feeling as enlightened, mostly feeling like a knucklehead now. :))

      Thus I am birdbrane

      update: Actually, I have just included the relevant lines of code, there is an "if" statement between the "split" and "join". Otherwise, I would be splitting and rejoining, while doing nothing in between. Doing something for no reason, hmmm, maybe that should be my philosophy for life... :)