Can anyone tell me where I am wrong? I can't figure it out....
Basically what my code is trying to do is to read the files and create a hash for each file, these hashes are organized into on hash. The user would input two parameters, one is the key of the outer hash, and the other is for the one inside.
The ones I input are city and PIT; the same as the parameter I wrote before the line that breaks down....
I tried thousands of times, I keep getting this error: Can't use an undefined value as a HASH reference I have commented that line out in the code.
The two files are cities.txt; school.txt.
Their content are just as below:
PIT\tPittsburgh
NY\tNewYork
#!/bin/perl -w
use strict;
use Data::Dumper;
our %hash_all = ();
sub readHash{
my #vars = #_;
my $filename = $vars[0];
my %iptable = ();
if(open(IN,$filename.".txt")) {
while(<IN>) {
my #tmp = split(/\t/);
$iptable{$tmp[0]} = $tmp[1];
}
}
return %iptable;
}
sub loadAll{
my %school = readHash("school");
my %city = readHash("cities");
$hash_all{school} = \%school;
$hash_all{city} = \%city;
print Dumper(\%hash_all);
}
sub queryValue{
my #pars = #_;
my $key1 = $pars[0];
my $key2 = $pars[1];
print "key1".$key1;
print "key2".$key2;
print Dumper(\%hash_all);
my %temp = %{$hash_all{"city"}};#THIS LINE WORKS
print $temp{"PIT"}; #THIS LINE WORKS
my %temp2 = %{$hash_all{$key1}};#THIS LINE HAS AN ERROR
print $temp2{$key2};
}
loadAll();
my $par1 = <>;
my $par2 = <>;
queryValue($par1,$par2);
Your problem is probably that when you read in $par1 and $par2, they include newlines at the end. So you end up looking for a hash key like "city\n", which is not the same as "city".
Make sure you use chomp on your input parameters, like chomp($par1) . That should take care of it.
Related
I'm trying to pass from Main.pl to a sub (in ReadConfigFile.pm) a hash by reference and a scalar value. The scalar variable is the path to a config file and once this file is opened I want to fill a hash with some of its values. How do i pass a hash by reference and a scalar so that I then have the hash values available to use in Main.pl
I have done a lot of reading but cant get this to work. I realise i cant do = #_; in my sub as that is creating a new hash.
Ive tried following the prototype method, this fills the hash ok but back in Main.pl the hash is empty.
Main.pl
# Read the config file. Return 3 scalars and a hash
my %apps;
my ($schema, $directory, $staticFile) = readConfigFile(\%apps, $configFilePath);
my %app_list = %apps; # ive tried this in, out and in a variety of states
foreach my $name (sort keys %app_list) {
print "\nMAIN $name";
}
# this is empty
ReadConfigFile.pm
sub readConfigFile (\%$) {
my ($apps_ref, $configFilePath) = #_;
# also tried
# $apps_ref = shift but then configFilePath is empty
# linearray is each line from open config file split by :
$apps_ref{$lineArray[1]}{id} = $lineArray[1];
$apps_ref{$lineArray[1]}{name} = $lineArray[2];
$schema = $lineArray[1];
$directory = $lineArray[1];
$staticFile = $lineArray[1];
return ($schema, $directory, $staticFile);
configFile.txt
APP:1101:ACTIVITY
APP:1102:EVENTS
APP:1103:PERFORMANCE
APP:1104:LOCATION
STATIC_FILE:static_file.sql
SCHEMA:CAASS
DIRECTORY:CAASS
I want to get the 3 scalar variable returned and the hash so i can use them throughout Main.pl and pass to other subs.
I have also tried passing in just the configfilename and returning 4 variables, the 3 scalars and the hash.
I expect someone will crack this in minutes but i just cant work out the combination of \ and # and % and $ to make it work.
Thanks for any help or ideas.
Edit 1:
Main.pl
my %apps;
my ($schema, $directory, $staticFile) = readConfigFile(\%apps, $configFilePath);
foreach my $name (sort keys %apps) {
print "\nMAIN $name";
}
ReadConfigFile
sub readConfigFile () {
my $apps_ref = shift;
my $configFilePath = $_[0];
#Fill It
$apps_ref{$lineArray[1]}{id} = $lineArray[1];
$apps_ref{$lineArray[1]}{name} = $lineArray[2];
# This shows results
foreach my $name (sort keys %apps_ref) {
print "\nreadConfigFile $name";
}
But the values arent coming back into Main.pl
edit 2:
So im still interested in how the above can be made to work. But ive attacked it a different way and it works
Main.pl
my ($schema, $directory, $staticFile, %apps) = readConfigFile($configFilePath);
foreach my $name (sort keys %apps) {
print "\nMAIN $name";
}
ReadConfigFile
sub readConfigFile () {
my $configFilePath = $_[0];
my %apps;
#Fill It
%apps{$lineArray[1]}{id} = $lineArray[1];
$apps{$lineArray[1]}{name} = $lineArray[2];
foreach my $name (sort keys %apps) {
print "\nreadConfigFile $name";
}
return ($schema, $directory, $staticFile, %apps);
Both sets off output show.
There is no implicit 'pass by reference' in Perl*. Everything is passed the same way - as a list of scalars, by alias (thus passing a hash itself will instead pass the list of its keys and values*). But you can create a reference, pass it, and then dereference it to use it - and references can be copied around without copying the underlying structure.
use strict;
use warnings;
my %hash;
my $ref = \%hash;
my $copy = $ref;
$copy->{a} = 1;
print "$ref->{a}\n"; # also 1
References will maintain their referenced structure after a my (...) = #_; or my $foo = shift; assignment in a subroutine.
use strict;
use warnings;
sub foo {
my ($ref, $key) = #_;
$ref->{$key} = 42;
}
my %hash;
foo(\%hash, 'foo');
print "$hash{foo}\n"; # 42
See https://p3rl.org/REF for the relevant documentation on Perl references.
Since you are already passing a reference, there is no need for your (\%$) prototype: you can just remove it from the subroutine definition.
*except sort of with prototypes, but it's better to avoid them in most cases.
The propose of the script to grep some value from some data table in the ASCII files.
I modified the
script which I posted yesterday.
Now it barely works. I wonder if it is the proper way to move a file handle in this way.
The usage is still the same
myscript.pl targetfolder/*> result.csv
F is my file handle.
The argument I passed to the subroutine is the scalar $_, which is used by the if condition. When I want to move downward in my subroutine next if 1..4 will not work, so I repeat $a = <F>; a few times to achieve moving file handle downward.
But I think this is not a proper way to move the same file handle both in my main code and my subroutine. I am not sure it will really go through every line. I need your advice.
myscript.pl
#Report strip
use warnings;
use strict;
##Print the title
Tfms2();
##Print the title
print "\n";
#ff = <#ARGV>;
foreach $ff ( #ff ) {
open (F, $ff);
#fswf = #fschuck = #fsxpos = #fsypos = #fsdev = #csnom = "";
#cswf = #cschuck = #csxpos = #csypos = #csnom = ""; # is there an efficient way?
while (<F>) {
Mfms2();
Mfms3();
}
$r = 1;
while ( $r <= $#fswf ) { # because #fsws is the largest array
Cfms3();
print "\n";
$r++;
}
close (F);
}
##==========================================================================================
##Subs
##==========================================================================================
##FS II
sub Tfms2 {
print "FS_Wafer,FS_ChuckID,FS_pos_X,FS_pos_Y,FS_deviation,CS_Wafer,CS_ChuckID,CS_pos_X,CS_pos_Y,CS_NofWafer_Ident_Spot";
}
sub Mfms2 {
if ( /^F\sM\sSTATISTICS\sII$/ ) {
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$r = 1;
#b = "";
while ( $a !~ /\+\-/ ) {
chomp $a;
#b = split / *\| */, $a;
$fswf[$r] = $b[1];
$fschuck[$r] = $b[2];
$fsxpos[$r] = $b[3];
$fsypos[$r] = $b[4];
$fsdev[$r] = $b[5];
$r++;
$a = (<F>);
#b = "";
}
}
}
##FS III
sub Mfms3 {
if ( /^F\sM\sSTATISTICS\sIII$/ ) {
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$a = (<F>);
$r = 1;
#b = "";
while ( $a !~ /\+\-/ ) {
chomp $a;
#b = split / *\| */, $a;
$cswf[$r] = $b[1];
$cschuck[$r] = $b[2];
$csxpos[$r] = $b[3];
$csypos[$r] = $b[4];
$csnom[$r] = $b[5];
$r++;
$a = (<F>);
#b = "";
}
}
}
sub Cfms3 {
print "$fswf[$r],$fschuck[$r],$fsxpos[$r],$fsypos[$r],$fsdev[$r],";
print "$cswf[$r],$cschuck[$r],$csxpos[$r],$csypos[$r],$csnom[$r],";
}
You have forgotten to tell us what the program is supposed to do, so it's very hard to be any help. There might be more information in your previous question, but we don't all read every question here and you don't even include a link to your previous question.
The answer to your question is that you can use seek() to move to an arbitrary position in a file. You might also find it useful to look at tell() which can tell you where you currently are in a file.
But I don't think that information will be particularly helpful to you as you seem rather confused about what you're trying to do. If you were to explain your task in a bit more detail, then I strongly suspect that we could help you (and I also suspect that help would largely involve rewriting your code from scratch). But until you give us details, all we can do is to point out some of the more obvious problems with your code.
You should always include use strict and use warnings in your code.
I don't think that #ff = <#ARGV> does what you think it does. I think you want #ff = #ARGV instead. And, even then, that feels like you're copying #ARGV pointlessly.
Having an array (#ff) and a scalar ($ff) with the same name is going to confuse someone at some point. And, more generally, you need to put more effort into naming your variables clearly.
Please use the three-argument version of open() along with lexical filehandles. You should also always check the return code from open() - open my $fh, "<", $ff or die "Could not open $ff: $!".
Your lines like #fswf=#fschuck=#fsxpos=#fsypos=#fsdev=#csnom="" aren't doing what you think they are doing. You end up with a lot of arrays each of which contain a single element. Better to just declare the arrays with my - Perl will create them empty (my (#fswf, #fschuck, #fsxpos, ...)).
If you really need to skip eight lines when reading from your file then it's much clearer to write: $a = <F> for 1 .. 8.
You are making heavy use of global variables. There's a good reason why this goes against software engineering best practices. It makes your code far more fragile than it needs to be.
All in all, you seem to be guessing at a solution here, and that's never a good approach. As I said above, we'd like to help you, but without a lot more information that's going to be almost impossible.
Update: Looking at your code a bit more closely, I see that you are storing the data that you parse from the files in a number of arrays. Each array contains data from a single column of the input file and in order to get all of the data from a single row, you need to access each of these arrays using the same index value. This isn't a very good idea. Splitting linked data across different variables is a recipe for disaster. A far better idea would be to store each record in a hash (where the key would denote the data item that is stored) and to store references to all of these hashes in an array. This brings all of your data together in a single variable.
Updated update: I don't know enough about your data to be sure, but here's the kind of approach I would take. I've only parsed the data and then used Data::Dumper to display the parsed data structure. Producing better output is left as an exercise for the reader :-)
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
#ARGV or die "Usage: $0 file [file...]\n";
# Define two list of keys for the different types
my %cols = (
II => [qw(wf chuck xpos ypos dev)],
III => [qw(wf chuck xpos ypos nom)],
);
my #data; # To store the parsed data
my $cur_type; # Keep track of the current type of record
while (<>) {
# Look for a record header and determine which type of
# record we're dealing with (I think 'II' or 'III').
if (/^F M STATISTICS (III?)$/) {
$cur_type = $1;
next;
}
# Skip lines that are just headers/footers
next if /\+[-=]/;
# Skip lines that don't include data
next unless /\d/;
chomp;
# Remove the start and end of the line
s/^\|\s+//;
s/\s+\|$//;
# Store the data in a hash with the correct keys
# for this type of record
my %rec;
#rec{#{$cols{$cur_type}}} = split /\s+\|\s+/;
# Store a reference to our hash in #data
push #data, \%rec;
}
# Dump the contents of #data
say Dumper \#data;
I am looking to parse a tab delimited text file into a nested hash with a subroutine. Each file row will be keyed by a unique id from a uid column(s), with the header row as nested keys. Which column(s) is(are) to become the uid changes (as sometimes there isn't a unique column, so the uid has to be a combination of columns). My issue is with the $uid variable, which I pass as a non-interpolated string. When I try to use it inside the subroutine in an interpolated way, it will only give me the non-interpolated value:
use strict;
use warnings;
my $lofrow = tablehash($lof_file, '$row{gene}', "transcript", "ENST");
##sub to generate table hash from file w/ headers
##input values are file, uid, header starter, row starter, max column number
##returns hash reference (deref it)
sub tablehash {
my ($file, $uid, $headstart, $rowstart, $colnum) = #_;
if (!$colnum){ # takes care of a unknown number of columns
$colnum = 0;
}
open(INA, $file) or die "failed to open $file, $!\n";
my %table; # permanent hash table
my %row; # hash of column values for each row
my #names = (); # column headers
my #values = (); # line/row values
while (chomp(my $line = <INA>)){ # reading lines for lof info
if ($line =~ /^$headstart/){
#names = split(/\t/, $line, $colnum);
} elsif ($line =~ /^$rowstart/){ # splitting lof info columns into variables
#values = split(/\t/, $line, $colnum);
#row{#names} = #values;
print qq($uid\t$row{gene}\n); # problem: prints "$row{gene} ACB1"
$table{"$uid"} = { %row }; # puts row hash into permanent hash, but with $row{gene} key)
}
}
close INA;
return \%table;
}
I am out of ideas. I could put $table{$row{$uid}} and simply pass "gene", but in a couple of instances I want to have a $uid of "$row{gene}|$row{rsid}" producing $table{ACB1|123456}
Interpolation is a feature of the Perl parser. When you write something like
"foo $bar baz"
, Perl compiles it into something like
'foo ' . $bar . ' $baz'
It does not interpret data at runtime.
What you have is a string where one of the characters happens to be $ but that has no special effect.
There are at least two possible ways to do something like what you want. One of them is to use a function, not a string. (Which makes sense because interpolation really means concatenation at runtime, and the way to pass code around is to wrap it in a function.)
my $lofrow = tablehash($lof_file, sub { my ($row) = #_; $row->{gene} }, "transcript", "ENST");
sub tablehash {
my ($file, $mkuid, $headstart, $rowstart, $colnum) = #_;
...
my $uid = $mkuid->(\%row);
$table{$uid} = { %row };
Here $mkuid isn't a string but a reference to a function that (given a hash reference) returns a uid string. tablehash calls it, passing a reference to %row to it. You can then later change it to e.g.
my $lofrow = tablehash($lof_file, sub { my ($row) = #_; "$row->{gene}|$row->{rsid}" }, "transcript", "ENST");
Another solution is to use what amounts to a template string:
my $lofrow = tablehash($lof_file, "gene|rsid", "transcript", "ENST");
sub tablehash {
my ($file, $uid_template, $headstart, $rowstart, $colnum) = #_;
...
(my $uid = $uid_template) =~ s/(\w+)/$row{$1}/g;
$table{$uid} = { %row };
The s/// code goes through the template string and manually replaces every word by the corresponding value from %row.
Random notes:
Bonus points for using strict and warnings.
if (!$colnum) { $colnum = 0; } can be simplified to $colnum ||= 0;.
Use lexical variables instead of bareword filehandles. Barewords are effectively global variables (and syntactically awkward because they're not first-class citizens of the language).
Always use the 3-argument form of open to avoid unexpected interpretation of the second argument.
Include the name of your program in error messages (either explicitly with $0 or implicitly by omitting \n from die).
my #foo = (); my %bar = (); is redundant and can be simplified to my #foo; my %bar;. Arrays and hashes start out empty; overwriting them with an empty list is pointless.
chomp(my $line = <INA>) will throw a warning when you reach EOF (because you're trying to chomp a variable containing undef).
my %row; should probably be declared inside the loop. It looks like it's supposed to only contain values from the current line.
Suggestion:
open my $fh, '<', $file or die "$0: can't open $file: $!\n";
while (my $line = readline $fh) {
chomp $line;
...
}
#! /usr/bin/perl
use strict;
use warnings;
use File::stat;
my $file_name = 0;
my $info = 0;
my $ret_mode = 0;
my $size;
my $last_mod;
my #array_file;
my $index = 0;
my #array_mode;
my #array_size;
my #array_last_mod;
foreach(#ARGV){
$file_name = $_;
$info = stat($file_name);
$size = $info->size;
$last_mod = scalar(localtime($info->mtime));
$ret_mode = $info->mode;
$ret_mode = $ret_mode & 0777;
$array_file[$index] = ($file_name);
$array_mode[$index] = ($ret_mode);
$array_size[$index] = ($size);
$array_last_mod[$index] = ($last_mod);
$ret_mode = 0;
$index++;
}
my #array_arrays = (#array_file, #array_mode, #array_size, #array_last_mod);
my $array_ref = \#array_arrays;
my $i = 0;
for(#$array_ref){
print "#$array_ref[$i]\n";
$i++;
}
I have created an array of arrays and I want to print the filename,mmode,size and last access time from the array of arrays created. Its not printing any values with,
for(#$array_ref){
print "#$array_ref[$i]\n";
$i++;
}
my #array_arrays = (#array_file, #array_mode, #array_size, #array_last_mod);
This statement does not create an array of arrays. Instead, It flattens the various arrays into one big flat list and then assigns that to #array_arrays. You want to assign array references. Obtain those with the reference operator \:
my #array_arrays = (\#array_file, \#array_mode, \#array_size, \#array_last_mod);
or with the shortcut
my #array_arrays = \(#array_file, #array_mode, #array_size, #array_last_mod);
Even then, your last foreach-loop is wrong. You probably meant
for my $i (0 .. $#{ $array_arrays[0] }) {
for my $aref (#array_arrays) {
print $aref->[$i], "\n";
}
}
or something similar.
Your code style could be improved.
Please don't declare all your variables at the top. Declare them in the tightest scope possible. Try to declare them at the point of initialization, e.g.
for my $file_name (#ARGV) {
my $info = stat($file_name);
my size = $info->size;
...
}
Don't prefix your array names with array_. The # sigil and/or subscripting with the [...] operator makes it clear that these are arrays.
$ret_mode & 0777 – The result should be $ret_mode itself: 0777 is 0b111111111. I.e. this removes all but the last 9 bits – you wouldn't care if there were more to the left.
$last_mod = scalar(localtime($info->mtime)); – due to the scalar assignment, localtime is already executed in scalar context. No need to make this explicit.
my $index = 0; ... for (...) { $array[$index] = ...; $index++ }. Please not. Just use push: for (...) { push #array, ... }. Don't maintain indices yourself unless you have to.
$ret_mode = 0; Why? you assign a new value during the next iteration anyway. Note that you should declare this variables inside the loop (see my point about tight scopes), which would create a new variable in each iteration, making this even more useless.
my $array_ref = \#array_arrays; .. #$array_ref[$i]. Isn't this a bit backwards? $array_arrays[$i] would work just as well. Note that in your defeferencing, # is probably the wrong sigil. You meant $$array_ref[$i].
Let's try a little something different.
First off, there's a nice syntax using the -> for referenced arrays and hashes. Here I am going to make a array of people. I'll make a hash of %person that contains all that person's information:
my %person;
my $person{NAME} = "Bob";
my $person{JOB} = "Programmer";
my $person{PHONE} = "555-1234";
Now, I'll put it into an array:
my #array
my $array[0] = \%person;
I could reference the person in the array this way:
print ${$array[0]}{NAME} . "\n"; #Prints Bob
print ${$array[0]}{JOB} . "\n"; #Prints Porgrammer
But, Perl gives me a nice clean way to do this:
print $array[0]->{NAME} . "\n"; #Prints Bob
print $array[0]->{JOB} . "\n"; #Prints Progammer
In fact, I could skip the hash all together. Here I am adding Jill to my array:
$array[1]->{NAME} = "Jill";
$array[1]->{JOB} = "DBA";
$array[1]->{PHONE} = "555-5555";
You can see this is a much simpler way to use references. It's easier to see what is going on and takes fewer lines of code.
You can refer to an array of an array like this:
$myarray[1]->[3] = 42;
Or have a hash which stores an array. In this day and age, who has only a single phone number?:
$person[1]->{PHONE}->[0] = "555-4567";
$person[1]->{PHONE}->[1] = "555-4444";
Or, to make it even more complex we could have a hash of a hash of an array:
$person[1]->{PHONE}->{CELL}->[0] = "555-1111";
$person[1]->{PHONE}->{CELL}->[1] = "555-2222";
$person[1]->{PHONE}->{HOME}->[0] = "555-3333";
$person[1]->{PHONE}->{JOB}->[0] = "555-4444";
$person[1]->{PHONE}->{JOB}->[1] = "555-5555";
Using this syntax will really help clean up a lot of your code. You won't have to store the information into individual structures that are then only used to make references. Instead, you can simple setup your structure the way you want without intermediary steps.
Now to your problem: You're trying to store a bunch of information about files into a series of arrays. What you're hoping is that $array_mode[1] goes with $array_file[1] and you have to keep all of these arrays in sync. This is a pain and it is complex.
The entire purpose of using references is to eliminate this need for multiple variables. If you're going to use references, why not simply store your entire file structure into a single array.
What you really, really want is an array of hash references. And, that hash reference will be keyed based upon your file attributes. Here is your code restructured into using an array of hash references. I didn't even bother to check the rest of it. For example, I'm not sure how your localtime thing will work:
use strict;
use warnings;
use feature qw(say);
use File::stat;
my #files;
for my $file ( #ARGV ) {
my $info = stat( $file );
my $file = {}; #This will be a reference to a hash
$file->{NAME} = $file;
$file->{SIZE} = $info->size;
$file->{RET_MODE} = $info->mode & 0777;
$file->{LAST_MOD} = localtime $info->mtime; #Does this work?
push #files, $file #Pushes the hash reference onto the array
}
That's way shorter and cleaner. Plus, you know that $files[0]->{NAME} goes with $files[1]->{SIZE}, and if you remove $files[0] from your array, or transfer it to another variable, all of the attributes of that file go together.
Here's how you'd print it out:
for my $file ( #files ) {
say "File Name: " . $file->{NAME};
say "File Size: " . $file->{SIZE};
say "Last Modified: " . $file->{LAST_MOD};
say "File Mode: " . $file->{RET_MODE};
}
Simple and easy to do.
However, I would argue what you really want is a hash of hashes. Let your file name be the key to your main hash, and let {SIZE}, {LAST_MOD}, and {RET_MODE} be the keys to your sub hash:
my %files = {}; #This is a hash of hashes
for my $file_name ( #ARGV ) {
my $info = stat( $file );
$files{$file_name}->{SIZE} = $info->size;
$files{$file_name}->{RET_MODE} = $info->mode & 0777;
$files{$file_name}->{LAST_MOD} = localtime $info->mtime; #Does this work?
}
Now if someone asks, "When was foo.txt last modified?", you can say:
say "File 'foo.txt' was last modified on " . $file{foo.txt}->{LAST_MOD};
And to print out your entire structure:
for my $file_name ( sort keys %files ) {
say "File: $file_name";
for my attribute ( sort keys %{ $file_name } ) {
say " $attribute: " . $files{$file_name}->{$attribute};
}
}
Next step is to learn about Object Oriented Perl! Object Oriented Perl uses these types of references, but will greatly simplify the handling of these references, so you make fewer programming mistakes.
So I have a text file with four sets of data on a line, such as aa bb username password. So far I have been able to parse through the first line of the file using substrings and indices, and assigning each of the four to variables.
My goal is to use an array and chomp through each line and assign them to the four variables, and than to match an user inputted argument to the first variable, and use the four variables in that correct line.
For example, this would be the text file:
"aa bb cc dd"
"ee ff gg hh"
And depending on whether the user inputs "aa" or "ee" as the argument, it would use that line's set of arguments in the code.
I am trying to get up a basic array and chomp through it based on a condition for the first variable, essentially.
Here is my code for the four variables for the first line, but like I said, this only works for the first line in the text file:
local $/;
open(FILE, $configfile) or die "Can't read config file 'filename' [$!]\n";
my $document = <FILE>;
close (FILE);
my $string = $document;
my $substring = " ";
my $Index = index($string, $substring);
my $aa = substr($string, 0, $Index);
my $newstring = substr($string, $Index+1);
my $Index2 = index($newstring, $substring);
my $bb = substr($newstring, 0, $Index2);
my $newstring2 = substr($newstring, $Index2+1);
my $Index3 = index($newstring2, $substring);
my $cc = substr($newstring2, 0, $Index3);
my $newstring3 = substr($newstring2, $Index3+1);
my $Index4 = index($newstring3, $substring);
my $dd = substr($newstring3, 0, $Index4);
First of all, you can parse your whole line using split instead of running index and substring on them:
my ( $aa, $bb, $cc, $dd ) = split /\s+/, $line;
Even better, use an array:
my #array = split /\s+/, $line;
I think you're saying that you need to store each array of command parts into another array of lines. Is that correct? Take a look at this tutorial on references available in the Perl Documentation.
Perl has three different types of variables. The problem is that each of the types of variables of these stores only a single piece of data. Arrays and hashes may store lots of data, but only one piece of data can be stored in each element of a hash or array.
References allow you to get around this limitation. A reference is simply a pointer to another piece of data. For example, if $line = aa bb cc dd, doing this:
my #command_list = split /\s+/ $line;
Will give you the following:
$command_list[0] = "aa";
$command_list[1] = "bb";
$command_list[2] = "cc";
$command_list[3] = "dd";
You want to store #command_list into another structure. What you need is a reference to #command_list. To get a reference to it, you merely put a backslash in front of it:
my $reference = \#command_list;
This could be put into an array:
my #array;
$array[0] = $reference;
Now, I'm storing an entire array into a single element of an array.
To get back to the original structure from the reference, you put the correct sigil. Since this is an array, you put # in front of it:
my #new_array = #{ $reference };
If you want the first item in your reference without using having to transport it into another array, you could simply treat #{ $reference } as an array itself:
${ $reference }[0] = "aa";
Or, use the magic -> which makes the syntax a bit cleaner:
$reference->[0] = "aa";
Go through the tutorial. This will help you understand the full power of references, and how they can be used. Your program would look something like this:
use strict;
use warnings;
use feature qw(say); #Better print that print
use autodie; #Kills your program if the file can't be open
my $file = [...] #Somehow get the file you're reading in...
open my $file_fh, "<", $file;
my #command_list;
while ( my $line = <$file_fh> ) {
chomp $line;
my #line_list = split /\s+/, $line;
push #command_list, \#line_list;
}
Note that push #command_list, \#line_list; is pushing a reference to one array into another. How do you get it back out? Simple:
for my $cmd_line_ref ( #command_list ) {
my $command = $cmd_line_ref->[0]; #This is the first element in your command
next unless $command eq $user_desires; # However you figure out what the user wants
my $line = join " ", #{ $cmd_line_ref } #Rejoins your command line once again
??? #Profit
}
Read the tutorial on references, and learn about join and split.
You are reading the whole file in the my $document = <FILE> line.
Try something like:
my #lines;
open my $file, '<', $configfile or die 'xxx';
while( <$file> ) {
chomp;
push #lines, [ split ]
}
And now #lines has an array of arrays with the information you need.
(EDIT) don't forget to lose the local $/; -- it's what is making you read the whole file at once.
my $document = <FILE> is reading in only the first line. Try using a while loop.
If you want to read all lines of the file at once - assuming it's a small file - you may want to use File::Slurp module:
use File::Slurp;
my #lines = File::Slurp::read_file($configfile);
foreach my $line (#lines) {
# do whatever
Also, you can use CPAN modules to split the strings into fields.
If they are single-space separated, simply read the whole file using a standard CSV parser (you can configure Text::CSV_XS to use any characater as separator). Example here: How can I parse downloaded CSV data with Perl?
If they are separated by random amount of whitespace, use #massa's advice below and use split function.