Is there a different way to concatenate variables in Perl?
I accidentally wrote the following line of code:
print "$linenumber is: \n" . $linenumber;
And that resulted in output like:
22 is:
22
I was expecting:
$linenumber is:
22
So then I wondered. It must be interpreting the $linenumber in the double quotes as a reference to the variable (how cool!).
What are the caveats to using this method and how does this work?
Variable interpolation occurs when you use double quotes. So, special characters need to be escaped. In this case, you need to escape the $:
print "\$linenumber is: \n" . $linenumber;
It can be rewritten as:
print "\$linenumber is: \n$linenumber";
To avoid string interpolation, use single quotes:
print '$linenumber is: ' . "\n$linenumber"; # No need to escape `$`
I like .= operator method:
#!/usr/bin/perl
use strict;
use warnings;
my $text .= "... contents ..."; # Append contents to the end of variable $text.
$text .= $text; # Append variable $text contents to variable $text contents.
print $text; # Prints "... contents ...... contents ..."
If you change your code from
print "$linenumber is: \n" . $linenumber;
to
print '$linenumber is:' . "\n" . $linenumber;
or
print '$linenumber is:' . "\n$linenumber";
it will print
$linenumber is:
22
What I find useful when wanting to print a variable name is to use single quotes so that the variables within will not be translated into their value making the code easier to read.
In Perl any string that is built with double quotes will be interpolated, so any variable will be replaced by its value. Like many other languages if you need to print a $, you will have to escape it.
print "\$linenumber is:\n$linenumber";
OR
print "\$linenumber is:\n" . $linenumber;
OR
printf "\$linenumber is:\n%s", $linenumber;
Scalar Interpolation
When formulating this response, I found this webpage which explains the following information:
###################################################
#Note that when you have double quoted strings, you don't always need to concatenate. Observe this sample:
#!/usr/bin/perl
$a='Big ';
$b='Macs';
print 'I like to eat ' . $a . $b;
#This prints out:
# I like to eat Big Macs
###################################################
#If we had used double quotes, we could have accomplished the same thing like this:
#!/usr/bin/perl
$a='Big ';
$b='Macs';
print "I like to eat $a $b";
#Printing this:
# I like to eat Big Macs
#without having to use the concatenating operator (.).
###################################################
#Remember that single quotes do not interpret, so had you tried that method with single quotes, like this:
#!/usr/bin/perl
$a='Big ';
$b='Macs';
print 'I like to eat $a $b';
#Your result would have been:
# I like to eat $a $b
#Which don't taste anywhere near as good.
I thought this would be helpful to the community so I'm asking this and answering my own question. Other helpful answers are more than welcome!
You can backslash the $ to print it literally:
print "\$linenumber is: \n" . $linenumber;
That prints what you were expecting. You can also use single quotes if you don't want Perl to interpolate variable names, but then the "\n" will be interpolated literally.
Related
I need to edit a multi-line scalar and print the results, however I am not able to do it neatly.
my $text = "$omething\n nothing\n Everything\n";
What I need to do is check each line, and if there's a capital letter or special charracter - print this line and remove it from the original scalar ($text).
In this example it would print two times, first time:
$omething
Second time:
Everything
And remove both of those strings from the $text scalar.
To include a dollar sign in a double quoted string, you need to escape it by a backslash.
You can remove the matching lines in a while loop:
#!/usr/bin/perl
use warnings;
use strict;
my $text = "\$omething\nnothing\nEverything\n";
while ($text =~ s/(.*[[:upper:]\$].*\n)//) {
print $1;
}
print "Remaining: $text";
A period never matches a newline (unless you specify the /s modifier).
Per the questions and ruminations in:
https://unix.stackexchange.com/questions/188658/writing-a-character-n-times-using-the-printf-command
and
How can I repeat a character in bash?
I would like to learn how one might go about parameterizing the repeat value for a character/string. For example, the followings works spiffingly:
printf " ,\n%0.s" {1..5}
However, if I wanted to parameterize '5', say:
num=5
I cannot seem to get the expansion correct to make this work. For instance:
printf " ,\n%0.s" {1..$((num))}
fails.
Any thoughts/ideas would be most welcome - I reckon there's a way to do this without having to resort to perl or awk so just curious if poss.
Thanks!
You can use seq
num=20;
printf '\n%.0s' $(seq $num)
If you can build the command as a string -- with all the parameter expansion you want -- then you can evaluate it. This prints X num times:
num=10
eval $(echo printf '"X%0.s"' {1..$num})
A slighly different approach
$ repeat() {
local str=$1 n=$2 spaces
printf -v spaces "%*s" $n " " # create a string of spaces $n chars long
printf "%s" "${spaces// /$str}" # substitute each space with the requested string
}
$ repeat '!' 10
!!!!!!!!!! # <= no newline
$ repeat $' ,\n' 5
,
,
,
,
,
In perl suppose I have a string like 'hello\tworld\n', and what I want is:
'hello world
'
That is, "hello", then a literal tab character, then "world", then a literal newline. Or equivalently, "hello\tworld\n" (note the double quotes).
In other words, is there a function for taking a string with escape sequences and returning an equivalent string with all the escape sequences interpolated? I don't want to interpolate variables or anything else, just escape sequences like \x, where x is a letter.
Sounds like a problem that someone else would have solved already. I've never used the module, but it looks useful:
use String::Escape qw(unbackslash);
my $s = unbackslash('hello\tworld\n');
You can do it with 'eval':
my $string = 'hello\tworld\n';
my $decoded_string = eval "\"$string\"";
Note that there are security issues tied to that approach if you don't have 100% control of the input string.
Edit: If you want to ONLY interpolate \x substitutions (and not the general case of 'anything Perl would interpolate in a quoted string') you could do this:
my $string = 'hello\tworld\n';
$string =~ s#([^\\A-Za-z_0-9])#\\$1#gs;
my $decoded_string = eval "\"$string\"";
That does almost the same thing as quotemeta - but exempts '\' characters from being escaped.
Edit2: This still isn't 100% safe because if the last character is a '\' - it will 'leak' past the end of the string though...
Personally, if I wanted to be 100% safe I would make a hash with the subs I specifically wanted and use a regex substitution instead of an eval:
my %sub_strings = (
'\n' => "\n",
'\t' => "\t",
'\r' => "\r",
);
$string =~ s/(\\n|\\t|\\n)/$sub_strings{$1}/gs;
I have a variable from which I have to grep the which in middle of %% adn the word which starts with $$. I used split it works... but for only some scenarios.
Example:
#!/usr/bin/perl
my $lastline ="%Filters_LN_RESS_DIR%\ARC\Options\Pega\CHF_Vega\$$(1212_GV_DATE_LDN)";
my #lastline_temp = split(/%/,$lastline);
print #lastline_temp;
my #var=split("\\$\\$",$lastline_temp[2]);
print #var;
I get the o/p as expected. But can i get the same using Grep command. I mean I dont want to use the array[2] or array[1]. So that I can replace the values easily.
I don't really see how you can get the output you expect. Because you put your data in "busy" quotes (interpolating, double, ...), it comes out being stored as:
'%Filters_LN_RESS_DIR%ARCOptionsPegaCHF_Vega$01212_GV_DATE_LDN)'
See Quote and Quote-like Operators and perhaps read Interpolation in Perl
Notice that the backslashes are gone. A backslash in interpolating quotes simply means "treat the next character as literal", so you get literal 'A', literal 'O', literal 'P', ....
That '0' is the value of $( (aka $REAL_GROUP_ID) which you unwittingly asked it to interpolate. So there is no sequence '$$' to split on.
Can you get the same using a grep command? It depends on what "the same" is. You save the results in arrays, the purpose of grep is to exclude things from the arrays. You will neither have the arrays, nor the output of the arrays if you use a non-trivial grep: grep {; 1 } #data.
Actually you can get the exact same result with this regular expression, assuming that the single string in #vars is the "result".
m/%([^%]*)$/
Of course, that's no more than
substr( $lastline, rindex( $lastline, '%' ) + 1 );
which can run 8-10 times faster.
First, be very careful in your use of quotes, I'm not sure if you don't mean
'%Filters_LN_RESS_DIR%\ARC\Options\Pega\CHF_Vega\$$(1212_GV_DATE_LDN)'
instead of
"%Filters_LN_RESS_DIR%\ARC\Options\Pega\CHF_Vega\$$(1212_GV_DATE_LDN)"
which might be a different string. For example, if evaluated, "$$" means the variable $PROCESS_ID.
After trying to solve riddles (not sure about that), and quoting your string
my $lastline =
'%Filters_LN_RESS_DIR%\ARC\Options\Pega\CHF_Vega\$$(1212_GV_DATE_LDN)'
differently, I'd use:
my ($w1, $w2) = $lastline =~ m{ % # the % char at the start
([^%]+) # CAPTURE everything until next %
[^(]+ # scan to the first brace
\( # hit the brace
([^)]+) # CAPTURE everything up to closing brace
}x;
print "$w1\n$w2";
to extract your words. Result:
Filters_LN_RESS_DIR
1212_GV_DATE_LDN
But what do you mean by replace the values easily. Which values?
Addendum
Now lets extract the "words" delimited by '\'. Using a simple split:
my #words = split /\\/, # use substr to start split after the first '\\'
substr $lastline, index($lastline,'\\');
you'll get the words between the backslashes if you drop the last entry (which is the $$(..) string):
pop #words; # remove the last element '$$(..)'
print join "\n", #words; # print the other elements
Result:
ARC
Options
Pega
CHF_Vega
Does this work better with grep? Seems to:
my #words = grep /^[^\$%]+$/, split /\\/, $lastline;
and
print join "\n", #words;
also results in:
ARC
Options
Pega
CHF_Vega
Maybe that is what you are after? What do you want to do with these?
Regards
rbo
Is there an easy way, using a subroutine maybe, to print a string in Perl without escaping every special character?
This is what I want to do:
print DELIMITER <I don't care what is here> DELIMITER
So obviously it will great if I can put a string as a delimiter instead of special characters.
perldoc perlop, under "Quote and Quote-like Operators", contains everything you need.
While we usually think of quotes as literal values, in Perl they function as operators, providing various kinds of interpolating and pattern matching
capabilities. Perl provides customary quote characters for these behaviors, but also provides a way for you to choose your quote character for any of
them. In the following table, a "{}" represents any pair of delimiters you choose.
Customary Generic Meaning Interpolates
'' q{} Literal no
"" qq{} Literal yes
`` qx{} Command yes*
qw{} Word list no
// m{} Pattern match yes*
qr{} Pattern yes*
s{}{} Substitution yes*
tr{}{} Transliteration no (but see below)
<<EOF here-doc yes*
* unless the delimiter is ''.
$str = q(this is a "string");
print $str;
if you mean quotes and apostrophes with 'special characters'
You can use the __DATA__ directive which will treat all of the following lines as a file that can be accessed from the DATA handle:
while (<DATA>) {
print # or do something else with the lines
}
__DATA__
#!/usr/bin/perl -w
use Some::Module;
....
or you can use a heredoc:
my $string = <<'END'; #single quotes prevent any interpolation
#!/usr/bin/perl -b
use Some::Module;
....
END
The printing is not doing special things to the escapes, double quoted strings are doing it. You may want to try single quoted strings:
print 'this is \n', "\n";
In a single quoted string the only characters that must be escaped are single quotes and a backslash that occurs immediately before the end of the string (i.e. 'foo\\').
It is important to note that interpolation does not work with single quoted strings, so
print 'foo is $foo', "\n";
Will not print the contents of $foo.
You can pretty much use any character you want with q or qq. For example:
#!/usr/bin/perl
use utf8;
use strict; use warnings;
print q∞This is a test∞;
print qq☼\nThis is another test\n☼;
print q»But, what is the point?»;
print qq\nYou are just making life hard on yourself!\n;
print qq¿That last one is tricky\n¿;
You cannot use qq DELIMITER foo DELIMITER. However, you could use heredocs for a similar effect:
print <<DELIMITER
...
DELIMETER
;
or
print <<'DELIMETER'
...
DELIMETER
;
but your source code would be really ugly.
If you want to print a string literally and you have Perl 5.10 or later then
say 'This is a string with "quotes"' ;
will print the string with a newline.. The importaning thing is to use single quotes ' ' rather than double ones " "