How do I protect quotes in a batch file? - perl

I want to wrap a Perl one-liner in a batch file. For a (trivial) example, in a Unix shell, I could quote up a command like this:
perl -e 'print localtime() . "\n"'
But DOS chokes on that with this helpful error message:
Can't find string terminator "'" anywhere before EOF at -e line 1.
What's the best way to do this within a .bat file?

For Perl stuff on Windows, I try to use the generalized quoting as much as possible so I don't get leaning toothpick syndrome. I save the quotes for the stuff that DOS needs:
perl -e "print scalar localtime() . qq(\n)"
If you just need a newline at the end of the print, you can let the -l switch do that for you:
perl -le "print scalar localtime()"
For other cool things you can do with switches, see the perlrun documentation.

In Windows' "DOS prompt" (cmd.exe) you need to use double quotes not single quotes. For inside the quoted Perl code, Perl gives you a lot of options. Three are:
perl -e "print localtime() . qq(\n)"
perl -e "print localtime() . $/"
perl -le "print ''.localtime()"
If you have Perl 5.10 or newer:
perl -E "say scalar localtime()"
Thanks to J.F. Sebastian's comment.

For general batch files under Windows NT+, the ^ character escapes lots of things (<>|&), but for quotes, doubling them works wonders:
C:\>perl -e "print localtime() . ""\n"""
Thu Oct 2 09:17:32 2008

First, any answer you get to this is command-specific, because the DOS shell doesn't parse the command-line like a uniq one does; it passes the entire unparsed string to the command, which does any splitting. That said, if using /subsystem:console the C runtime provides splitting before calling main(), and most commands use this.
If an application is using this splitting, the way you type a literal double-quote is by doubling it. So you'd do
perl -e "print localtime() . ""\n"""

In DOS, you use the "" around your Perl command. The DOS shell doesn't do single quotes like the normal Unix shell:
perl -e "print localtime();"

Related

Pass command line parameters to perl via file?

Could command lines parameters been saved to a file and then pass the file to perl to parse out the options? Like response file (prefix the name with #) for some Microsoft tools.
I am trying to pass expression to perl via command line, like perl -e 'print "\n"', and Windows command prompt makes using double quotes a little hard.
There are several solutions, from most to least preferable.
Write your program to a file
If your one liner is too big or complicated, write it to a file and run it. This avoids messing with shell escapes. You can reuse it and debug it and work in a real editor.
perl path\to\some_program
Command line options to perl can be put on the otherwise useless on Windows #! line. Here's an example.
#!/usr/bin/perl -i.bak -p
# -i.bak Backs up the file.
# -p Puts each line into $_ and writes out the new value of $_.
# So this changes all instances in a file of " with '.
s{"}{'}g;
Use alternative quote delimiters
Perl has a slew of alternative ways to write quotes. Use them instead. This is good for both one liners as well as things like q[<tag key='value'>].
perl -e "print qq[\n]"
Escape the quote
^ is the cmd.exe escape character. So ^" is treated as a literal quote.
perl -e "print ^"\n^""
Pretty yucky. I'd prefer using qq[] and reserve ^" for when you need to print a literal quote.
perl -e "print qq[^"\n]"
Use the ASCII code
The ASCII and UTF-8 hex code for " is 22. You can supply this to Perl with qq[\x22].
perl -e "print qq[\x22\n]"
You can read the file into a string and then use
use Getopt::Long qw(GetOptionsFromString);
$ret = GetOptionsFromString($string, ...);
to parse the options from that.

Perl: Replace text parameter by current timestamp

I have an utility to generate code documentation every night. I would like to add a timestamp in order to be aware how old the generated documentation is. I would like to use perl.
I've seen that with the following sentence I can change a joker (%1) by any value I want
perl -pi.bak -e 's/%1/date/g' footer.html
And with this other one I can get the system timestamp:
perl -MPOSIX -we "print POSIX::strftime('%d/%m/%Y %H:%M:%S', localtime)"
My question is whether there is any way to merge both instructions in just one sentence.
Thank you very much
Try doing this :
perl -MPOSIX -pi.bak -e 'BEGIN{$date = strftime("%d/%m/%Y %H:%M:%S", localtime);} s/%1/$date/g' file.html
sh command:
perl -i.bak -MPOSIX -pe's/%1/strftime("%d/%m/%Y %H:%M:%S", localtime)/eg'
cmd command:
perl -i.bak -MPOSIX -pe"s/%1/strftime('%d/%m/%Y %H:%M:%S', localtime)/eg"
/e cause the replacement expression to be treated as Perl code to execute, the result of which is the replacement text.

Perl one-liner to remove trailing space from a file

I am calling a perl one-liner inside a Perl script.
The intention of the one-liner is to remove the trailing space from a file.
Inside the main perl script:
`perl -pi -e 's/\s+$//' tape.txt`;
Now it is throwing me an error Substitution replacement not terminated at -e line 2.
Where is it going wrong?
It's because of the $/ (special variable) inside your main perl script. Note that variables are interpolated inside `` strings just like inside "" strings, and the fact that there are some single quotes in there doesn't change that. You need to escape that $:
`perl -pi -e 's/\s+\$//' tape.txt;`
The backtick syntax invokes a shell and when invoked, the shell assumes it should interpolate the string passed.
A cleaner syntax might be:
system('perl -pli -e "s/\s*$//" tape.txt');
Since you aren't capturing the output of the command, using backticks or qx in lieu of system isn't an issue.
Too, adding the -l switch autochomps each line read and then adds a newline back --- probably what you want.
\s matches [ \t\n\r\f] and do not want to match \n.
Notice use of {} for subst delimiters:
$ echo -e 'hi \nbye'| perl -pe 's{[\t\040]+$}{};' | cat -A
hi$
bye$

Using perl in windows oddities

I've got windows bat file, (using activeperl)
#echo off
perl -p -e 's/DIV\[/div\[/g' orginal.txt > output.txt
perl -p -e 'rename("output.txt", "orginal.txt")';
...
Running a .bat file, and I just cant get it to run properly..
Can't open ;: No such file or directory, <> line 12248.
Can't find string terminator "'" anywhere before EOF at -e line 1.
Not sure what I'm doing wrong..
You can't use single-quotes to enclose the Perl code in Windows. As a result, you need to escape the double-quotes or find other alternatives, such as qq(...).
perl -pe "s/DIV\[/div\[/g" original.txt > output.txt
perl -pe "rename(qq(output.txt), qq/original.txt/)"
Note that in this case, the arguments to rename can simply be rename('a.txt', 'b.txt') since they are literals and no variable interpolation is required.
You ought to use double quotes to quote the program text under Windows cmd. In your example, you can just swiztch double and single quotes. In cases where you really need double quotes in the perl text, use qq{ .... } instead.
The other posters are correct: windows requires double quotes for -e scripts to perl, which often screws things up. There is one more thing you can do, though: Use the -i switch, like this:
#echo off
perl -pi.bak -we "s/DIV\[/div\[/g" original.txt
The -i.bak switch will edit the file in place - no rename required - AND it will store a backup of the file in "original.txt.bak". If you do not want a backup, remove the ".bak" part and just use -pi.

How can I combine two conditions in Perl?

I have two Perl one liners:
perl -pe "s/\b$a\b/$b/g if m/param1 /" test
and
perl -pe "s/\b$a\b/$b/g unless /^#/" test
How can I combine theif m/somthing/ and the unless /something/, like:
[root#localhost tmp]# perl -pe "s/\b$a\b/$b/g if m/param1/ unless /^#/" test
syntax error at -e line 1, near "m/param1/ unless"
Maybe
perl -pe "s/\b$a\b/$b/g if m/param1/ && ! /^#/" test
does it.
unless is the same as if not. Judging by the way you've written the statement, I'm guessing you mean the following:
perl -pe "s/\b$a\b/$b/g if m/param1/ and not /^#/" test
(Although, you might have meant or instead of and?)
On an unrelated note, you may want to add the \Q and \E escape sequences around $a in your regex:
perl -pe "s/\b\Q$a\E\b/$b/g if m/param1 /" test
They escape any characters that are special to regexes. If you intend for $a to hold a regex you should probably move the word boundary assertions (\b) into it.
No matter what you choose to do, you will need to be careful with values in $a and $b. For instance:
a="/"
b="slash"
perl -pe "s/\b\Q$a\E\b/$b/g if m/param1 /" test
will cause a syntax error. One solution to this is to not use environment variables to replace code. Perl allows you access to the environment through the %ENV hash:
perl -pe 's/\b\Q$ENV{a}\E\b/$ENV{b}/g if m/param1 /' test
Notice the use of single ticks to avoid treating $ENV as a shell variable.