Skipping extra line while inserting variable in perl - perl

I have this PERL subroutine
sub request
{
#INPUTS
....
....
my $users_info = #_[5] ;
my $req_exp = <<DOC;
#SN# Step $number
#DN# AUTHENT $type_authent + PFS SERVICE
#ED# $reponse
GET /$jonction/testussowt/printenv HTTP/1.1
Host : $HOST
$users_info
---
DOC
return $req_exp ;
}
if i call it like this
my $test = &request(other_arguments..,'user1');
print $test ;
I get the correct output ( i am showing only the last two lines that matter)
user1
---
But if i call it like this
my $var = &other_sub_that_returns_a_string;
my $test = &request(other_arguments..,$var);
print $test;
I got this extra empty line in my output
user1
---

Without the full code, it's hard to say exactly what's happening here.
Best guess is that your subroutine is returning with a line break at the end, or pulling the line break from somewhere else.
To fix that:
my $var = &other_sub_that_returns_a_string;
chomp($var);
my $test = &request(other_arguments..,$var);
print $test;
If you post your code, I can definitely take a look and try to help you figure out where the line break is coming from.

Related

Value not stored in file <Perl>

I am new to perl. My motive is to read some value from a txt file and use it in my perl script.
The text file is something like this ::
Servers ::
(local)
Tomas-Server1
Tomas-Server2
.........**
What i need to do is to get the 3rd line value (Tomas-Server2) and use it in my perl script. basically its calling the 3rd value to the perl script.
I have written a basic code for it ::
my($ServName1,$ServName,$ServName3) = getservername($servername);
my ($filenam) = 'data.txt';
my #Param = ();
open(INFILE,"<$filenam") or die "Couldn't open $filenam for reading\n";
while(<INFILE>) {
chop($_);
push(#Param,$_);
}
close(INFILE);
return #Param;
}
But when i try to use the "$ServName2" value , it does not return anything. I guess the value contained should be "(local)" for it.
There a few problems with you example data, but I think I understand what you are trying to do. Assuming that you want the third server value (and not just line #3) you would probably want to have code that looks something like this...
#!perl
#some includes
use strict;
use warnings;
use Data::Dumper;
#set some vars
my $TargetFileName="data_file.txt";
my $TargetServerNumber=3;
#call the sub
my #ServerArray=GetServerName($TargetFileName);
#did we get anything back?
if(#ServerArray) {
#yep, we got some content
print "Server number ".$TargetServerNumber."/".($#ServerArray+1)." is \"".$ServerArray[$TargetServerNumber-1]."\"\n"; #array numbers are 0-based
} else {
#nope, we read the file but didn't any matching content
print "No servers found in file \"".$TargetFileName."\".\n";
} #end if
print "\n";
print "Here's what was loaded into \#ServerArray...\n";
print Data::Dumper::Dumper(#ServerArray); #so you can see the full content of #ServerArray
print "All Done\n";
exit;
#----- subs go here -----
sub GetServerName {
my $DataFileName=shift; #pull in the first arg - this alters #_ for the rest of the sub, so take care when doing this
my #ReturnArray;
#do some QA
if(!$DataFileName) {die "You need to provide a file name to use this sub.\n"} #do we have a file name?
if(!stat($DataFileName)) {die "The requested file name of \"".$DataFileName."\" does not exist.\n";} #does the file exist?
open(INFILE, "<".$DataFileName) or die "Unable to open \"".$DataFileName."\" - ".$!;
#ok, read the file content
while(my $Line=<INFILE>) {
chop($Line);
$Line=~s/^\s+//g; #remove leading white spaces
$Line=~s/\s+$//g; #remove trailing white spaces
if(!$Line) {next;} #blank line, skip it
#check for the headers
if($Line=~m/^Servers/) {next;} #skip lines beginning with "Servers"
if($Line=~m/^\(local\)/) {next;} #skip lines beginning with "(local)"
#if we get here, we must want the line content, so add it to the array
push(#ReturnArray, $Line);
} #end while
close(INFILE);
#send the array data back, if any
return #ReturnArray
} #end GetServerName sub
__END__
stuff below here does not need to be commented
I admit this is not the best way to approach the problem, but like most Perl hacks, it works. You'll notice the code is a bit overkill and does validation checks before passing data to some operations - get into the habit of doing this in any language you work in. This will help you return more meaningful errors when things go wrong and will also catch data related problems. The sample code also does a bit of data cleanup because leading and trailing will likely cause you problems later on. Blank lines (after cleaning) are also removed.
BTW, the data file I used as an example look like this...
Servers ::
(local)
Tomas-Server1
Tomas-Server2
Tomas-Server3
Tomas-Server4
Tomas-Server5
Tomas-ServerLast
You never define a $ServName2 variable. The variable between $ServName1 and $ServName3 is just named $ServName, with no number.

Multiple text parsing and writing using the while statement, the diamond operator <> and $ARGV variable in Perl

I have some text files, inside a directory and i want to parse their content and write it to a file. So far the code i am using is this:
#!/usr/bin/perl
#The while loop repeats the execution of a block as long as a certain condition is evaluated true
use strict; # Always!
use warnings; # Always!
my $header = 1; # Flag to tell us to print the header
while (<*.txt>) { # read a line from a file
if ($header) {
# This is the first line, print the name of the file
**print "========= $ARGV ========\n";**
# reset the flag to a false value
$header = undef;
}
# Print out what we just read in
print;
}
continue { # This happens before the next iteration of the loop
# Check if we finished the previous file
$header = 1 if eof;
}
When i run this script i am only getting the headers of the files, plus a compiled.txt entry.
I also receive the following message in cmd : use of uninitialized $ARGV in concatenation <.> or string at concat.pl line 12
So i guess i am doing something wrong and $ARGV isn't used at all. Plus instead of $header i should use something else in order to retrieve the text.
Need some assistance!
<*.txt> does not read a line from a file, even if you say so in a comment. It runs
glob '*.txt'
i.e. the while loop iterates over the file names, not over their contents. Use empty <> to iterate over all the files.
BTW, instead of $header = undef, you can use undef $header.
As I understand you want to print a header with the filename just before the first line, and concatenate them all to a new one. Then a one-liner could be enough for the task.
It checks first line with variable $. and closes the filehandle to reset its value between different input files:
perl -pe 'printf qq|=== %s ===\n|, $ARGV if $. == 1; close ARGV if eof' *.txt
An example in my machine yields:
=== file1.txt ===
one
=== file2.txt ===
one
two

new to Perl - CSV - find a string and print all numbers in that column

I've got a bunch of data in a CSV file, first row is all strings (all text and underscores), all subsequent rows are filled with numbers relating to said strings.
I'm trying to parse through the first line and find particular strings, remember which column that string was in, and then go through the rest of the file and get the data in the same column. I need to do this to three strings.
I've been using Text::CSV but I can't figure out how to get it to increment a counter until it finds the string in the first line and then go to the next line, get the data from that same column, etc. etc. Here's what I've tried so far:
while (<CSV>) {
if ($csv->parse($data)) {
my #field = $csv->fields;
my $count = 0;
for $column (#field) {
print ++$count, " => ", $column, "\n";
}
} else {
my $err = $csv->error_input;
print "Failed to parse line: $err";
}
}
Since $data is in line 1, it prints "1 $data" 25 times (# of lines in CSV file). How do I get it to remember which column it found $data in? Also, since I know all of the strings are in line 1, how do I get it to only parse through line 1, find all of the strings in #data, and then parse through the rest of the file, grabbing data from the necessary columns and putting it into a matrix or array of arrays?
Thanks for the help!
edit: I realized my questions were a bit poorly phrased. I don't know how to get the column number from CSV. How is this done?
Also, once I've got the column number, how do I tell it CSV to run through the subsequent lines and grab data from only that column?
Try something like this:
use strict;
use warnings;
use Text::CSV;
my $csv = Text::CSV->new({binary=>1});
my $thing_to_match = "blah";
my $matched_index;
my #stored_data = ();
while(my $row= $csv->getline(*DATA)) #grabs lines below __DATA__
#(near the end of the script)
{
my #fields = #$row;
#If we haven't found the matched index, yet, search for it.
if(not defined $matched_index)
{
foreach my $i(0..$#fields)
{
$matched_index = $i if($fields[$i] eq $thing_to_match);
}
}
#NOTE: We're pushing a *reference* to an array!
#Look at perldoc perldata
push #stored_data,\#fields;
}
die "Column for '$thing_to_match' not found!" unless defined $matched_index;
foreach my $row(#stored_data)
{
print $row->[$matched_index] . "\n";
}
__DATA__
stuff,more stuff,yet more stuff
"yes, this thing, is one item",blah,blarg
1,2,3
The output is:
more stuff
blah
2
I don't have time to write up a full example, but I wrote a module that might help you do this. Tie::Array::CSV uses some magic to make your csv file act like a Perl array of arrayrefs. In this way you can use your knowledge of Perl to interact with the file.
A word of warning though! One benefit of my module is that it is read/write. Since you only want read, be careful not to assign to it!

Perl Global variable uninitialized

I'm new to perl so please bear with me.
I have script that is parsing a CSV file. To make things easier to debug I am using a state machine FSA::Rules (works great love it).
Every thing is going well only now I need to make my logs make sense, as part of this I need to record line numbers so my program looks some thing like this.
my $line = '';
my $lineCount = 0;
sub do {
...
#CSV opened
...
#State machine stuff happens here
readLine;
if ($line =~ m/.*Pattern*/){
#do stuff
}
}
sub readLine{
$line = <CSV>;
$lineCount ++;
}
But I get the following error
Use of uninitialized value $line in pattern match (m//) at
Any one know why $line would not be initialized?
Thanks.
When you reach end of file, $line = <CSV> will assign the undefined value to $line. The usual idiom is to check whether the readline function (which is implicitly called by the <> operator) returned a good value or not before proceeding ...
while (my $line = <CSV>) {
# guaranteed that $line has a defined value
...
}
but you with your sequence of calls, you are avoiding that check. Your current code also increments $lineCount even when <CSV> does not return a good value, which may not be what you want either.

Weird behavior with Perl string concatenation

I'm working on a pretty simple script, reading a maplist.txt file and using the \n separated map names in it to build a command string - however, I'm getting some unexpected behavior.
My full code:
# compiles a map pack from maplist.txt
# for every server.
# Filipe Dobreira <dobreira#gmail.com>
# v1 # Sept. 2011
use strict;
my #servers = <*>;
foreach my $server (#servers)
{
# we only want folders:
next if -f $server;
print "server: $server\n";
my $maplist = $server . '/orangebox/cstrike/maplist.txt';
my $mapdir = $server . '/orangebox/cstrike/maps';
print " maplist: $maplist\n";
print " map folder: $mapdir\n";
# check if the maplist actually exists:
if(!(-e $maplist))
{
print "!!! failed to find $maplist\n";
next;
}
open MAPLIST, "<$maplist";
foreach my $map (<MAPLIST>)
{
chomp($map);
next if !$map;
# full path to the map file:
my $mapfile = "$mapdir/$map.bsp";
print "$mapfile\n";
}
}
Where I declare $mapfile, I expect the result to be something like:
zombieescape1/orangebox/cstrike/maps/ze_stargate_escape_v8.bsp
However, it seems like the concatenation is being made to the START of the string, and the final result ends up being something like:
.bspiescape1/orangebox/cstrike/maps/ze_stargate_escape_v8
So the .bsp portion is actually being written over the start of the leftmost string. I have very little perl experience, and I can only assume this is me failing to understand some quirk or operator behavior.
Note: I've also tried using "${mapdir}/${map}.bsp", concatenating everything with the dot operator, and a join "", $mapdir, $map, ".bsp", with the same result.
Thanks in advance.
PS: for reference, here's what a maplist.txtlooks like:
zm_3dubka_v3
zm_4way_tunnel_v2
zm_abstractchode_pyramid2
zm_anotheruglyzmap_v1e
zm_app7e_betterbworld_JDfix_v3
zm_atix_helicopter_mini
zm_base_winter_beta3
zm_battleforce_panic_ua
zm_black_lion_macd_v8
zm_bunker_f57_v2
zm_burbsdelchode_b3
zm_choddarena_b12
zm_choddasnowpanic_b4
zm_citylife_V2b
zm_crazycity
zm_deep_thought_nv
zm_desert_fortress_v2
ZM_desprerados_a1
zm_doomlike_station_v2
zm_dust_arena_v1_final
zm_exhibit_night_2F
zm_facility_v1
zm_farm3_nav72
zm_firewall_samarkand
zm_fortress_b7
zm_ghs_flats
zm_gl33m4x_errata
zm_idm_hauntedhouse_v1
zm_industry_v2
zm_kruma_kakariko_village_006
zm_kruma_panic_004
zm_lila_off!ce_v4
zm_little_city_v5pf_fix
zm_moonlight_v3_pF
zm_moon_roflicious_pF_02
zm_moocbblechode_b2
zm_mountain_b2
zm_neko_abura_v2
zm_neko_athletic_park_v2
zm_novum_v3_JDfix
zm_ocx_orly_v4
zm_officeattack_b5a
zm_officerush_betav7
zm_officesspace_pfss
zm_omi_facility_pfv2
zm_penumbra_PF3
zm_raindance_ak_v2
zm_roflicious_pfcf2
zm_roy_abandoned_canals_new
zm_roy_barricade_factory
zm_roy_highway
zm_roy_industrial_complex
zm_roy_old_industrial_pF
zm_roy_the_ship_pf
zm_roy_zombieranch_night_b4
zm_survival_f2a
zm_temple_v3pf
zm_towers_v3
zm_tx_highschool_zkedit_v2
zm_unpanicv2_pF
zm_vc2_office_redone_b1
zm_wasteyard_beta3
zm_winterfun_b4a
zm_wtfhax_v6
zm_wtfhax_v6e
zm_wwt_twinsteel_v8
I'd guess that the maplist.txt has non-unix line endings - probably dos - and as result you see what looks like prepending.
The problem is that the chomp() is only consuming one of the two line ending characters, leaving the carriage return behind.
You might find that if you set the Perl special variable $/ (input record seperator) before opening the map list, that chomp then does the job - it will consume both line-ending characters.
$/ = qq{\r\n};
Another solution would be to convert the line endings in the file before processing, perhaps using dos2unix.