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.
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>
};
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.