.cmd vs .ps1 in Path - powershell

I have added a folder to my Path that contains a foo.cmd batch file and foo.ps1 powershell script. (The batch file is there to run the powershell script with a bypassed execution policy.)
When in powershell/command prompt I run
> foo
it runs foo.ps1 in preference to foo.cmd, which is the opposite of what I want. Is there any way to get round this, without having to type > foo.cmd?

Command precedence in PowerShell is defined like this:
If you do not specify a path, PowerShell uses the following precedence order when it runs commands:
Alias
Function
Cmdlet
Native Windows commands
Therefore, if you type "help", PowerShell first looks for an alias named "help", then a function named "Help", and finally a cmdlet named "Help". It runs the first "help" item that it finds.
Precedence of external ("native Windows") commands is then controlled by the PATH and PATHEXT environment variables. The former lists the directories Windows searches for external commands is the command wasn't invoked with a (relative or absolute) path, the latter lists extensions that Windows will auto-append if no match was found. For each environment variable the first match wins, meaning if you have a PATH listing C:\foo;C:\bar and have a folder structure like this:
C:
├─bar
│ └─a.exe
└─foo
├─a.cmd
└─a.vbs
Windows will execute C:\foo\a.cmd when you invoke the command a (without path or extension), because C:\foo comes first in the PATH and .cmd comes before .vbs in the PATHEXT variable.
However, PowerShell scripts seem to rank somewhere between cmdlets and external commands as far as PowerShell is concerned, because their extension not listed in $env:PATHEXT, but you can't supersede builtin cmdlets with PowerShell scripts of the same name. I wasn't able to find documentation about this, though.
Bottom line: I think you'll either have to invoke the batch script with extension or rename the PowerShell script. The latter could be done by appending a fixed suffix to the basename of the file and then invoke it from the batch script like this:
#echo off
set "suffix=-bar"
powershell.exe -ExecutionPolicy ByPass -File "%~dpn0%suffix%.ps1"

Related

Powershell Script only opens as a .txt file when run

Hi I have a script on a memory stick that I want to be able to run in cmd line before a computer has windows fully installed. (the stage just after you've connected to a network).
Cmd used to run the script.
Start > Run D:\pscript\Intune.ps1
This only opens a .txt file, while researching I've found that the reason this happens is due to security, is there anyway to override this bar changing the default file type out.
Unlike batch files (.cmd, .bat) associated with cmd.exe (the legacy Command Prompt), PowerShell's .ps1 script files are by default not directly executable from outside PowerShell.
Instead, they are treated as documents that are by default associated with either Notepad or the (obsolescent) Windows PowerShell ISE, depending on the , and invoking them therefore opens them for editing, which applies to the following contexts:
Invoking a .ps1 file from cmd.exe
Invoking a .ps1 file from Start Menu's Run dialog, as in your case (which you can invoke with WinKey+R, for instance)
Opening (double-clicking) a .ps1 file from File Explorer or Desktop.
To execute a .ps1 script from outside PowerShell, you must therefore invoke it via PowerShell's CLI, powershell.exe for Windows PowerShell, pwsh for PowerShell (Core) 7+.
In the simplest case, using Windows PowerShell and the -File parameter, as also shown by Mathias R. Jessen in a comment; see the comments below and the linked docs for additional parameters:
# Note:
# * The effective execution policy applies; use -ExecutionPolicy Bypass to bypass.
# * Profiles are loaded; use -NoProfile to suppress.
# * The console window auto-closes when the script terminates; use -NoExit
# to keep the session open.
powershell.exe -File D:\pscript\Intune.ps1
For a comprehensive overview of PowerShell's CLI, see this post.
It is possible - though not advisable - to configure your system to execute .ps1 files by default - see this answer.

Powershell: attempting to run a script whose filename has embedded spaces executes a different, similarly named script instead

I am executing PS script under w2008R2 .
e.g.
Alter_all.ps1
If I make standard copy of it to keep original intact next copy is made
e.g.
Alter_all - Copy.ps1
If I try to execute new edited script Alter_all - Copy.ps1 it runs content from previous script no matter what. To resolve this I found that I need to rename script to be without spaces and only then script works with new values
e.g.
Alter_all-Copy.ps1
It seems that Powershell somehow checks name of script but stops at empty space name thinking that you are executing same script you were executing previously?
What is the reason behind this?
It's because - Copy.ps1 are passed as arguments.
Try to execute your new script using the following syntax :
& 'Alter_all - Copy.ps1'
Arnaud Claudel's helpful answer shows you how to invoke a script with embedded spaces in its path from a PowerShell console window or script.
In a later comment you state that you're not calling your script from the command line, but attempting to run it by double-clicking from File Explorer:
By default, for security reasons, double-clicking *.ps1 files does NOT run them - instead, they are opened for editing.
That double-clicking actually executes your scripts if they don't contains spaces in their paths implies two things:
Your system uses a custom configuration that overrides the default behavior.
That custom configuration is flawed, because it cannot handle file paths with embedded spaces.
Therefore, you need to fix your custom configuration, by editing the registry:
Note:
The following assumes the default configuration and overrides it at the current-user level, which doesn't require admin privileges.
If you do want to modify the machine-level configuration, substitute HKEY_LOCAL_MACHINE for HKEY_CURRENT_USER below, as noted in the comments.
The currently configured execution policy is respected, and profiles are loaded; -NoExit is used to invoke powershell.exe, which means that the windows stays open after the script exits.
These behaviors can easily be tweaked by editing the code below.
Note how the placeholder for the script file path, %1, is enclosed in \"...\" to ensure that even paths with embedded spaces are passed as a single argument to powershell.exe's -File parameter.
# Create temp. file for registry definitions.
$tmpFile = [IO.Path]::GetTempFileName()
# Change this to 'HKEY_LOCAL_MACHINE' to override
# the machine-level configuration - REQUIRES RUNNING AS ADMIN.
$rootKey = 'HKEY_CURRENT_USER'
# Create the registry definitions...
#"
Windows Registry Editor Version 5.00
[$rootKey\Software\Classes\Microsoft.PowerShellScript.1\shell\Open\command]
#="$($PSHOME -replace '\\', '\\')\\powershell.exe -NoExit -File \"%1\" %*"
"# > $tmpFile
# ... and import them.
reg.exe import $tmpFile
# Clean up.
Remove-Item -LiteralPath $tmpFile

Running a small WMI Powershell Script

I'm trying to have a few scripts that I can map to run from my keyboard for quickly changing the monitor/screen brightness. After some searching on the internet, I found this script which works when I enter it into Powershell.
$monitor=#(gwmi WmiMonitorBrightnessMethods -ns root/wmi)[0]
$monitor.WmiSetBrightness(50,0)
After I saved it as a .ps1 file and tried running it from the file, powershell tells me: The term "path of the file" is not recognized as the name of a cmdlet, function... and so on.
I'm not familiar with Powershell at all, can someone help with what I need to add in order for the script to run properly?
By default you can't run a PowerShell script that is in the current directory without putting .\ in front of the script name, or calling the full path of the script.
This is a security feature.
If you are in the directory that contains the script, run it by executing in a PowerShell window:
.\yourscript.ps1
Where yourscript is the name of your script.
See here for more information: https://ss64.com/ps/syntax-run.html
You may also see this error if your script has spaces in its name. If that is the case, enclose the path in quotes:
.\'your script.ps1'

Execute batch file in Powershell

I want to execute the following from a batch file:
"C:\OpenCover\tools\OpenCover.Console.exe" -register:user -target:"%VS110COMNTOOLS%..\IDE\mstest.exe" -targetargs:"/testcontainer:\"C:\Develop\bin\Debug\MyUnitTests.dll\" ... "
PAUSE
Now I would like to log the output of the process to a file for which I came across the quite handy powershell usage of
powershell "dir | tee output.log"
but this does not take my batch file as first argument (powershell "my.bat | tee output.log") because it is not the name of a cmdlet or a function or a script file.
I could change my batch file so that is says powershell "OpenCover.Console.exe..." but I would have to adapt all quotes and change escape characters and so forth.
Is there a way to make a batch file execute in powershell? Or is there a way to drop in my line unchanged from the batch after some powershell command and it all executes "like it ought to"?
Unless your batch file is in a folder in the %PATH%, PowerShell won't find it [1], so you'll have to supply an explicit file path (whether relative or absolute).
For instance, if the batch file is in the current folder, run:
powershell -c ".\my.bat | tee output.log"
Consider adding -noprofile to suppress loading of the profile files, which is typically only needed in interactive sessions.
If your batch file path contains embedded spaces, enclose it in single quotes and prepend &:
powershell -c "& '.\my script.bat' | tee output.log"
Note: I've deliberately added the -c (short for: -Command) parameter name above; while powershell.exe - Windows PowerShell - defaults to this parameter, that is no longer true in PowerShell [Core] v6+ (whose executable name is pwsh), where -File is now the default - see about_PowerShell.exe and about_pwsh
[1] More accurately, PowerShell - unlike cmd.exe - will by design not execute scripts in the current folder by their mere filename (in an interactive PowerShell session you'll get a hint to that effect). This is a security feature designed to prevent accidental invocation of a different executable than intended.
Unless you have some purpose for doing so not stated in the OP, there isn't a reason to use both Powershell and a batch script. If you want to do this solely from PS, you can create a PS script that does everything the batch file does.
To avoid the escaping issues (or alternatively to take advantage of CMD.EXE's somewhat strange escaping behavior :-) you can use --%, introduced in PS 3.0. It is documented under about_escape_characters.

Set up PowerShell Script for Automatic Execution

I have a few lines of PowerShell code that I would like to use as an automated script. The way I would like it to be able to work is to be able to call it using one of the following options:
One command line that opens PowerShell, executes script and closes PowerShell (this would be used for a global build-routine)
A file that I can double-click to run the above (I would use this method when manually testing components of my build process)
I have been going through PowerShell documentation online, and although I can find lots of scripts, I have been unable to find instructions on how to do what I need. Thanks for the help.
From http://blogs.msdn.com/b/jaybaz_ms/archive/2007/04/26/powershell-polyglot.aspx
If you're willing to sully your beautiful PowerShell script with a little CMD, you can use a PowerShell-CMD polyglot trick. Save your PowerShell script as a .CMD file, and put this line at the top:
#PowerShell -ExecutionPolicy Bypass -Command Invoke-Expression $('$args=#(^&{$args} %*);'+[String]::Join(';',(Get-Content '%~f0') -notmatch '^^#PowerShell.*EOF$')) & goto :EOF
If you need to support quoted arguments, there's a longer version, which also allows comments. (note the unusual CMD commenting trick of double #).
##:: 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 -ExecutionPolicy Bypass -Command Invoke-Expression $('$args=#(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join(';',$((Get-Content '%~f0') -notmatch '^^##'))) & goto :EOF
Save your script as a .ps1 file and launch it using powershell.exe, like this:
powershell.exe .\foo.ps1
Make sure you specify the full path to the script, and make sure you have set your execution policy level to at least "RemoteSigned" so that unsigned local scripts can be run.
Run Script Automatically From Another Script (e.g. Batch File)
As Matt Hamilton suggested, simply create your PowerShell .ps1 script and call it using:
PowerShell C:\Path\To\YourPowerShellScript.ps1
or if your batch file's working directory is the same directory that the PowerShell script is in, you can use a relative path:
PowerShell .\YourPowerShellScript.ps1
And before this will work you will need to set the PC's Execution Policy, which I show how to do down below.
Run Script Manually Method 1
You can see my blog post for more information, but essentially create your PowerShell .ps1 script file to do what you want, and then create a .cmd batch file in the same directory and use the following for the file's contents:
#ECHO OFF
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%MyPowerShellScript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'"
Replacing MyPowerShellScript.ps1 on the 3rd line with the file name of your PowerShell script.
This will allow you to simply double click the batch file to run your PowerShell script, and will avoid you having to change your PowerShell Execution Policy.
My blog post also shows how to run the PowerShell script as an admin if that is something you need to do.
Run Script Manually Method 2
Alternatively, if you don't want to create a batch file for each of your PowerShell scripts, you can change the default PowerShell script behavior from Edit to Run, allowing you to double-click your .ps1 files to run them.
There is an additional registry setting that you will want to modify so that you can run scripts whose file path contains spaces. I show how to do both of these things on this blog post.
With this method however, you will first need to set your execution policy to allow scripts to be ran. You only need to do this once per PC and it can be done by running this line in a PowerShell command prompt.
Start-Process PowerShell -ArgumentList 'Set-ExecutionPolicy RemoteSigned -Force' -Verb RunAs
Set-ExecutionPolicy RemoteSigned -Force is the command that actually changes the execution policy; this sets it to RemoteSigned, so you can change that to something else if you need. Also, this line will automatically run PowerShell as an admin for you, which is required in order to change the execution policy.
Source for Matt's answer.
I can get it to run by double-clicking a file by creating a batch file with the following in it:
C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe LocationOfPS1File
you can use this command :
powershell.exe -argument c:\scriptPath\Script.ps1