262

I am trying to run this script in PowerShell. I have saved the below script as ps.ps1 on my desktop.

$query = "SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2"
Register-WMIEvent -Query $query -Action { invoke-item "C:\Program Files\abc.exe"}

I have made a batch script to run this PowerShell script

@echo off
Powershell.exe set-executionpolicy remotesigned -File  C:\Users\SE\Desktop\ps.ps1
pause

But I am getting this error:

Enter image description here

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eka
  • 14,170
  • 38
  • 128
  • 212
  • Note that if you get wacky errors executing like this for scripts that work when the script is invoked from within PowerShell, you might need to use `pwsh.exe` in your bat file instead of `powershell.exe`. Quick explanation: `powershell.exe` is v5.1- and `pwsh.exe` is 6.0+. [More here](https://superuser.com/a/1556070/1143880). – ruffin Oct 26 '21 at 20:21

9 Answers9

351

You need the -ExecutionPolicy parameter:

Powershell.exe -executionpolicy remotesigned -File  C:\Users\SE\Desktop\ps.ps1

Otherwise PowerShell considers the arguments a line to execute and while Set-ExecutionPolicy is a cmdlet, it has no -File parameter.

Joey
  • 344,408
  • 85
  • 689
  • 683
  • 1
    Don't you have to run as admin to change the execution policy? Cause I think it changes an entry in the registry – Kellen Stuart Nov 04 '16 at 04:32
  • 1
    @KolobCanyon: To change it, you need to be Adminstrator, yes. Above doesn't change it, though, it just specifies for a given instance what its execution policy should be. – Joey Nov 05 '16 at 12:50
  • 3
    @Joey Haha, so effectively you can override this policy without being an admin. Is that a security issue? – Kellen Stuart Nov 05 '16 at 16:45
  • 4
    @KolobCanyon: If you're in a position where you can run PowerShell, you can do pretty much everything else as well. Note that the execution policy does not mean that PowerShell has more privileges than it would have otherwise. In a way it's merely a convenience to avoid accidentally running things you may not want to run. Similar to having to prefix commands in the current directory with `./` and having an executable flag on Unix. – Joey Nov 06 '16 at 14:26
  • The word 'Powershell' is Case sensitive ?? – Rajeev Feb 01 '23 at 07:28
  • 1
    @Rajeev: No. Although on Linux you probably have to use `pwsh`, all lower-case. – Joey Feb 05 '23 at 22:45
142

I explain both why you would want to call a PowerShell script from a batch file and how to do it in my blog post here.

This is basically what you are looking for:

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& 'C:\Users\SE\Desktop\ps.ps1'"

And if you need to run your PowerShell script as an admin, use this:

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""C:\Users\SE\Desktop\ps.ps1""' -Verb RunAs}"

Rather than hard-coding the entire path to the PowerShell script though, I recommend placing the batch file and PowerShell script file in the same directory, as my blog post describes.

deadlydog
  • 22,611
  • 14
  • 112
  • 118
  • Invoke-WebRequest is working fine when I type the command line in a cmd window, but returns a 404 whenever I run it from within a batch file. I'm trying `PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass Invoke-WebRequest https://www.example.com/example.ics -OutFile C:\_my\script.ics' -Verb RunAs}";` or `powershell -Command "Invoke-WebRequest https://www.example.com/example.ics -OutFile c:\_my\file.ics"`, or using the -File option to same in a .ps1 file, or `(New-Object Net.WebClient).DownloadFile`. Any ideas? – Chris Oct 07 '14 at 17:29
  • Try using -ExecutionPolicy Unrestricted. I'm guessing that the Bypass option does not give PowerShell network access. – deadlydog Oct 07 '14 at 21:07
31

If you want to run from the current directory without a fully qualified path, you can use:

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& './ps.ps1'"
e.gad
  • 1,088
  • 19
  • 22
22

If you run a batch file calling PowerShell as a administrator, you better run it like this, saving you all the trouble:

powershell.exe -ExecutionPolicy Bypass -Command "Path\xxx.ps1"

It is better to use Bypass...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Root Loop
  • 3,004
  • 9
  • 46
  • 72
8

This allows Batch files to contain PowerShell code inside of them (save as test.cmd or test.bat)

<# :
  @echo off
    powershell /nologo /noprofile /command ^
        "&{[ScriptBlock]::Create((cat """%~f0""") -join [Char[]]10).Invoke(@(&{$args}%*))}"
  exit /b
#>

Write-Host Hello, $args[0] -fo Green
# * Your program goes here * ...
xjcl
  • 12,848
  • 6
  • 67
  • 89
DimoN
  • 81
  • 1
  • 1
  • 1
    Very cool trick. What's [Char[]]10? Does it have any limitations? – jackhab Jul 26 '21 at 11:30
  • [`%~f0`=fully-qualified path name of invoker](https://stackoverflow.com/a/35685743/495157) [`@( )`=Array Subexpression operator. `$( )`=Subexpression operator.](https://ss64.com/ps/syntax-operators.html) [`$args`](https://ss64.com/ps/syntax-args.html) – JGFMK Nov 25 '22 at 07:13
  • This is a great solution because it supports Read-Host and args you pass to it! – xjcl Jun 21 '23 at 10:52
  • @jackhab `[Char[]]10` is the 10th ASCII character, i.e. `\n` (newline) – xjcl Jun 21 '23 at 10:56
  • @jackhab `[Char[]]10` is the 10th ASCII character, i.e. `\n`. This is needed because cat/Get-Content reads files as a list of strings, so they need to be joined using the newline character. A simpler alternative is to just `cat -Raw '%~f0'`, theen no joining is needed :) – xjcl Jun 21 '23 at 11:56
6

Posted it also here: How to run powershell command in batch file

Following this thread:
https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/converting-powershell-to-batch


you can convert any PowerShell script into a batch file easily using this PowerShell function:

function Convert-PowerShellToBatch
{
    param
    (
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [string]
        [Alias("FullName")]
        $Path
    )
 
    process
    {
        $encoded = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes((Get-Content -Path $Path -Raw -Encoding UTF8)))
        $newPath = [Io.Path]::ChangeExtension($Path, ".bat")
        "@echo off`npowershell.exe -NoExit -encodedCommand $encoded" | Set-Content -Path $newPath -Encoding Ascii
    }
}


To convert all PowerShell scripts inside a directory, simply run the following command:

Get-ChildItem -Path <DIR-PATH> -Filter *.ps1 |
  Convert-PowerShellToBatch

Where is the path to the desired folder. For instance:

Get-ChildItem -Path "C:\path\to\powershell\scripts" -Filter *.ps1 |
  Convert-PowerShellToBatch


To convert a single PowerShell script, simply run this:

Get-ChildItem -Path <FILE-PATH> |
  Convert-PowerShellToBatch

Where is the path to the desired file.

The converted files are located in the source directory. i.e., <FILE-PATH> or <DIR-PATH>.

Putting it all together:
create a .ps1 file (PowerShell script) with the following code in it:

function Convert-PowerShellToBatch
{
    param
    (
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [string]
        [Alias("FullName")]
        $Path
    )
 
    process
    {
        $encoded = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes((Get-Content -Path $Path -Raw -Encoding UTF8)))
        $newPath = [Io.Path]::ChangeExtension($Path, ".bat")
        "@echo off`npowershell.exe -NoExit -encodedCommand $encoded" | Set-Content -Path $newPath -Encoding Ascii
    }
}

# change <DIR> to the path of the folder in which the desired powershell scripts are.
# the converted files will be created in the destination path location (in <DIR>).
Get-ChildItem -Path <DIR> -Filter *.ps1 |
  Convert-PowerShellToBatch


And don't forget, if you wanna convert only one file instead of many, you can replace the following

Get-ChildItem -Path <DIR> -Filter *.ps1 |
  Convert-PowerShellToBatch

with this:

Get-ChildItem -Path <FILE-PATH> |
  Convert-PowerShellToBatch

as I explained before.

Elyasaf755
  • 2,239
  • 18
  • 24
3

If you want to run a few scripts, you can use Set-executionpolicy -ExecutionPolicy Unrestricted and then reset with Set-executionpolicy -ExecutionPolicy Default.

Note that execution policy is only checked when you start its execution (or so it seems) and so you can run jobs in the background and reset the execution policy immediately.

# Check current setting
Get-ExecutionPolicy

# Disable policy
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
# Choose [Y]es

Start-Job { cd c:\working\directory\with\script\ ; ./ping_batch.ps1 example.com | tee ping__example.com.txt }
Start-Job { cd c:\working\directory\with\script\ ; ./ping_batch.ps1 google.com  | tee ping__google.com.txt  }

# Can be run immediately
Set-ExecutionPolicy -ExecutionPolicy Default
# [Y]es
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nux
  • 9,276
  • 5
  • 59
  • 72
3

Another easy way to execute a ps script from batch is to simply incorporate it between the ECHO and the Redirection characters,(> and >>), example:

@echo off
set WD=%~dp0
ECHO New-Item -Path . -Name "Test.txt" -ItemType "file" -Value "This is a text string." -Force > "%WD%PSHELLFILE.ps1"
ECHO add-content -path "./Test.txt" -value "`r`nThe End" >> "%WD%PSHELLFILE.ps1"
powershell.exe -ExecutionPolicy Bypass -File "%WD%PSHELLFILE.ps1"
del "%WD%PSHELLFILE.ps1"

Last line deletes the created temp file.

Dobbelina
  • 71
  • 1
  • 5
1

If your PowerShell login script is running after 5 minutes (as mine was) on a 2012 server, there is a GPO setting on a server - 'Configure Login script Delay' the default setting 'not configured' this will leave a 5-minute delay before running the login script.

mehulmpt
  • 15,861
  • 12
  • 48
  • 88
Neil 11111
  • 11
  • 1