Newbie powershell argument issues - powershell

I made powershell script that [1] accepts 2 arguments (aka parameters), [2] changes a file's modified date & time, and [3] writes something to host. The following command line works just fine in the powershell console, but triggers an error message when I run the same command line in a Windows cmd prompt (DOS) Window:
E:\Apps\UtilitiesByMarc\Change_DateTime_for_test1.bat_and_Hello_world_with_2_named_args_aaa.ps1 -dateTimeVarArg "01/11/2005 06:01:36" -file_dateTimeMod_fullname "E:\Apps\Delete01\test1.bat"
The following is the coding for the powershell script to which I gave the long name, 'Change_DateTime_for_test1.bat_and_Hello_world_with_2_named_args_aaa.ps1':
param ( [string]$dateTimeVarArg, [string]$file_dateTimeMod_fullname)
Get-ChildItem $file_dateTimeMod_fullname | % {$_.LastWriteTime = $dateTimeVarArg}
#Get-ChildItem "E:\Apps\Delete01\test1.bat" | % {$_.LastWriteTime = $dateTimeVarArg}
$strString = "Hello World"
write-host $strString
function ftest{
$test = "Test"
write-host $test
}
ftest
When I run the command line shown above in a Windows DOS command prompt setting, I get the following error message:
Exception setting "LastWriteTime": "Cannot convert null to type "System.DateTime"."
At E:\Apps\UtilitiesByMarc\Change_DateTime_for_test1.bat_and_Hello_world_with_1_named_arg_aaa.ps1:6 char:50
+ ... "E:\Apps\Delete01\test1.bat" | % {$_.LastWriteTime = $dateTimeVarArg}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
I would like to know [1] how to alter the command line shown above (which works fine in the powershell console) so that it works in a Windows DOS command prompt setting, and [2] where I can learn more about why my command line triggers errors, and how to avoid them.
According to the output from the command "Get-Host | Select-Object Version", I am running version. 5.1.19041.1682.
Any tips would be much appreciated.

By default, you can not directly execute PowerShell scripts (.ps1 files) from cmd.exe, the Windows legacy shell, or from outside PowerShell altogether.
Attempting to do so opens the script file for editing instead, as does double-clicking .ps1 files from File Explorer / the desktop.
Executing .ps1 scripts from outside PowerShell itself requires use of the PowerShell CLI (powershell.exe for Windows PowerShell, pwsh for PowerShell (Core) 7+), which in your case translates to a call such as (additional CLI parameters may be called for):
powershell.exe -File E:\Apps\UtilitiesByMarc\Change_DateTime_for_test1.bat_and_Hello_world_with_2_named_args_aaa.ps1 -dateTimeVarArg "01/11/2005 06:01:36" -file_dateTimeMod_fullname "E:\Apps\Delete01\test1.bat"
As for what you tried:
The fact that you were able to execute your .ps1 from cmd.exe suggests that you changed the file-type definition for such files to execute via powershell.exe instead.
The fact that the arguments you tried to pass to the script were ignored - as implied by the error message you got - suggests that you used File Explorer's Open with shortcut-menu command to choose to open all .ps1 files with powershell.exe; said method does not support argument-passing.
There is a way to change the file-type definition to support argument-passing too, and it is detailed in this answer (section "Programmatic method").
Generally, however, I suggest not applying this customization, especially in batch files that must also be run by other users / on other machines, which cannot be expected to have the same customization in place.

Related

Format PS1 to be Double-Clicked [duplicate]

I have my_project.ps1 file from which I am activating virtual environment & starting my project.
currently I need to open my powershell then after I need to go to directory where I have saved my .ps1 file & have to open it from powershell only.
Is there any way so that I can double click on .ps1 file & it will open automatically in power shell ?
By design, double-clicking (opening) *.ps1 files from the Windows [GUI] shell (in this case: Desktop, File Explorer, and the taskbar, via pinned items) does not execute them - instead they're opened for editing in Notepad or in the PowerShell ISE, depending on the Windows / PowerShell version.
However, since at least Windows 7, the shortcut menu for *.ps1 files contains a Run with PowerShell command, which does invoke the script at hand; this may be enough for your purposes, but this invocation method has limitations - see the bottom section for details.
If you do want to redefine double-clicking / opening so that it executes *.ps1 scripts, you have two options:
Note:
For a given script (as opposed to all .ps1 files), you may alternatively create a shortcut file or batch file that launches it, but that isn't a general solution, as you'd have to create a companion file for each and every .ps1 file you want to run by double-clicking. It does, however, give you full control over the invocation. You can create shortcut files interactively, via File Explorer, as described in this answer, or programmatically, as shown in this answer. Similarly, you may create a companion batch file (.cmd or .bat) that invokes your script, because batch file are executed when double-clicked; e.g., if you place a batch file with the same base name as your .ps1 script in the same directory (e.g., foo.cmd next to foo.ps1), you can call it from your batch file as follows; -NoExit keeps the session open:
#powershell.exe -NoExit -File "%~dpn0.ps1" %*
The methods below also enable direct execution of a .ps1 script from a cmd.exe console window, synchronously, inside the same window. In other words: You can execute, say, script foo.ps1 directly as such, instead of having to use the PowerShell CLI, say, powershell.exe -File foo.ps1
[Not recommended] GUI method:
Use File Explorer to make PowerShell execute .ps1 files by default:
Right-click on a .ps1 file and select Properties.
Click on Change... next to the Opens with: label.
Click on More apps on the bottom of the list and scroll down to Look for another app on this PC
Browse to or paste file path C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe and submit.
This method gives you no control over the specifics of the PowerShell invocation and has major limitations; in effect you'll end up with the following behavior:
Major limitations:
Script paths with embedded spaces and ' chars. cannot be invoked this way, because, even though such paths are passed with double quotes, the latter are in effect stripped by PowerShell, because the path is passed to the implied -Command parameter, which first strips (unescaped) double quotes from the command line before interpreting the result as PowerShell code - in which case paths with spaces are seen as multiple arguments / paths that contain (an odd number of) ' cause a syntax error.
Note that if you were to select pwsh.exe instead, the CLI of the cross-platform, install-on-demand PowerShell (Core) 7+ edition, that problem would not arise, because it defaults to the -File parameter - in which case a double-quoted script-file path is properly recognized.
For the difference between PowerShell CLI calls using -Command vs. those using -File, see this answer.
Passing arguments is not supported, which matters if you want to invoke .ps1 files directly from cmd.exe and need to pass arguments.
The redefinition is only in effect for the current user - which is probably a good thing, as other users may not expect this change, which can result in unwanted execution of scripts.
Whatever execution policy is in effect will be honored; e.g., if Restricted is in effect, invocation will fail altogether.
As with the default Run in PowerShell command, the window in which the script runs will automatically close when the script ends - thus, unless the script explicitly prompts the user before exiting, you may not be able to examine its output.
To exercise more control over how PowerShell invokes the script including support for paths with spaces and for passing arguments, use the programmatic method shown in the next section.
Programmatic method:
Important:
The GUI method overrides a programmatic solution, so it must be removed - the code below does this automatically.
Unfortunately, there's another, accidental override that can happen if you have Visual Studio Code installed: Whenever you use File Explorer's shortcut menu to open a file in Visual Studio Code, it unexpectedly becomes the default action. The code below detects this condition and fixes the problem, but it will resurface the next time a .ps1 file is opened this way.
Modify the registry to redefine the Open shortcut-menu command for *.ps1 files at HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\shell\Open\Command, as shown below.
You can run the code as-is to create a user-level file-type definition that:
uses the executable that runs the current PowerShell session, i.e. powershell.exe in Windows PowerShell, and pwsh.exe in PowerShell (Core) 7+.
respects the effective execution policy - add an -ExecutionPolicy argument to override.
loads the profiles first - add -NoProfile to suppress loading; this is primarily of interest if you're planning to directly invoke .ps1 files from cmd.exe, not (just) from File Explorer, in combination with not using -NoExit.
runs in the script in its own directory
keeps the session open after the script exits - remove -NoExit to exit the session when the script ends; this is primarily of interest if you're planning to directly invoke .ps1 files from cmd.exe, not (just) from File Explorer.
If you requirements differ - if you need different CLI parameters and /or you want to use pwsh.exe, i.e. PowerShell (Core) 7+ instead - tweak the code first, by modifying the $cmd = ... line below; see the comments above it.
# Specify if the change should apply to the CURRENT USER only, or to ALL users.
# NOTE: If you set this to $true - which is NOT ADVISABLE -
# you'll need to run this code ELEVATED (as administrator)
$forAllUsers = $false
# Determine the chosen scope's target registry key path.
$targetKey = "$(('HKCU', 'HKLM')[$forAllUsers]):\Software\Classes\Microsoft.PowerShellScript.1\shell\Open\Command"
# In the user-specific hive (HKCU: == HKEY_CURRENT_USER), the target key
# doesn't exist by default (whereas it does in the local-machine hive (HLKM: == HKEY_LOCAL_MACHINE)),
# so we need to make sure that it exists.
if (-not $forAllUsers -and -not (Test-Path -LiteralPath $targetKey)) {
$null = New-Item -Path $targetKey -Force -ErrorAction Stop
}
# Specify the command to use when opening / double-clicking *.ps1 scripts:
# As written here:
# * The script runs in the directory in which it resides.
# * The profiles are loaded (add -NoProfile to change).
# * The current execution policy is respected (add -ExecutionPolicy <policy> to override, if possible)
# * The window stays open after the script exits (remove -NoExit to change)
# For help with all parameters, see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe
$cmd = "`"$((Get-Process -Id $PID).Path)`" -nologo -noexit -file `"%1`" %*"
# Write the command to the registry.
Set-ItemProperty -ErrorAction Stop -LiteralPath $targetKey -Name '(default)' -Value $cmd
Write-Verbose -Verbose "$(('User-level', 'Machine-level')[$forAllUsers]) file-type definition for *.ps1 files successfully updated."
# Additionally, make sure that NO OVERRIDES preempt the new definition.
# See if a user override established interactively via File Explorer happens to be defined,
# and remove it, if so.
if ($fileExplorerOverrideKey = Get-Item -ErrorAction Ignore -LiteralPath 'registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice') {
Write-Verbose -Verbose 'Removing File Explorer override...'
# Get the parent key path and the key name
$parentKeyPath = $fileExplorerOverrideKey.PSParentPath -replace '^.+?::\w+\\' # Remove the 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\' prefix
$keyName = $fileExplorerOverrideKey.PSChildName
$key = $null
try {
# Open the *parent* key for writing.
$key = [Microsoft.Win32.Registry]::CurrentUser.OpenSubkey($parentKeyPath, $true)
# Delete the subkey.
# !! Due to the specific permissions assigned by File Explorer to the key
# !! (an additional DENY access-control entry for the current user, for the key itself only, for the 'Set Value' permission),
# !! using the .DeleteSubKey*Tree*() method fails (Remove-Item implicitly uses this method and therefore fails too)
# !! However, since there should be no nested subkeys, using .DeleteSubkey() should work fine.
$key.DeleteSubKey($keyName)
}
catch {
throw
}
finally {
if ($key) { $key.Close()}
}
}
# See if *Visual Studio Code* was most recently used to open a *.ps1 file:
# If so, it inexplicably OVERRIDES a file-type definition.
# (This doesn't seem to happen with other executables.)
# !! We fix the problem, but it will RESURFACE the next time File Explorer's shortcut menu
# !! is used to open a *.ps1 file in Visual Studio Code.
if ($itm = Get-Item -ErrorAction Ignore -LiteralPath 'registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\OpenWithList') {
if (($names = $itm.GetValueNames().Where({ $itm.GetValue($_) -ceq 'Code.exe' })) -and ($mruList = $itm.GetValue('MRUList')) -and $mruList[0] -in $names) {
Write-Warning "Visual Studio Code was most recently used to open a .ps1 file, which unexpectedly overrides the file-type definition.`nCorrecting the problem for now, but it will resurface the next time you use File Explorer's shortcut menu to open a .ps1 file in Visual Studio Code."
# Note: Normally there aren't, but there *can* be *multiple* Code.exe entries, namely after manual removal of the MRUList:
# The next time you choose to open in VSCode via File Explorer's shortcut menu, an *additional* Code.exe entry is added.
do { # Trim the start of the MRUList until its first entry no longer references Code.exe
$mruList = $mruList.Substring(1)
} while ($mruList[0] -in $names)
# Update the MRUList value in the registry.
$itm | Set-ItemProperty -Name 'MRUList' -Value $mruList
}
}
Explanation of the predefined Run in PowerShell shortcut-menu command:
It is defined in registry key HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\shell\0\Command (as of Windows 10) as follows:
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "-Command" "if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & '%1'"
This command is flawed in that it breaks with script-file paths that happen to contain ' characters.
Unless execution policy AllSigned is in effect - in which case only signed scripts can be executed but are executed without prompting - the command attempts to set the execution policy for the invoked process to Bypass, which means that any script can be executed, but only after the user responds to a confirmation prompt beforehand (irrespective of whether the script is signed or not, and whether it was downloaded from the web or not).
At least in earlier Windows 7 releases / PowerShell versions, the command was misdefined[1] in a way that effectively ignored the attempt to set the process' execution policy, which meant that whatever execution policy was persistently configured applied - and no confirmation prompt was shown.
Unless the targeted script explicitly pauses to wait for user input before exiting, the window in which the script will close automatically when the script finishes, so you may not get to see its output.
The targeted script executes in the directory in which it is located as the working directory (current location)
[1] The earlier, broken command definition was "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "-file" "%1" "-Command" "if((Get-ExecutionPolicy ) -ne AllSigned) { Set-ExecutionPolicy -Scope Process Bypass }", which meant what anything after -file "%1" was passed as arguments to file "%1" instead of the intended execution of the commands following -Command; additionally - a moot point - the AllSigned operand would have need to be quoted.
To execute a PS1 file by double-click (to run)
Make a shortcut for the file and set the target to this:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "C:\Temp\MyPowershellScript.ps1"
Replace the second directory (the one in quotes) with the location of your script.
To read a PS1 file by double-click (to edit)
Same as above, but target ISE instead, as that will force it into edit mode.
C:\Windows\System32\WindowsPowerShell\v1.0\powershell_ise.exe "C:\Temp\MyPowershellScript.ps1"
Server 2012 and newer by default do not associate the .PS1 file extension with the PowerShell executable; rather, they default to open .PS1 files with notepad by default for security reasons.
If you have access, you need to change the file association through the 'default programs' in your control panel for the .PS1 files to execute by double clicking.
Also be aware that you may have to change your execution policy to get particular scripts to run.
Also, as it sounds like this script might be a core automation, you can execute scripts from in another one with either of these, without the need to change the active working directory:
Invoke-Item ""
& ''
I have fixed the registry values so that the .ps1 scripts are executed with double click or with "Run with PowerShell" from any position without problem, even with paths with multiple consecutive spaces and with apostrophes:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.ps1]
#="Microsoft.PowerShellScript.1"
[HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell\command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit -Command \"Set-Location -LiteralPath \\\"%V\\\"\""
[HKEY_CLASSES_ROOT\Directory\Shell\Powershell\command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit -Command \"Set-Location -LiteralPath \\\"%V\\\"\""
[HKEY_CLASSES_ROOT\Drive\shell\Powershell\command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit -Command \"Set-Location -LiteralPath \\\"%V\\\"\""
[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\DefaultIcon]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\",0"
[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\Open\Command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -File \"%1\""
[HKEY_CLASSES_ROOT\SystemFileAssociations\.ps1\Shell\0\Command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -File \"%1\""
[HKEY_CLASSES_ROOT\SystemFileAssociations\.ps1\Shell\Edit\Command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell_ise.exe\" -File \"%1\""
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell]
"ExecutionPolicy"="RemoteSigned"

Pipe into executable through .lnk file

I have an executable at C:\Very\Long\Path\StreamToClipboard.exe
The file C:\InPath\StreamToClipboard.lnk points to that executable.
The directory C:\InPath is in my PATH variable, and .lnk is in my PATHEXT variable.
In regular cmd, I can execute any of these commands:
echo Hello | C:\Very\Long\Path\StreamToClipboard.exe
echo Hello | C:\InPath\StreamToClipboard.lnk
echo Hello | StreamToClipboard.lnk
echo Hello | StreamToClipboard
and the the executable is started, the text "Hello" is correctly piped into that process.
In PowerShell, I can execute echo Hello | C:\Very\Long\Path\StreamToClipboard.exe and it works too. But all of the other commands don't work:
Fehler beim Ausführen des Programms "StreamToClipboard.lnk": Die angegebene ausführbare Datei ist keine gültige
Anwendung für diese Betriebssystemplattform.In Zeile:1 Zeichen:12
+ echo foo | C:\InPath\StreamToClipboard.lnk
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.
In Zeile:1 Zeichen:1
+ echo foo | C:\InPath\StreamToClipboard.lnk
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (:) [], ApplicationFailedException
+ FullyQualifiedErrorId : NativeCommandFailed
(and respective paths)
Which roughly translates to Error while executing "StreamToClipboard.lnk": The specified executable file is not a valid application for this operating system plattform.
Note that echo Hello | "C:\Very\Long\Path\StreamToClipboard.exe" also does not work, with a different error message:
In Zeile:1 Zeichen:14
+ ... cho Hello | "C:\Very\Long\Path\StreamToClipboard.exe"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ausdrücke sind nur als erstes Element einer Pipeline zulässig.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ExpressionsMustBeFirstInPipeline
Roughly translates to Expressions are only valid as the first element in a pipeline.
If I, instead of creating the .lnk file, copy the entire executable to C:\InPath\StreamToClipboard.exe, then these commands:
echo Hello | C:\InPath\StreamToClipboard.exe
echo Hello | StreamToClipboard.exe
echo Hello | StreamToClipboard
work fine.
How can I get PowerShell to accept echo Hello | StreamToClipboard (where it's an .lnk file), or at least echo Hello | StreamToClipboard.lnk?
Unlike cmd.exe, PowerShell does not support invoking shortcut files (.lnk) like console applications; instead:
A new console window opens, asynchronously.
The process in the new console window does NOT receive stdin input (via the pipeline):
If a command is used - such as echo hello, using the built-in echo alias for PowerShell's (rarely needed) Write-Output cmdlet - the following error occurs, as of Windows PowerShell v5.1 / PowerShell (Core) 7.2.4:
Cannot run a document in the middle of a pipeline
If an expression - such as 'hello' - is used, no error occurs, but the input is effectively ignored.
In other words: PowerShell considers .lnk files documents, not executables, and defers to Windows (GUI) shell for opening them;[1] in effect, invoking a .lnk file is like passing it to Invoke-Item or Start-Process; adding -Wait to the latter makes the invocation synchronous, but still runs in a separate window and doesn't support stdin input; attempting to use -NoNewWindow and/or -RedirectStandardInput (to provide stdin input via a file) results in an error, similar to the one you saw:[2]
This command cannot be run due to the error: %1 is not a valid Win32 application.
Workarounds:
Call the target .exe file directly in your pipeline, which - if your path is quoted and/or contains variable references or expressions - requires use of &, the call operator:
# & is required, because the executable path is quoted.
# Note: 'Hello' is the PowerShell-idiomatic equivalent of
# echo Hello
'Hello' | & "C:\Very\Long\Path\StreamToClipboard.exe"
Note: For simplicity, you may choose to always use & when invoking external programs, but it is only required in the cases mentioned above, discussed in more detail in this answer.
Invoke the shortcut (.lnk) file via cmd /c:
'Hello' | cmd /c C:\InPath\StreamToClipboard.lnk
Note that, in both workarounds, character encoding issues may arise, given that data is being sent to an external program:
The $OutputEncoding preference variable controls what encoding is used to send data to an external program.
The encoding stored in [Console]::OutputEncoding determines how PowerShell decodes data received from external programs.
See this answer for more information.
[1] Note that (temporarily) appending ";.LNK" to the value of $env:PATHEXT, the environment variable that lists all filename extensions that belong to executables, does not help.
[2] The reason is that these parameters cause Start-Process to switch from the ShellExecute WinAPI function to CreateProcess, and the latter only works with actual executables.

Powershell script executes correctly when I choose "run with powershell", but not when I "open with" powershell or double-click it [duplicate]

I have my_project.ps1 file from which I am activating virtual environment & starting my project.
currently I need to open my powershell then after I need to go to directory where I have saved my .ps1 file & have to open it from powershell only.
Is there any way so that I can double click on .ps1 file & it will open automatically in power shell ?
By design, double-clicking (opening) *.ps1 files from the Windows [GUI] shell (in this case: Desktop, File Explorer, and the taskbar, via pinned items) does not execute them - instead they're opened for editing in Notepad or in the PowerShell ISE, depending on the Windows / PowerShell version.
However, since at least Windows 7, the shortcut menu for *.ps1 files contains a Run with PowerShell command, which does invoke the script at hand; this may be enough for your purposes, but this invocation method has limitations - see the bottom section for details.
If you do want to redefine double-clicking / opening so that it executes *.ps1 scripts, you have two options:
Note:
For a given script (as opposed to all .ps1 files), you may alternatively create a shortcut file or batch file that launches it, but that isn't a general solution, as you'd have to create a companion file for each and every .ps1 file you want to run by double-clicking. It does, however, give you full control over the invocation. You can create shortcut files interactively, via File Explorer, as described in this answer, or programmatically, as shown in this answer. Similarly, you may create a companion batch file (.cmd or .bat) that invokes your script, because batch file are executed when double-clicked; e.g., if you place a batch file with the same base name as your .ps1 script in the same directory (e.g., foo.cmd next to foo.ps1), you can call it from your batch file as follows; -NoExit keeps the session open:
#powershell.exe -NoExit -File "%~dpn0.ps1" %*
The methods below also enable direct execution of a .ps1 script from a cmd.exe console window, synchronously, inside the same window. In other words: You can execute, say, script foo.ps1 directly as such, instead of having to use the PowerShell CLI, say, powershell.exe -File foo.ps1
[Not recommended] GUI method:
Use File Explorer to make PowerShell execute .ps1 files by default:
Right-click on a .ps1 file and select Properties.
Click on Change... next to the Opens with: label.
Click on More apps on the bottom of the list and scroll down to Look for another app on this PC
Browse to or paste file path C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe and submit.
This method gives you no control over the specifics of the PowerShell invocation and has major limitations; in effect you'll end up with the following behavior:
Major limitations:
Script paths with embedded spaces and ' chars. cannot be invoked this way, because, even though such paths are passed with double quotes, the latter are in effect stripped by PowerShell, because the path is passed to the implied -Command parameter, which first strips (unescaped) double quotes from the command line before interpreting the result as PowerShell code - in which case paths with spaces are seen as multiple arguments / paths that contain (an odd number of) ' cause a syntax error.
Note that if you were to select pwsh.exe instead, the CLI of the cross-platform, install-on-demand PowerShell (Core) 7+ edition, that problem would not arise, because it defaults to the -File parameter - in which case a double-quoted script-file path is properly recognized.
For the difference between PowerShell CLI calls using -Command vs. those using -File, see this answer.
Passing arguments is not supported, which matters if you want to invoke .ps1 files directly from cmd.exe and need to pass arguments.
The redefinition is only in effect for the current user - which is probably a good thing, as other users may not expect this change, which can result in unwanted execution of scripts.
Whatever execution policy is in effect will be honored; e.g., if Restricted is in effect, invocation will fail altogether.
As with the default Run in PowerShell command, the window in which the script runs will automatically close when the script ends - thus, unless the script explicitly prompts the user before exiting, you may not be able to examine its output.
To exercise more control over how PowerShell invokes the script including support for paths with spaces and for passing arguments, use the programmatic method shown in the next section.
Programmatic method:
Important:
The GUI method overrides a programmatic solution, so it must be removed - the code below does this automatically.
Unfortunately, there's another, accidental override that can happen if you have Visual Studio Code installed: Whenever you use File Explorer's shortcut menu to open a file in Visual Studio Code, it unexpectedly becomes the default action. The code below detects this condition and fixes the problem, but it will resurface the next time a .ps1 file is opened this way.
Modify the registry to redefine the Open shortcut-menu command for *.ps1 files at HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\shell\Open\Command, as shown below.
You can run the code as-is to create a user-level file-type definition that:
uses the executable that runs the current PowerShell session, i.e. powershell.exe in Windows PowerShell, and pwsh.exe in PowerShell (Core) 7+.
respects the effective execution policy - add an -ExecutionPolicy argument to override.
loads the profiles first - add -NoProfile to suppress loading; this is primarily of interest if you're planning to directly invoke .ps1 files from cmd.exe, not (just) from File Explorer, in combination with not using -NoExit.
runs in the script in its own directory
keeps the session open after the script exits - remove -NoExit to exit the session when the script ends; this is primarily of interest if you're planning to directly invoke .ps1 files from cmd.exe, not (just) from File Explorer.
If you requirements differ - if you need different CLI parameters and /or you want to use pwsh.exe, i.e. PowerShell (Core) 7+ instead - tweak the code first, by modifying the $cmd = ... line below; see the comments above it.
# Specify if the change should apply to the CURRENT USER only, or to ALL users.
# NOTE: If you set this to $true - which is NOT ADVISABLE -
# you'll need to run this code ELEVATED (as administrator)
$forAllUsers = $false
# Determine the chosen scope's target registry key path.
$targetKey = "$(('HKCU', 'HKLM')[$forAllUsers]):\Software\Classes\Microsoft.PowerShellScript.1\shell\Open\Command"
# In the user-specific hive (HKCU: == HKEY_CURRENT_USER), the target key
# doesn't exist by default (whereas it does in the local-machine hive (HLKM: == HKEY_LOCAL_MACHINE)),
# so we need to make sure that it exists.
if (-not $forAllUsers -and -not (Test-Path -LiteralPath $targetKey)) {
$null = New-Item -Path $targetKey -Force -ErrorAction Stop
}
# Specify the command to use when opening / double-clicking *.ps1 scripts:
# As written here:
# * The script runs in the directory in which it resides.
# * The profiles are loaded (add -NoProfile to change).
# * The current execution policy is respected (add -ExecutionPolicy <policy> to override, if possible)
# * The window stays open after the script exits (remove -NoExit to change)
# For help with all parameters, see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe
$cmd = "`"$((Get-Process -Id $PID).Path)`" -nologo -noexit -file `"%1`" %*"
# Write the command to the registry.
Set-ItemProperty -ErrorAction Stop -LiteralPath $targetKey -Name '(default)' -Value $cmd
Write-Verbose -Verbose "$(('User-level', 'Machine-level')[$forAllUsers]) file-type definition for *.ps1 files successfully updated."
# Additionally, make sure that NO OVERRIDES preempt the new definition.
# See if a user override established interactively via File Explorer happens to be defined,
# and remove it, if so.
if ($fileExplorerOverrideKey = Get-Item -ErrorAction Ignore -LiteralPath 'registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice') {
Write-Verbose -Verbose 'Removing File Explorer override...'
# Get the parent key path and the key name
$parentKeyPath = $fileExplorerOverrideKey.PSParentPath -replace '^.+?::\w+\\' # Remove the 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\' prefix
$keyName = $fileExplorerOverrideKey.PSChildName
$key = $null
try {
# Open the *parent* key for writing.
$key = [Microsoft.Win32.Registry]::CurrentUser.OpenSubkey($parentKeyPath, $true)
# Delete the subkey.
# !! Due to the specific permissions assigned by File Explorer to the key
# !! (an additional DENY access-control entry for the current user, for the key itself only, for the 'Set Value' permission),
# !! using the .DeleteSubKey*Tree*() method fails (Remove-Item implicitly uses this method and therefore fails too)
# !! However, since there should be no nested subkeys, using .DeleteSubkey() should work fine.
$key.DeleteSubKey($keyName)
}
catch {
throw
}
finally {
if ($key) { $key.Close()}
}
}
# See if *Visual Studio Code* was most recently used to open a *.ps1 file:
# If so, it inexplicably OVERRIDES a file-type definition.
# (This doesn't seem to happen with other executables.)
# !! We fix the problem, but it will RESURFACE the next time File Explorer's shortcut menu
# !! is used to open a *.ps1 file in Visual Studio Code.
if ($itm = Get-Item -ErrorAction Ignore -LiteralPath 'registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\OpenWithList') {
if (($names = $itm.GetValueNames().Where({ $itm.GetValue($_) -ceq 'Code.exe' })) -and ($mruList = $itm.GetValue('MRUList')) -and $mruList[0] -in $names) {
Write-Warning "Visual Studio Code was most recently used to open a .ps1 file, which unexpectedly overrides the file-type definition.`nCorrecting the problem for now, but it will resurface the next time you use File Explorer's shortcut menu to open a .ps1 file in Visual Studio Code."
# Note: Normally there aren't, but there *can* be *multiple* Code.exe entries, namely after manual removal of the MRUList:
# The next time you choose to open in VSCode via File Explorer's shortcut menu, an *additional* Code.exe entry is added.
do { # Trim the start of the MRUList until its first entry no longer references Code.exe
$mruList = $mruList.Substring(1)
} while ($mruList[0] -in $names)
# Update the MRUList value in the registry.
$itm | Set-ItemProperty -Name 'MRUList' -Value $mruList
}
}
Explanation of the predefined Run in PowerShell shortcut-menu command:
It is defined in registry key HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\shell\0\Command (as of Windows 10) as follows:
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "-Command" "if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & '%1'"
This command is flawed in that it breaks with script-file paths that happen to contain ' characters.
Unless execution policy AllSigned is in effect - in which case only signed scripts can be executed but are executed without prompting - the command attempts to set the execution policy for the invoked process to Bypass, which means that any script can be executed, but only after the user responds to a confirmation prompt beforehand (irrespective of whether the script is signed or not, and whether it was downloaded from the web or not).
At least in earlier Windows 7 releases / PowerShell versions, the command was misdefined[1] in a way that effectively ignored the attempt to set the process' execution policy, which meant that whatever execution policy was persistently configured applied - and no confirmation prompt was shown.
Unless the targeted script explicitly pauses to wait for user input before exiting, the window in which the script will close automatically when the script finishes, so you may not get to see its output.
The targeted script executes in the directory in which it is located as the working directory (current location)
[1] The earlier, broken command definition was "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "-file" "%1" "-Command" "if((Get-ExecutionPolicy ) -ne AllSigned) { Set-ExecutionPolicy -Scope Process Bypass }", which meant what anything after -file "%1" was passed as arguments to file "%1" instead of the intended execution of the commands following -Command; additionally - a moot point - the AllSigned operand would have need to be quoted.
To execute a PS1 file by double-click (to run)
Make a shortcut for the file and set the target to this:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "C:\Temp\MyPowershellScript.ps1"
Replace the second directory (the one in quotes) with the location of your script.
To read a PS1 file by double-click (to edit)
Same as above, but target ISE instead, as that will force it into edit mode.
C:\Windows\System32\WindowsPowerShell\v1.0\powershell_ise.exe "C:\Temp\MyPowershellScript.ps1"
Server 2012 and newer by default do not associate the .PS1 file extension with the PowerShell executable; rather, they default to open .PS1 files with notepad by default for security reasons.
If you have access, you need to change the file association through the 'default programs' in your control panel for the .PS1 files to execute by double clicking.
Also be aware that you may have to change your execution policy to get particular scripts to run.
Also, as it sounds like this script might be a core automation, you can execute scripts from in another one with either of these, without the need to change the active working directory:
Invoke-Item ""
& ''
I have fixed the registry values so that the .ps1 scripts are executed with double click or with "Run with PowerShell" from any position without problem, even with paths with multiple consecutive spaces and with apostrophes:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.ps1]
#="Microsoft.PowerShellScript.1"
[HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell\command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit -Command \"Set-Location -LiteralPath \\\"%V\\\"\""
[HKEY_CLASSES_ROOT\Directory\Shell\Powershell\command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit -Command \"Set-Location -LiteralPath \\\"%V\\\"\""
[HKEY_CLASSES_ROOT\Drive\shell\Powershell\command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit -Command \"Set-Location -LiteralPath \\\"%V\\\"\""
[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\DefaultIcon]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\",0"
[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\Open\Command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -File \"%1\""
[HKEY_CLASSES_ROOT\SystemFileAssociations\.ps1\Shell\0\Command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -File \"%1\""
[HKEY_CLASSES_ROOT\SystemFileAssociations\.ps1\Shell\Edit\Command]
#="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell_ise.exe\" -File \"%1\""
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell]
"ExecutionPolicy"="RemoteSigned"

How to run powershell script on computer start-up

I am trying to run a PowerShell script Daily.ps1 on start-up, however, due to administrator settings (I cannot run as admin, that is not an option), I cannot run it through the Task Scheduler. For example, this is the contents of Daily.ps1:
if (1 -eq 1) {
"Hello there!"
}
So I tried to have a batch script Daily.cmd run on start up (through the start-up folder), which runs, but I cannot get it run the Daily.ps1, and I get a message saying running scripts is disabled. (Both files are in the same directory)
powershell C:\Users\Simon\Desktop\Daily.ps1
File C:\Users\Simon\Desktop\Daily.ps1 cannot be loaded because running scripts is disabled on this system
I then tried using this line of code from a trick I learned to bypass running scripts directly:
powershell cat Daily.ps1 | powershell invoke-expression
This works but only for one liners. So I added the -raw flag for
cat, which works when in powershell, but not in CMD. For some reason, Daily.ps1's text is still stored as an array of strings. (apologies for formatting)
cmdlet Invoke-Expression at command pipeline position 1
Supply values for the following parameters:
Command: if (1 -eq 1) {
invoke-expression : At line:1 char:14
if (1 -eq 1) {
Missing closing '}' in statement block or type definition.
At line:1 char:1
invoke-expression ~~~~~~~~~~~~~~~~~
So I tried to add this to Daily.cmd:
powershell
cat -raw Daily.ps1 | powershell-invoke-expression
However, the rest of the script doesn't get executed at all once I enter PowerShell.
I don't know to get Daily.ps1 to run through a batch command. Is there a way I missed, or is one of the ways I tried faulty (without admin rights)?
Edit: To clarify, ExecutionPolicy is set to Restricted, and that cannot be changed. Additionally, I can run PowerShell scripts fine through right clicking the file and running with PS.
Create a scheduled task to run at computer startup. Put powershell.exe in the field "program/script" and -File "C:\path\to\your.ps1" in the field "arguments" (you may want to avoid placing the script in a user profile). Set the task to run whether the user is logged on or not.
I found an answer!
After trying many different methods, I came across this line of code that allows you to run PS scripts if ExecutionProperty is set to restricted:
start powershell "cat -raw C:\Users\Simon\Desktop\Daily.ps1 | invoke-expression"
This runs powershell and uses the trick of piping the results of cat -raw [file.ps1] to invoke-expression. This is useful workaround if ExecutionProperty is set to restricted.
Then you can save this line to a .cmd or .bat file and use either Task Scheduler (more customizability) or put it in the startup folder.
P.S. for everyone who kept saying change the ExecutionProperty to something other than restricted. I clearly stated multiple times that I cannot do that(not admin), nor will the Sys Admin do that, nor will it ever happen(must stay restricted) :)

PowerShell Script in PostBuild

Continuous Integration
I have been working on a PowerShell script to keep our development process streamlined. I was planning on running it as a post-build event, but I'm having some trouble.
From the PowerShell prompt, the following works wonderfully:
PS C:\> ./example.ps1
However, when attempting to run this from cmd.exe as follows:
C:\> powershell -command "&\"C:\path to script\example.ps1\""
The script executes but I get a round of errors back from PowerShell, consisting mostly of path resolution errors from the resolve-path function:
Resolve-Path : Cannot find path 'C:\Documents and Settings\bdunbar\My Documents
\Visual Studio 2008\Projects\CgmFamilyComm\FamilyComm\iirf\cms\isapirewrite4.dl
l' because it does not exist.
At C:\Documents and Settings\bdunbar\My Documents\Visual Studio 2008\Projects\C
gmFamilyComm\scripts\cms.ps1:4 char:27
+ $iirfpath = (resolve-path <<<< ../iirf/cms/isapirewrite4.dll).path,
Resolve-Path : Cannot find path 'C:\Documents and Settings\bdunbar\My Documents
\Visual Studio 2008\Projects\CgmFamilyComm\FamilyComm\familycomm' because it do
es not exist.
At C:\Documents and Settings\bdunbar\My Documents\Visual Studio 2008\Projects\C
gmFamilyComm\scripts\cms.ps1:5 char:27
+ $vdirpath = (resolve-path <<<< ../familycomm).path
Is there a way to work around this? Could it be an issue with running resolve-path under cmd.exe?
[Update]
I've been able to change things to get around the errors that are occurring, but I still receive errors that work perfectly fine from the powershell command prompt. I can't figure out what the difference is.
I've made this work in the past (see http://sharepointpdficon.codeplex.com/SourceControl/changeset/view/13092#300544 if interested):
C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe -NoLogo
-NonInteractive -Command .'$(ProjectDir)Deployment\PostBuildScript.ps1'
-ProjectDir:'$(ProjectDir)' -ConfigurationName:'$(ConfigurationName)' -TargetDir:'$(TargetDir)' -TargetFileName:'$(TargetFileName)' -TargetName:'$(TargetName)
Then throw these parameters in the first line of your post-build script (if you think you may be able to use them):
param($ProjectDir, $ConfigurationName, $TargetDir, $TargetFileName)
Also I should point out, I am not using this presently. I did like using it as a quick scratchpad to reload test data for running integration tests.
Looks like your problem is how relative paths are resolved. Relative paths are resolved based on the current location (stored in $pwd) and not based on the location of the script. So if you launched the script from C:\, it definitely would not work.
I would suggest you calculate the paths based on an argument (like Peter Seale shows), or grab the actual location of the script from:
$MyInvocation.MyCommand.Path