perl: upper case evaluation for subroutine - perl

I need to pass system variable in upper case to perl subroutine.
For example, if the variable with name VARNAME (value 'super'), i need to pass "SUPER_MAN".
In general, if we use 'uc' option like in the example below, we can convert to upper case
perl -e 'print uc"$ENV{VARNAME}\n"'
But when we try to pass it in subroutine, we need to include uc function in the syntax and evaluate during runtime. To emulate that I was trying the below but not working, Where am I going wrong?
perl -e 'print ".uc($ENV{VARNAME})_MAN\n"'
.uc(super)_MAN
Alternate methods/approach is also welcome.

Take the uc out of the quotes "", since perl thinks you want the literal letters uc:
FOO=abc perl -e 'print "." . uc($ENV{FOO}) . "_MAN\n"'
.ABC_MAN
perldoc perlop - Quote and Quote like Operators

Related

Difference between eof with and without parentheses? (Perl5)

I'm trying to make a perl one-liner mimic awk's file-relative line number counter "FNR". In itself this is not a problem. In one attempt, I used the following command:
perl -lnE 'say "$. : $_"; $.=0 if eof' testfiles
This works: the (automatically incremented) cross-file line counter $. is reset upon reaching the last line in each of the testfiles. When I add parentheses to the eof function call, like so:
perl -lnE 'say "$. : $_"; $.=0 if eof()' testfiles
the program doesn't work anymore as intended, since eof() only returns true when processing the last line of the last of the testfiles. This is properly documented here.
My question is: How does eof know if it is called with or without (), and thus what to do? Is the function somehow special-cased, or can any function find out if it is called with or without parentheses?
It's an operator whose semantics can't be replicated by subs.
Just like + and and, functions in perlfunc are operators.
The functions in this section can serve as terms in an expression. They fall into two major categories: list operators and named unary operators.
As operators, they can provide any syntax they please.
Prototypes can be used to replicate the syntax of some of the functions. You can determine which using prototype("CORE::funcname").
$ perl -M5.010 -e'say prototype("CORE::length") // "[undef]"'
_
$ perl -M5.010 -e'say prototype("CORE::sysread") // "[undef]"'
*\$$;$
$ perl -M5.010 -e'say prototype("CORE::say") // "[undef]"'
[undef]
Technically, eof's syntax can be replicated by subs. So prototype does return a value for eof.
$ perl -M5.010 -e'say prototype("CORE::eof") // "[undef]"'
;*
Its semantics can't be replicated, however. There's no way for a sub to tell if it was called with or without parens. Well, it might be possible using Devel::CallParser or the keyword plugin, but this would require C code, would require a lot work, and they have problems of their own.
As a side note, open's syntax is misreported as being able to be reproduced.
$ perl -M5.010 -e'say prototype("CORE::open") // "[undef]"'
*;$#
Built-in unary functions are special-cased in the parser. There's no perl-level way to do the same for subroutine calls.

Perl generate a file based on a template

I am working on a use case that requires me to generate .hpp files based on a template. So something like
#ifdef changethis_hpp
#define changethis_hpp
#include<fixedheader1>
...
#include<fixedheaderN>
class changethis
{
....
};
needs to be generated based on the requirement of changethis string.
How can I achieve this in perl?
WHITSF
I wrote a fixed template.txt file and and then replaced the text with changethis string and then dumped it as a changethis.hpp.
But is there any other way I can achieve this in perl?
There's a Perl FAQ, How can I expand variables in text strings?. It starts like this:
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.
I'd highly recommend you ignore most of this advice and go directly to a templating system (as recommended in the first line).
I use Text::Template for such tasks.

Why perl parses regex

Perl misses my DWIM (ternary ?:) and forces regex.
perl -e "print $bool ?'T' :'F'"
Use of ?PATTERN? without explicit operator is deprecated at -e line 1.
Search pattern not terminated or ternary operator parsed as search pattern at -e
line 1.
Why is that so?
Following two examples have correctly parsed ternary operator,
perl -e "print $bool ? 'T' :'F'"
perl -e "print [] ?'T' :'F'"
This is on windows, but almost same thing on *nix
perl -e 'print $bool ?"T" :"F"'
so it doesn't look like shell related.
The ?...? is an operator, as seen here, and the problem here is one of ambiguity. The compiler does not know if it is seeing a pattern or a ternary operator (as the subsequent warning says), so it guesses, and guesses wrong.
I think it has something to do with print() setting it up for a fall, in that print() has the syntax:
print FILEHANDLE LIST
So, it pre-determines $bool as a file handle, and ?'.... as a statement. Then it notices that $bool is not a file handle, but the ternary is already cast in its faulty role. You will notice that the following works as intended:
perl -e"print STDOUT $bool ?'t' :'f'"
Perl syntax has ambiguities.
print $bool ?...
can be parsed as
print FILEHANDLE LIST
+----------------- print
| +----------- FILEHANDLE
| | +------ LIST (A regex match ?...?)
| | |
_____ _____ ____
print $bool ?...
or as
print LIST
+----------------- print
| +--------- LIST (A conditional operator ...?...:...)
| |
_____ __________
print $bool ?...
Without looking too far ahead, perl incorrectly guesses that you meant the first one, but lets you know that it guessed.
Search pattern not terminated or ternary operator parsed as search pattern
Workarounds:
Adding a space after the ? (print $bool ? 'T' :'F';) makes it guess correctly (since it no longer looks as much like ?...?).
Adding a + before the file handle (print +$bool ?'T' :'F';) makes it guess correctly (since it's no longer a valid file handle expression).
Remove the space before the ? (print $bool?'T' :'F';) makes it guess correctly (since it's no longer a valid file handle expression).
Adding a file handle (print STDOUT $bool ?'T' :'F';) makes it guess correctly (since $bool must now be the start of the argument list).
Are you on Windows or on *nix?
On linux I get no warnings when I switch " and ' ( perl -e 'print $bool ? "T" :"F"'). The way you wrote it the $bool is interpolated by the shell, to an empty string, and your code becomes perl -e "print ?'T' :'F'" which correctly triggers the warning.

Simple search and replace without regex

I've got a file with various wildcards in it that I want to be able to substitute from a (Bash) shell script. I've got the following which works great until one of the variables contains characters that are special to regexes:
VERSION="1.0"
perl -i -pe "s/VERSION/${VERSION}/g" txtfile.txt # No problems here
APP_NAME="../../path/to/myapp"
perl -i -pe "s/APP_NAME/${APP_NAME}/g" txtfile.txt # Error!
So instead I want something that just performs a literal text replacement rather than a regex. Are there any simple one-line invocations with Perl or another tool that will do this?
The 'proper' way to do this is to escape the contents of the shell variables so that they aren't seen as special regex characters. You can do this in Perl with \Q, as in
s/APP_NAME/\Q${APP_NAME}/g
but when called from a shell script the backslash must be doubled to avoid it being lost, like so
perl -i -pe "s/APP_NAME/\\Q${APP_NAME}/g" txtfile.txt
But I suggest that it would be far easier to write the entire script in Perl
Use the following:
perl -i -pe "s|APP_NAME|\\Q${APP_NAME}|g" txtfile.txt
Since a vertical bar is not a legal character as part of a path, you are good to go.
I don't particularly like this answer because there should be a better way to do a literal replace in Perl. \Q is cryptic. Using quotemeta adds extra lines of code.
But... You can use substr to replace a portion of a string.
#!/usr/bin/perl
my $name = "Jess.*";
my $sentence = "Hi, my name is Jess.*, dude.\n";
my $new_name = "Prince//";
my $name_idx = index $sentence, $name;
if ($name_idx >= 0) {
substr($sentence, $name_idx, length($name), $new_name);
}
print $sentence;
Output:
Hi, my name is Prince//, dude.
You don't have to use a regular expression for this (using substr(), index(), and length()):
perl -pe '
foreach $var ("VERSION", "APP_NAME") {
while (($i = index($_, $var)) != -1) {
substr($_, $i, length($var)) = $ENV{$var};
}
}
'
Make sure you export your variables.
You can use a regex but escape any special characters.
Something like this may work.
APP_NAME="../../path/to/myapp"
APP_NAME=`echo "$APP_NAME" | sed -e '{s:/:\/:}'`
perl -i -pe "s/APP_NAME/${APP_NAME}/g" txtfile.txt
Use:
perl -i -pe "\$r = qq/\Q${APP_NAME}\E/; s/APP_NAME/\$r/go"
Rationale: Escape sequences
I managed to get a working solution, partly based on bits and pieces from other peoples' answers:
app_name='../../path/to/myapp'
perl -pe "\$r = q/${app_name//\//\\/}/; s/APP_NAME/\$r/g" <<<'APP_NAME'
This creates a Perl variable, $r, from the result of the shell parameter expansion:
${app_name//\//\\/}
${ # Open parameter expansion
app_name # Variable name
// # Start global substitution
\/ # Match / (backslash-escaped to avoid being interpreted as delimiter)
/ # Delimiter
\\/ # Replace with \/ (literal backslash needs to be escaped)
} # Close parameter expansion
All that work is needed to prevent forward slashes inside the variable from being treated as Perl syntax, which would otherwise close the q// quotes around the string.
In the replacement part, use the variable $r (the $ is escaped, to prevent it from being treated as a shell variable within double quotes).
Testing it out:
$ app_name='../../path/to/myapp'
$ perl -pe "\$r = q/${app_name//\//\\/}/; s/APP_NAME/\$r/g" <<<'APP_NAME'
../../path/to/myapp

How can I interpolate literal \t and \n in Perl strings? [duplicate]

This question already has answers here:
How can I manually interpolate string escapes in a Perl string?
(2 answers)
Closed 8 years ago.
Say I have an environment variable myvar:
myvar=\tapple\n
When the following command will print out this variable
perl -e 'print "$ENV{myvar}"'
I will literally have \tapple\n, however, I want those control chars to be evaluated and not escaped. How would I achieve it?
In the real world $ENV residing in substitution, but I hope the answer will cover that.
Use eval:
perl -e 'print eval qq{"$ENV{myvar}"}'
UPD: You can also use substitution with the ee switch, which is safer:
perl -e '(my $s = $ENV{myvar}) =~ s/(\\n|\\t)/"qq{$1}"/gee; print $s'
You should probably be using String::Escape.
use String::Escape qw(unbackslash);
my $var = unbackslash($ENV{'myvar'});
unbackslash unescapes any string escape sequences it finds, turning them into the characters they represent. If you want to explicitly only translate \n and \t, you'll probably have to do it yourself with a substitution as in this answer.
There's nothing particularly special about a sequence of characters that includes a \. If you want to substitute one sequence of characters for another, it's very simple to do in Perl:
my %sequences = (
'\\t' => "\t",
'\\n' => "\n",
'foo' => 'bar',
);
my $string = '\\tstring fool string\\tfoo\\n';
print "Before: [$string]\n";
$string =~ s/\Q$_/$sequences{$_}/g for ( keys %sequences );
print "After: [$string]\n";
The only trick with \ is to keep track of the times when Perl thinks it's an escape character.
Before: [\tstring fool string\tfoo\n]
After: [ string barl string bar
]
However, as darch notes, you might just be able to use String::Escape.
Note that you have to be extremely careful when you're taking values from environment variables. I'd be reluctant to use String::Escape since it might process quite a bit more than you are willing to translate. The safe way is to only expand the particular values you explicitly want to allow. See my "Secure Programming Techniques" chapter in Mastering Perl where I talk about this, along with the taint checking you might want to use in this case.