views:

778

answers:

4

How do I have a powershell script embedded within the same file as a DOS batch script?

I know this kind of thing is possible in other scenarios:

  • Embedding SQL in a DOS batch script using sqlcmd and a clever arrangements of goto's and comments at the beginning of the file
  • In a *nix environment having a the name of the program you wish to run the script with on the first line of the script commented out e.g. #!/usr/local/bin/python

There may not be a way to do this - in which case I will have to call the separate powershell script from the launching DOS script.

One possible solution I've considered is to echo out the powershell script, and then run it. A good reason to not do this is that part of the reason to attempt this is to be using the advantages of the powershell environment without the pain of, for example, DOS escape characters

I have some unusual constraints and would like to find an elegant solution. I suspect this question may be baiting responses of the variety: "why don't you try and solve this different problem instead." Suffice to say these are my constraints, sorry about that.

Any ideas? Is there a suitable combination of clever comments and escape characters that will enable me to achieve this?

Some thoughts on how to achieve this:

  • A carat ^ at the end of a line in DOS is a continuation - like an underscore in VB
  • An ampersand & in DOS typically is used to separate commands echo Hello & echo World results in 2 echos on separate lines
  • %0 will give you the script that's currently running

So something like this (if I could make it work) would be good:

# & call powershell -psconsolefile %0
# & goto :EOF
/* From here on in we're running nice juicy powershell code */
Write-Output "Hello World"

Except...

  • It doesn't work... because
  • the extension of the file isn't as per powershell's liking: Windows PowerShell console file "insideout.bat" extension is not psc1. Windows PowerShell console file extension must be psc1.
  • DOS isn't really altogether happy with the situation either - although it does stumble on '#' is not recognized as an internal or external command, operable program or batch file.
A: 

Without fully understanding your question, my suggestion would be something like:

@echo off
set MYSCRIPT="some cool powershell code"
powershell -c %MYSCRIPT%

or better yet

@echo off
set MYSCRIPTPATH=c:\work\bin\powershellscript.ps1
powershell %MYSCRIPTPATH%
John Weldon
Many thanks - it's not quite what I'm after though: Like the first part of your answer I want to incorporate the powershell script directly into the batch file, but by passing the script itself to powershell to run. I'll perhaps update my question with an example of how this might look-ish
Don Vince
+2  A: 

This seems to work, if you don't mind one error in powershell at the beginning:

dosps.cmd:

@powershell -<%~f0&goto:eof
Write-Output "Hello World" 
Write-Output "Hello World again" 
Carlos Gutiérrez
Nice work - got past my issue of having it actually run the powershell script! I'm still in the business for more answers though if we can get rid of the powershell errors
Don Vince
+4  A: 
Carlos Gutiérrez
Don Vince
I made two changes. 1) Use `@@` as the prefix to look for, so it doesn't get confused by a here-string. 2) use spaces to separate elements of the command line, for readability
Jay Bazuzi
This answer doesn't support arguments at all. I bet you'll want that feature soon.
Jay Bazuzi
+2  A: 

It sounds like you're looking for what is sometimes called a "polyglot script". For CMD -> PowerShell,

@@:: This prolog allows a PowerShell script to be embedded in a .CMD file.
@@:: Any non-PowerShell content must be preceeded by "@@"
@@setlocal
@@set POWERSHELL_BAT_ARGS=%*
@@if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
@@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join(';',$((Get-Content '%~f0') -notmatch '^^@@'))) & goto :EOF

If you don't need to support quoted arguments, you can even make it a one-liner:

@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %*);'+[String]::Join(';',(Get-Content '%~f0') -notmatch '^^@PowerShell.*EOF$')) & goto :EOF

Taken from http://blogs.msdn.com/jaybaz_ms/archive/2007/04/26/powershell-polyglot.aspx. That was PowerShell v1; it may be simpler in v2, but I haven't looked.

Jay Bazuzi
Great answer - thank you for teaching me the term "polyglot script"
Don Vince