PowerShell script in a .bat file

How do you put PowerShell code into a batch/cmd file without having to also have a separate .ps1 file? (If you can have an external .ps1 file – you just invoke powershell.exe and supply the path to the script file as parameter.)

I got this question recently from one of our field guys and thought I would blog about the trick publicly.

The problem is that PowerShell syntax can obviously have elements that .bat files cannot stand, and that you cannot pass multiline script as powershell.exe parameter.

There are actually a couple of ways to do so:

1. Encode the script:

As Bruce points here PowerShell has the -EncodedCommand parameter, which lets you pass any PowerShell code as base-64-encoded string.

So if you have some sort of script, like this:

#iterate numbers 1 through 10
1..10 | foreach-object {
# just output them
"Current output:"
$_
}

You simply (in PowerShell command-line or script) put it in curcly brackets and assign (as scriptblock) to a variable:

$code = {
    #iterate numbers 1 through 10
    1..10 | foreach-object {
    # just output them
    "Current output:"
    $_
    }
}

Then use this PowerShell command to get the encoded version:

[convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($code))

Then copy/paste the output of the command to your batch file:

powershell.exe -EncodedCommand DQAKAAkAIwBpAHQAZQByAGEAdABlACAAbgB1AG0AYgBlAHIAcwAgADEAIAB0AGgAcgBvAHUAZwBoACAAMQAwAA0ACgAJADEALgAuADEAMAAgAHwAIABmAG8AcgBlAGEAYwBoAC0AbwBiAGoAZQBjAHQAIAB7AA0ACgAJACMAIABqAHUAcwB0ACAAbwB1AHQAcAB1AHQAIAB0AG
gAZQBtAA0ACgAJACIAQwB1AHIAcgBlAG4AdAAgAG8AdQB0AHAAdQB0ADoAIgANAAoACQAkAF8ADQAKAAkAfQANAAoA

2. Keep the code as PowerShell but turn it to a string:

If the first approach for whatever reason does not work for you (e.g. you care about readability), you can try to flatten the script and pass it as a string:

  1. Take the PowerShell script.
  2. Remove all the comments ( everything that starts with #).
  3. Put ; at the end of each line.
  4. Remove all line breaks.
  5. Supply the string you get as the -command parameter for powershell.exe.

The reason for all of this is that powershell.exe (the executable which allows you to run any PowerShell code allows you to either start an external .ps1 script file (which often creates additional complexity of having to maintain and ship 2 files) or execute a single line of PowerShell code as the -command parameter. Hence the requirement to flatten the script and turn something like this:

#iterate numbers 1 through 10
1..10 | foreach-object {
# just output them
"Current output:"
$_
}

into:

powershell.exe -command '1..10 | foreach-object { "Current output:"; $_; }'

See also this blog post by MoW on making PowerShell pass its exit code to command files.

Tags: , , ,

29 Responses to “PowerShell script in a .bat file”


  1. 1 Stuart June 27, 2008 at 11:59 am

    As far as I can see, the double quotes will get the command interpreter confused, so you will have to escape them – as in

    powershell.exe -command “1..10 | foreach-object { \”Current ou
    tput:\”; $_; }”

    If you don’t escape them you get the message:

    The term ‘Current’ is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
    At line:1 char:33
    + 1..10 | foreach-object { Current <<<< output:; $_; }

    You also missed off the closing double quote, but that didn’t seem to make a difference.

    Thanks for the post though, it could be useful for short scripts.

    • 2 rajeev May 26, 2010 at 9:22 pm

      I am invoking a powershell script from batch file.
      I want to return parameter from Powershell script (.PS1) to Batch file, how to do it?
      and How to assign the return parameter from powershell script to some variable in batch file?

      Thanks
      Rajeev

      • 3 Dmitry Sotnikov May 26, 2010 at 10:13 pm

        Rajeev,

        I believe you can just supply the code in the exit command – e.g.:

        exit 1

        If this does not help, please ask at the forum at powergui.org – there are a lot of smart guys there and they should be able to help.

        Dmitry

  2. 4 Andy June 28, 2008 at 12:47 am

    powershell.exe -command “1..10 | foreach-object { ‘Current output:’; $_; }”

  3. 5 Bruce Payette June 29, 2008 at 12:27 am

    One way to completely avoid the quoting problem is to use the -encoded parameter on powershell.exe. This takes a Base64 encoded string, decodes it and then executes it. For example, we’ll put some code in a script block (since it will be syntax checked) instead of a simple string:

    PS (41) > $code = {
    >> #iterate numbers 1 through 10
    >> 1..10 | foreach-object {
    >> # just output them
    >> “Current output:”
    >> $_
    >> }
    >>
    >> }
    >>

    Now encode the command:

    PS (42) > $encoded = [convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($code))

    The result of this looks like:

    PS (43) > $encoded
    MQAuAC4AMQAwACAAfAAgAGYAbwByAGUAYQBjAGgALQBvAGIAagBlAGMAdAAgAHsACgAjACAAagB1AHMAdAAgAG8AdQB0AHAAdQB0ACAAdABoAGUAbQAKACI
    AQwB1AHIAcgBlAG4AdAAgAG8AdQB0AHAAdQB0ADoAIgAKACQAXwAKAH0ACgA=

    Then run it:

    (CXT: win7) (STA-ISS) (44) > powershell -encoded MQAuAC4AMQAwACAAfAAgAGYAbwByAGUAYQBjAGgALQBvAGIAagBlAGMAdAAgAHsACgAjACA
    AagB1AHMAdAAgAG8AdQB0AHAAdQB0ACAAdABoAGUAbQAKACIAQwB1AHIAcgBlAG4AdAAgAG8AdQB0AHAAdQB0ADoAIgAKACQAXwAKAH0ACgA=

    BTW – PowerShell has built-in support for this so when you call powershell from powershell with a scriptblock, the script block is automatically encoded and passed to the child process:

    (CXT: win7) (STA-ISS) (46) > powershell { “Hi there. Today is ” + (get-date).DayOfWeek }
    Hi there. Today is Saturday

    -bruce
    ——————–
    Principal Developer, Windows PowerShell Team
    Microsoft

  4. 6 Manish December 19, 2010 at 2:15 pm

    Hi Could you please help me writing this powershell code into a .bat file.

    POWERSHELL CODE

    param([string]$alertID,[string[]]$Recipients)
    $error.clear()
    $erroractionpreference = “SilentlyContinue”
    $thisScript = $myInvocation.MyCommand.Path
    $scriptRoot = Split-Path(Resolve-Path $thisScript)
    $errorLogFile = Join-Path $scriptRoot “error.log”
    if (Test-Path $errorLogFile) {Remove-Item $errorLogFile -Force}

    function getResStateName($resStateNumber)
    {
    switch($resStateNumber)
    {
    “0” { $resStateName = “New” }
    #”85″ { $resStateName = “Notified” }
    “255” { $resStateName = “Closed” }
    }
    $resStateName
    }

    ———————
    i tried below but it errored out stating ‘param’ is not recognized as an internal or external command,
    operable program or batch file.
    ——————————–
    param([string]$alertID,[string[]]$Recipients); $error.clear(); $erroractionpreference = “SilentlyContinue”; $thisScript = $myInvocation.MyCommand.Path; $scriptRoot = Split-Path(Resolve-Path $thisScript);$errorLogFile = Join-Path $scriptRoot “error.log”;if (Test-Path $errorLogFile) {Remove-Item $errorLogFile -Force;}$kbxmlfile =”E:\Scripts\new2.xml”; function getResStateName($resStateNumber){ switch($resStateNumber) { “0” { $resStateName = “New” ;} “255” { $resStateName = “Closed” ;} }

    • 7 Dmitry Sotnikov May 25, 2011 at 9:25 pm

      Manish,

      In general, use the -EncodedCommand approach recommended by Bruce above.

      However, your code seems to expect input parameters ($alertID and $recipients) which makes is tricky… Do you actually need to pass these as parameters? Do the values come from the bat file?

      One thing you could try to do is: save the script as ps1 file and then invoke:

      powershell.exe -Command “& c:\myscript.ps1 123 ‘foo'”

      Dmitry

  5. 8 walid toumi May 20, 2011 at 7:42 pm

    ;@echo off & Title Demo Batch_PS
    ;Findstr -rbv ; %0 | powershell -c –
    ;goto:sCode

    Function Get-MyFavoriteBeep
    {
    $s = “262_500″,”393_500″,”350_100″,”330_100″,”294_100″,”525_500”
    $s += “393_500″,”350_100″,”330_100″,”294_100″,”525_500″,”393_500”
    $s += “350_200″,”330_200″,”350_200″,”294_400”
    $s |% {
    [console]::beep($_.split(“_”)[0],$_.split(“_”)[1])
    }
    }
    Get-MyFavoriteBeep

    ;:sCode
    ;echo done
    ;pause & goto :eof

  6. 10 walid toumi May 20, 2011 at 7:55 pm

    on dirai qu’il y a un problème avec les guillemets, ils sont males interpréter par la balise “code”
    voici un autre exemple:

    ;@echo off & Title Demo Batch_PS
    ;Findstr -rbv ; %0 | powershell -c –
    ;goto:sCode

    1..9 | % { write-host $env:username -fore $_ }

    ;:sCode
    ;echo done
    ;pause & goto :eof

    • 11 Dmitry Sotnikov May 25, 2011 at 9:29 pm

      Walid,

      You can either use single quotes inside double quotes:

      “This string has substrings ‘a’ and ‘b’ and gets parsed fine”

      Or -EncodedCommand as explained above.

      Dmitry

  7. 12 walid toumi May 26, 2011 at 8:17 pm

    hi Dmitry,

    another variant:

    @echo off
    ::::::::::::::::::::::::::::::::::::
    For /f “delims=:” %%a In (‘
    findstr /Bn “” %0
    ‘) do Set /ALine=%%a
    more +%Line% %0 | powershell -c –
    ::::::::::::::::::::::::::::::::::::
    :: ici suite du code Batch

    dir
    pause & exit /b

    ::::::::::::::::::::::::::::::
    Function Get-Test {
    param()
    Begin{
    Write-Warning “Debut du traitement”
    }
    Process{
    write-verbose “ici n’importe quel traitement”
    gps power*,cmd*

    }
    End{
    Write-warning “Fin du traitement”
    }
    }
    Get-Test
    exit

  8. 13 walid toumi May 26, 2011 at 8:21 pm

    Dmitry,

    sorry for my english,

    les codent que j’ai mis dans ton blog sont tronqués, il y a un problème avec la balise “code” voici le lien de mon blog avec les scripts en question

    http://walid-toumi.blogspot.com/

    merci

  9. 14 Anonymous November 3, 2011 at 11:07 am

    Thanks a lot Dude. The script sample saved a lot of time…….

  10. 15 Pardha Nallan November 3, 2011 at 7:40 pm

    Thanks for this.. But how can I schedule this batch file in task scheduler? Do I have to add arguments?
    I’ve tried but its not working

  11. 17 Håkon Lund February 16, 2012 at 11:53 am

    Hi Dmitry!
    I’m making a batch file who is supposed to run av powershell script file, but it won’t work.

    As you said above: “If you can have an external .ps1 file – you just invoke powershell.exe and supply the path to the script file as parameter.”

    Well.. i can’t make it work.

    Here is my batch-file code:
    ———————–
    @ECHO off
    echo test!
    C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe C:\Scripts\test.ps1
    ———————–

    I’m running the batchfile from java which do work, but the batch file never runs the .ps1 file. Any ideas where i’m failling?

    Regards
    Håkon Lund

  12. 19 andrew June 19, 2015 at 10:01 am

    hi. I am running a powershell.exe line within a batch file. How can i pass named variables obtained/set during the traditional cmd/batch section of the batch file into my powershell.exe line?

    e.g.
    earlier in the batch file,
    set DB = myDBname
    is executed (this value needs to be set depending on the scenario and cannot be trivially set within the powershell.exe line)

    I then want to pass this variable to be used in the -database variable for invoke-sqlcmd

    thanks

  13. 20 Денис Порфирьев (den po) September 2, 2015 at 3:29 pm

    Hey, check my one:

    ———-
    @powershell -command “(Get-Content \”%0\”) | select -skip 1 ” | powershell -c – & goto :eof
    Write-Host “put your powershell code here”
    ———-

    • 21 Mirco Babin June 21, 2016 at 12:16 am

      Update to the reply of den PO:

      @setlocal
      @set command=”(Get-Content \”%~f0\”) | select -skip 4″
      @powershell -NoProfile -ExecutionPolicy Bypass -command %command% | powershell -c –
      @goto :eof

      Write-Host “Hello world”;

      • 22 Brian "The Noob" January 26, 2017 at 11:23 am

        I was wondering if I could get a little help with the attempt at zipping a few files in a directory. PS scripts have been blocked, running them with a batch file seems to work but I can’t figure out what I am doing wrong when I covert the following:

        $srcdir = “C:\temp222”
        $zipFilename = “APS.zip”
        $zipFilepath = “C:\temp222”
        $zipFile = “$zipFilepath$zipFilename”

        if(-not (test-path($zipFile))) {
        set-content $zipFile (“PK” + [char]5 + [char]6 + (“$([char]0)” * 18))
        (dir $zipFile).IsReadOnly = $false
        }

        $shellApplication = new-object -com shell.application
        $zipPackage = $shellApplication.NameSpace($zipFile)
        $files = Get-ChildItem -Path $srcdir | where{! $_.PSIsContainer}

        foreach($file in $files) {
        $zipPackage.CopyHere($file.FullName)
        while($zipPackage.Items().Item($file.name) -eq $null){
        Start-sleep -seconds 1
        }
        }


  1. 1 Passing parameters to -EncodedCommand « Dmitry’s PowerBlog: PowerShell and beyond Trackback on July 6, 2011 at 10:01 am
  2. 2 Subscribe Confirmed Subscription to Posts on Dmitry’s PowerBlog: PowerShell and beyond « Wag the Real Trackback on September 17, 2011 at 12:48 pm
  3. 3 PowerShell script in a .bat file « kb4you Trackback on April 9, 2012 at 4:06 pm
  4. 4 PowerShell script in a .bat file « VM.Blog. Trackback on June 23, 2012 at 5:41 pm
  5. 5 How do I run SP2010 Management Shell Script from a BAT file? « Rana Omer Hussain's RegEdit Trackback on July 26, 2012 at 10:22 am
  6. 6 How to run SP2010 Management Shell Script from a BAT file? « Rana Omer Hussain's RegEdit Trackback on July 26, 2012 at 10:24 am

Leave a comment




Legal

The posts on this blog are provided “as is” with no warranties and confer no rights. The opinions expressed on this site are mine and mine alone, and do not necessarily represent those of my employer - WSO2 or anyone else for that matter. All trademarks acknowledged.

© 2007-2014 Dmitry Sotnikov

June 2008
M T W T F S S
 1
2345678
9101112131415
16171819202122
23242526272829
30