10

While attempting to rewrite a batch file, I was looking into the possibility of using powershell. The main issue that I had with it was that .ps1 files aren't executable by default. I found a solution here, which I'll copy below, but I'm very confused about the syntax on the first line:

<# : batch script
@echo off
setlocal
cd %~dp0
powershell -executionpolicy remotesigned -Command "Invoke-Expression $([System.IO.File]::ReadAllText('%~f0'))"
endlocal
goto:eof
#>
# here write your powershell commands...

Here's what I know from testing:

  • The space between <# and : is required, though there can be multiple.
  • It can also have a number 0-9 beforehand, such as 3<# :, which makes me suspect that there's some weirdness involving redirects and labels.
  • The text after the colon is ignored and not necessary
  • If I add a command before it, such as git <# : batch script, the shell shows that it tried to evaluate git : batch script 0<# and can't find the file specified
  • Changing the # to a valid filename makes the complaint go away (naturally)
  • Attempting to run the line in an interactive session actually causes it to complain about not being able to find the file specified, but running it from a script (even if it's the only line in the .cmd file) silently succeeds

That last bullet is the part I'm trying to figure out. What's different about the environments? My understanding was that part of the issue was that running a bat file is parsed as if each line were manually entered. Why doesn't it complain about # being an invalid file when called via a script?

Community
  • 1
  • 1
Matt Soucy
  • 103
  • 7

2 Answers2

9

"...running a bat file is parsed as if each line were manually entered."

No, parser rules are sometimes different between command line and batch file and in this case the difference is critical.

It complains about # being an invalid file in command line, and not in batch file, because the <# : construct is (to the batch parser) just a redirected label. The same kind of label you use in goto :label or call :label.

That means that the redirection, that is not executed in batch file as the "command" associated to the redirection is a label, is executed in command line as you can not have a label in a command line.

MC ND
  • 69,615
  • 8
  • 84
  • 126
  • Is there any documentation showing this? I'm still not entirely sure why the difference exists, since it appears that I can create labels in the interactive session? – Matt Soucy May 11 '17 at 00:01
  • 1
    @MattSoucy, If you use something like `:label` from command line, you will not have a label (you can not `goto` or `call` to it), but a *null command* (I have to give it a name) as far as the first non blank/non delimiter character is a colon. You can use `:label – MC ND May 11 '17 at 15:40
2

The trick is that you need a valid construct for both, powershell and also batch.
For powershell only th <# is important to open a multiline comment.

For batch you need something that will be valid code but is invisible inspite of the echo on state.
Therefore a colon is perfect in this situation, as label lines will not be shown, even with echo on.

You can see the result of the parser, when you replace the colon with REM.

<# REM Batch script

Output:

c:\Temp>REM batch script 0<#

The # is a valid filename, but the file itself is never accesed.

jeb
  • 78,592
  • 17
  • 171
  • 225