Appending a string variable to a fixed string in Perl - string

I have a variable that is entered at a prompt:
my $name = <>;
I want to append a fixed string '_one'to this (in a separate variable).
E.g. if $name = Smith then it becomes 'Smith_one'
I have tried several various ways which do not give me the right results, such as:
my $one = "${name}_one";
^ The _one appears on the next line when I print it out and when I use it, the _one is not included at all.
Also:
my $one = $name."_one";
^ The '_one' appears at the beginning of the string.
And:
my $end = '_one';
my $one = $name.$end;
or
my $one = "$name$end";
None of these produce the result I want, so I must be missing something related to how the input is formatted from the prompt, perhaps. Ideas appreciated!

Your problem is unrelated to string appending: When you read a line (e.g. via <>), then the record input separator is included in that string; this is usually a newline \n. To remove the newline, chomp the variable:
my $name = <STDIN>; # better use explicit filehandle unless you know what you are doing
# now $name eq "Smith\n"
chomp $name;
# now $name eq "Smith"
To interpolate a variable into a string, you usually don't need the ${name} syntax you used. These lines will all append _one to your string and create a new string:
"${name}_one" # what you used
"$name\_one" # _ must be escaped, else the variable $name_one would be interpolated
$name . "_one"
sprintf "%s_one", $name
# etc.
And this will append _one to your string and still store it in $name:
$name .= "_one"

Related

Getting the characters of a string up to the first "."

I'm attempting to use Perl's gethostnamebyaddr function. The annoying thing is that it returns the entire domain name in scalar format. I want to parse out only the hostname and discard the rest.
I'm using split to divide the domain name into an array and then taking only the first value but this doesn't seem to work.
#!/usr/bin/perl
use Socket;
my $name;
my $hostname;
my #tmpStr;
$name = gethostbyaddr(inet_aton("192.168.2.3"), AF_INET);
print "$name\n";
#tmpStr = split ".", $name;
$hostname = $tmpStr[0];
print "Host name is $hostname\n";
When the above code is executed, I get the following:
dc1-ent.ent.ped.local
Host name is
According to this website the return value is not a string but is rather a scalar value and so my attempt at splitting it doesn't work.
I can't figure out how to convert it to a string before I can split it or parse out the hostname by itself.
The dot character has special meaning for regular expressions in Perl, and the 1st argument to split is a regular expression. You need to escape the dot:
use warnings;
use strict;
my $name = 'dc1-ent.ent.ped.local';
print "$name\n";
my #tmpStr = split /\./, $name;
my $hostname = $tmpStr[0];
print "Host name is $hostname\n";
This outputs:
dc1-ent.ent.ped.local
Host name is dc1-ent
I would write it like this
my $name = gethostbyaddr(inet_aton('192.168.2.3'), AF_INET);
my ($host) = $name =~ /([^.]+)/;
say $host;
Your problem is not related to gethostbyaddr() but by what follows.
Proof:
DB<1> $name = 'dc1-ent.ent.ped.local';
DB<2> #tmpStr = split ".", $name;
DB<3> print #tmpStr;
(nothing printed)
Try instead using split that way:
DB<8> $name = 'dc1-ent.ent.ped.local';
DB<9> #tmpStr = split(/\./, $name);
DB<10> print #tmpStr;
dc1-ententpedlocal
DB<11> print join(' ', #tmpStr);
dc1-ent ent ped local
DB<12> x #tmpStr;
0 'dc1-ent'
1 'ent'
2 'ped'
3 'local'
Or if you absolutely want a string and not a regex, protect the dot also as your string is still parsed as a regular expression (which is why being explicit with / / has its merits, it forces you to remember that some character have special meaning there, like the dot):
DB<1> $name = 'dc1-ent.ent.ped.local';
DB<2> #tmpStr = split('.', $name);
DB<3> print #tmpStr;
DB<4> #tmpStr = split('\.', $name);
DB<5> x #tmpStr
0 'dc1-ent'
1 'ent'
2 'ped'
3 'local'

Perl: How to pass in an input without being replaced by the substitution s/// Operator?

For example,
$test = "abc";
$test =~ s/b//g;
normally test will be ac. How do I manipulate the string "abc" so that it will go through this test and still return abc.
I am trying to do a command injection so I can't change $test =~ s/b//g; but can change the input.
Hopefully this makes sense.
You want to use the /r modifier. it's a new'ish feature so older versions will not run it
$test = "abc";
my $str= $test =~ s/b//gr;
http://perldoc.perl.org/perlre.html#Modifiers
There is no string you can pass to s/b//g that will result in a string that contains a b.
However, if you have access to $test and not just it's value, you could set its pos such that the substitution operator won't find anything.
$ perl -e'
my $test = "abc";
pos($test) = length($test);
$test =~ s/b//g;
CORE::say($test);
'
ac

Perl - Searching values in a log file and store/print them as a string.

I would like to search values after a specific word (Current Value = ) in a log file, and makes a string with values.
vcs_output.log: a log file
** Fault injection **
Count = 1533
0: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rcc_data_e[6]
0: Current value = x
1: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs3_data_e[51]
1: Current value = x
2: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs1_data_e[3]
2: Current value = 1
3: Path = cmp_top.iop.sparc0.exu.alu.shft_alu_shift_out_e[18]
3: Current value = 0
4: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs3_data_e[17]
4: Current value = x
5: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs1_data_e[43]
5: Current value = 0
6: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rcc_data_e[38]
6: Current value = x
7: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs2_data_e_l[30]
7: Current value = 1
.
.
.
If I store values after "Current value = ", then x,x,1,0,x,0,x,1. I ultimately save/print them as a string such as xx10x0x1.
Here is my code
code.pl:
#!/usr/bin/perl
use strict;
use warnings;
##### Read input
open ( my $input_fh, '<', 'vcs_output.log' ) or die $!;
chomp ( my #input = <$input_fh> );
my $i=0;
my #arr;
while (#input) {
if (/Current value = /)
$arr[i]= $input; # put the matched value to array
}
}
## make a string from the array using an additional loop
close ( $input_fh );
I think there is a way to make a string in one loop (or even not using a loop). Please advise me to make it. Any suggestion is appreciated.
You can do both that you ask for.
To build a string directly, just append to it what you capture in the regex
my $string;
while (<$input_fh>)
{
my ($val) = /Current\s*value\s*=\s*(.*)/;
$string .= $val;
}
If the match fails then $val is an empty string, so we don't have to test. You can also write the whole while loop in one line
$string .= (/Current\s*value\s*=\s*(.*)/)[0] while <$input_fh>;
but I don't see why that would be necessary. Note that this reads from the filehandle, and line by line. There is no reason to first read all lines into an array.
To avoid (explicit) looping, you can read all lines and pass them through map, naively as
my $string = join '',
map { (/Current\s*value\s*=\s*(.*)/) ? $1 : () } <$input_fh>;
Since map needs a list, the filehandle is in list context, returning the list of all lines in the file. Then each is processed by code in map's block, and its output list is then joined.
The trick map { ($test) ? $val : () } uses map to also do grep's job, to filter -- the empty list that is returned if $test fails is flattened into the output list, thus disappearing. The "test" here is the regex match, which in the scalar context returns true/false, while the capture sets $1.
But, like above, we can return the first element of the list that match returns, instead of testing whether the match was successful. And since we are in map we can in fact return the "whole" list
my $string = join '',
map { /Current\s*value\s*=\s*(.*)/ } <$input_fh>;
what may be clearer here.
Comments on the code in the question
the while (#input) is an infinite loop, since #input never gets depleted. You'd need foreach (#input) -- but better just read the filehandle, while (<$input_fh>)
your regex does match on a line with that string, but it doesn't attempt to match the pattern that you need (what follows =). Once you add that, it need be captured as well, by ()
you can assign to the i-th element (which should be $i) but then you'd have to increment $i as you go. Most of the time it is better to just push #array, $value
You can use capturing parentheses to grab the string you want:
use strict;
use warnings;
my #arr;
open ( my $input_fh, '<', 'vcs_output.log' ) or die $!;
while (<$input_fh>) {
if (/Current value = (.)/) {
push #arr, $1;
}
}
close ( $input_fh );
print "#arr\n";
__END__
x x 1 0 x 0 x 1
Use grep and perlre
http://perldoc.perl.org/functions/grep.html
http://perldoc.perl.org/perlre.html
If on a non-Unix environment then...
#!/usr/bin/perl -w
use strict;
open (my $fh, '<', "vcs_output.log");
chomp (my #lines = <$fh>);
# Filter for lines which contain string 'Current value'
#lines = grep{/Current value/} #lines;
# Substitute out what we don't want... leaving us with the 'xx10x0x1'
#lines = map { $_ =~ s/.*Current value = //;$_} #lines;
my $str = join('', #lines);
print $str;
Otherwise...
#!/usr/bin/perl -w
use strict;
my $output = `grep "Current value" vcs_output.log | sed 's/.*Current value = //'`;
$output =~ s/\n//g;
print $output;

Perl: String in Substring or Substring in String

I'm working with DNA sequences in a file, and this file is formatted something like this, though with more than one sequence:
>name of sequence
EXAMPLESEQUENCEATCGATCGATCG
I need to be able to tell if a variable (which is also a sequence) matches any of the sequences in the file, and what the name of the sequence it matches, if any, is. Because of the nature of these sequences, my entire variable could be contained in a line of the file, or a line of the variable could be a part of my variable.
Right now my code looks something like this:
use warnings;
use strict;
my $filename = "/users/me/file/path/file.txt";
my $exampleentry = "ATCG";
my $returnval = "The sequence does not match any in the file";
open file, "<$filename" or die "Can't find file";
my #Name;
my #Sequence;
my $inx = 0;
while (<file>){
$Name[$inx] = <file>;
$Sequence[$inx] = <file>;
$indx++;
}unless(index($Sequence[$inx], $exampleentry) != -1 || index($exampleentry, $Sequence[$inx]) != -1){
$returnval = "The sequence matches: ". $Name[$inx];
}
print $returnval;
However, even when I purposely set $entry as a match from the file, I still return The sequence does not match any in the file. Also, when running the code, I get Use of uninitialized value in index at thiscode.pl line 14, <file> line 3002. as well as Use of uninitialized value within #Name in concatenation (.) or string at thiscode.pl line 15, <file> line 3002.
How can I perform this search?
I will assume that the purpose of this script is to determine if $exampleentry matches any record in the file file.txt. A record describes here a DNA sequence and corresponds to three consecutive lines in the file. The variable $exampleentry will match the sequence if it matches the third line of the record. A match means here that either
$exampleentry is a substring of $line, or
$line is a substring of $exampleentry,
where $line referes to the corresponding line in the file.
First, consider the input file file.txt:
>name of sequence
EXAMPLESEQUENCEATCGATCGATCG
in the program you try to read these two lines, using three calls to readline. Accordingly, that last call to readline will return undef since there are no more lines to read.
It therefore seems reasonable that the two last lines in file.txt are malformed, and the correct format should be:
>name of sequence
EXAMPLESEQUENCE
ATCGATCGATCG
If I now understand you correctly, I hope this could solve your problem:
use feature qw(say);
use strict;
use warnings;
my $filename = "file.txt";
my $exampleentry = "ATCG";
my $returnval = "The sequence does not match any in the file";
open (my $fh, '<', $filename ) or die "Can't find file: $!";
my #name;
my #sequence;
my $inx = 0;
while (<$fh>) {
chomp ($name[$inx] = <$fh>);
chomp ($sequence[$inx] = <$fh>);
if (
index($sequence[$inx], $exampleentry) != -1
|| index($exampleentry, $sequence[$inx]) != -1
) {
$returnval = "The sequence matches: ". $name[$inx];
last;
}
}
say $returnval;
Notes:
I have changed variable names to follow snake_case convention. For example the variable #Name is better written using all lower case as #name.
I changed the open() call to follow the new recommended 3-parameter style, see Don't Open Files in the old way for more information.
Used feature say instead of print
Added a chomp after each readline to avoid storing newline characters in the arrays.

How explicitly resolve variables in a perl string?

In my perl script I want to have both versions of $config directory:
my $config='$home/client/config';
and
my $config_resolved="$home/client/config";
But I want to get $config_resolved from $config, i.e. something like this:
my $config_resolved=resolve_vars($config);
How can I do such thing in perl?
From the Perl FAQ (which every Perl programmer should read at least once):
How can I expand variables in text strings?
(contributed by brian d foy)
If you can avoid it, don't, or if you can
use a templating system, such as Text::Template or Template Toolkit,
do that instead. You might even be able to get the job done with
sprintf or printf:
my $string = sprintf 'Say hello to %s and %s', $foo, $bar;
However, for the one-off simple case where I don't want to pull out a
full templating system, I'll use a string that has two Perl scalar
variables in it. In this example, I want to expand $foo and $bar to
their variable's values:
my $foo = 'Fred';
my $bar = 'Barney';
$string = 'Say hello to $foo and $bar';
One way I can do this involves the substitution operator and a double /e flag. The
first /e evaluates $1 on the replacement side and turns it into $foo. The
second /e starts with $foo and replaces it with its value. $foo,
then, turns into 'Fred', and that's finally what's left in the string:
$string =~ s/(\$\w+)/$1/eeg; # 'Say hello to Fred and Barney'
The /e will also silently ignore violations of strict, replacing undefined
variable names with the empty string. Since I'm using the /e flag
(twice even!), I have all of the same security problems I have with
eval in its string form. If there's something odd in $foo, perhaps
something like #{[ system "rm -rf /" ]}, then I could get myself in
trouble.
To get around the security problem, I could also pull the
values from a hash instead of evaluating variable names. Using a
single /e, I can check the hash to ensure the value exists, and if it
doesn't, I can replace the missing value with a marker, in this case
??? to signal that I missed something:
my $string = 'This has $foo and $bar';
my %Replacements = (
foo => 'Fred',
);
# $string =~ s/\$(\w+)/$Replacements{$1}/g;
$string =~ s/\$(\w+)/
exists $Replacements{$1} ? $Replacements{$1} : '???'
/eg;
print $string;
I use eval for this.
So, you must replace all scalars (their names) with their values.
$config = 'stringone';
$boo = '$config/any/string';
$boo =~ s/(\$\w+)/eval($1)/eg;
print $boo;
Because you are using my to declare it as private variable, you might as well use a /ee modifier. This can find variables declared to be in local scope:
$boo =~ s/(\$\w+)/$1/eeg;
This is most tidily and safely done by the double-eval modifier on s///.
In the program below, the first /e evaluates the string $1 to get $home, while the second evaluates $home to get the variable's value HOME.
use strict;
my $home = 'HOME';
my $config = '$home/client/config';
my $config_resolved = resolve_vars($config);
print $config_resolved, "\n";
sub resolve_vars {
(my $str = shift) =~ s/(\$\w+)/$1/eeg;
return $str;
}
output
HOME/client/config

Resources