Running perl one-liners and scripts from powershell

From Svendsen Tech PowerShell Wiki
Jump to: navigation, search




Perl from PowerShell

Also see the main Perl on Windows article.

If you want to start a Perl script/program from PowerShell, you will need to manually specify the interpreter by prepending "perl " in front of the script (like "perl .\myscript.pl"). Of course, you need a full path to perl.exe unless it's in a directory included in the %PATH% environment variable (accessible as $env:PATH in PowerShell).

If you just type ".\perlscript.pl", it will pop open a cmd.exe window that often immediately closes, depending on the nature of the script you're running. Some are made interactive or have GUIs - and should work as expected, although they will close abruptly once they're done, and leave you unable to inspect output that might have been there. You want "perl .\myscript.pl".

In cmd.exe, you can just use the script name followed by its arguments without the need to prepend "perl " - given that .pl files are correctly associated with a Perl interpreter (c:\perl\bin\perl.exe or similar).

Perl One-liner Quoting in cmd.exe

With cmd.exe, you run one-liners using double quotes to surround the -e part. Single quotes will not work, as shown here (although it is a bit quirky):

E:\>echo abc123def | perl -nwe "print /(\d+)/"
123
E:\>echo abc123def | perl -nwe 'print /(\d+)/'
Can't find string terminator "'" anywhere before EOF at -e line 1.

E:\>

To print or use literal double quotes inside the Perl one-liner, you will need to escape them:

C:\>perl -le "print 'something \"with\" quotes';"
something "with" quotes

This is also quirky, because I accidentally left out the last quote, and it didn't complain. cmd.exe seems to have a vague concept of quoting and strings. As seen here:

C:\>perl -le "print 'something \"with\" quotes';
something "with" quotes

Your safest bet will be to quote correctly, of course. You can also leave out the semicolon if you only have a single statement.

Perl One-liner Quoting in PowerShell

With PowerShell, double-quoted strings are interpolated by PowerShell, so you will run into issues when using variable names that will be interpolated by PowerShell before being passed to Perl. I find that when doing Perl one-liners from PowerShell, single quotes are usually what you want, combined with Perl's q and qq operators. Read more about them in perldoc perlop. "q($text)" will print "$text" literally, so it's like a single-quoted string, while "qq($text)" will print the content of the variable $text, making it behave like a double-quoted string.

If there are no conflicting meta characters or "PowerShell syntax" in the Perl one-liner, both will work seemingly equivalently, as demonstrated here:

PS C:\> 'abc123def' | perl -nwle 'print /(\d+)/'
123
PS C:\> 'abc123def' | perl -nwle "print /(\d+)/"
123

Notice how I added the "-l" option, which basically says "append a newline to everything you print()". In cmd.exe you don't really need it because the shell is designed to always present you with a prompt starting on a new line, so you get the last newline for free. On Linux, and in PowerShell, you need to be aware of this issue and add newlines unless you want output like this:

PS C:\> 'abc123def' | perl -nwe 'print /(\d+)/'
123PS C:\>

Without the "-l" option, you would have to do something like this:

PS C:\> 'abc123def' | perl -nwe 'print qq($1\n) if /(\d+)/'
123

Notice how I use qq() rather than double quotes. This is because double quotes apparently can confuse the PowerShell interpreter (sometimes it seems to work). Escaped single quotes don't seem to work too well either, so use q() for that. To sum it up: For PowerShell-called Perl one-liners, use single quotes and Perl's handy qq() and q() operators for "stringification" between the single quotes.

Although escaping the single quotes for PowerShell's sake, by doubling them up, can also work:

PS C:\> "foo","bar",'baz' | perl -nwle 'print qq(''$_'');'
'foo'
'bar'
'baz'

To use double quotes literally in Perl, you need to escape them (in most cases, and always if they're unbalanced, is my current theory) with a backslash, not a backtick. This is a bit confusing:

PS C:\> '"Hello, ' | perl -pwe 's/(\"Hello, )/${1}world\"/'
"Hello, world"

With Perl 5.10 came say(), which is just like print(), but automatically appends a newline (type "perldoc -f say" at the prompt to read more about it), but you might need to enable it like this, so it's about as clunky as print/qq - and the parameter "-l" is probably still better. Using say(), it looks like this:

PS E:\> 'abc123def' | perl -Mfeature=say -nwe 'say /(\d+)/'
123

Processing PowerShell Pipeline Output with Perl

Below is another example where I process PowerShell-produced pipeline output/input with Perl, which I saw a question about somewhere. Again, a single-quoted string and the q/qq operators are the solution:

PS C:\> 1..3 | perl -nle 'print qq(#$_#)'
#1#
#2#
#3#

If you were to try with double quotes, it would fail, but I do not fully understand what's going on, since you get just the pipeline output without the surrounding parts. I think somehow the string is being parsed by PowerShell before Perl does its thing with it. If you escape the double quotes with backslashes, it will also work:

PS C:\> 1..3 | perl -nle 'print "#$_#";'
1
2
3
PS C:\> 1..3 | perl -nle 'print \"#$_#\";'
#1#
#2#
#3#

If you try using double quotes around the expression, and then escaping the inner double quotes with the PowerShell escape character, you will see the broken behaviour again:

PS C:\> 1..3 | perl -nle "print `"#$_#`";"
1
2
3

If you try to escape the inner double quotes with backslashes like demonstrated below, you will see a different type of unwanted behaviour:

PS C:\> 1..3 | perl -nle "print \"#$_#\";"
Can't find string terminator '"' anywhere before EOF at -e line 1.

It also seems like "#" isn't a random character, because:

PS C:\> 1..3 | perl -nle "print `"foo: $_`";"
syntax error at -e line 1, near "foo:"
Execution of -e aborted due to compilation errors.

PS C:\> 1..3 | perl -nle "print `"foo: `$_`";"
syntax error at -e line 1, near "foo:"
Execution of -e aborted due to compilation errors.

I haven't been able to make it work with double quotes both places and getting the escaping right, but if you use qq and escape "$_" for PowerShell's sake, you can work around it, but I stand by the "best practices" I outline at the top of this Perl one-liner quoting in PowerShell section.

PS C:\> 1..3 | perl -nle "print qq(foo: `$_)"
foo: 1
foo: 2
foo: 3

Executing Perl Code Stored in a PowerShell Variable

On a more obscure note, here's a demonstration of Perl code stored in a PowerShell variable, executed by the Perl interpreter:

PS C:\> $PerlCode = 'print join q(, ), q(a)..q(z)'
PS C:\> perl -wle $PerlCode
a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
PS C:\>