perl discard first array element in map operation

2019-07-17 20:07发布

I'm starting to harness the power of perl map, and have run into a question that I couldn't find an answer to. Basically I am parsing the return of a unix command which has a header line that I don't need, and then 2 lines of information per item. Currently, I am doing this:

(undef, @ret) = map { [split /\n/] } split(/(?:Host: )/, `cat /proc/scsi/scsi`);

Which works fine to skip the header and give me one array element per "useful" line of text. However, I want to build a hash instead, which I would know how to do except for that extra line. So how can I, in a single line of code, ignore that first array element, to allow me to create the hash? I was thinking somewhere along the lines of a slice or a splice, but I would need to know the size of the array created by the split on Host (is that possible?). I guess I could also do a (undef,undef, %ret) = map {...} but if this could be done with a slice or a splice, that would be great to learn how.

2条回答
倾城 Initia
2楼-- · 2019-07-17 20:14

One way to remove the first element from a split and still be able to chain a more commands would be to use grep with a state variable:

use strict;
use warnings;

my @lines = do {
    my $line = 0;
    grep {++$line > 1} split /\n/, "1\n2\n3\n4\n5\n6\n7\n"
};

print "@lines";

Output:

2 3 4 5 6 7

However, I think you're trying to do too much in a single line of code.

Since it appears you're just reading a file, I would suggest that you use Perl to open the file instead of shelling out to cat.

Assuming your key / value delimiter is a colon, the following is how I'd recommend you construct your logic:

use strict;
use warnings;
use autodie;

my %hash = do {
    open my $fh, '<', '/proc/scsi/scsi';
    <$fh>; # Skip Header Row
    map {chomp; split /:/, $_, 2} <$fh>
};
查看更多
迷人小祖宗
3楼-- · 2019-07-17 20:15

heh, well, it seems it was indeed as easy as I thought, I had just forgotten about the -1 index position, so, the solution for anyone that might be interested in doing something like this is:

@ret = map { [split /\n/] } (split(/(?:Host: )/, `cat /proc/scsi/scsi`))[1,-1];

to get it to skip the first array element returned by the split on Host.


(The above is completely wrong, as pointed out by @M42, keeping it there for reference only.)

For those interested, this is the final code I came up with (based on @Miller's awesome approach, with some simplifications) to my problem:

my $i;
my $split_RE = qr(\S+|ANSI  SCSI revision); # you can create the precise list of keys here
my %attached = map {
        chomp;
        my ($host, %infohash) = split(/\s*($split_RE):\s*/);
        ($host => {%infohash} ) # FIXME: one host can have multiple devices.. an array with push() needs to be used here.. somehow (might have to avoid use of map)
        } 
    grep{$i++} # remove/ignore first line
    (split(/(?:Host: )/, `cat /proc/scsi/scsi`));

I still need to address the host clobberin' time, as pointed out by @ThisSuitIsBlackNot, but this is not urgent to my problem, and should turn out to be straight-forward if not trivial.

查看更多
登录 后发表回答