My powershell profile has a custom powershell prompt that unfortunately causes $lastexitcode values to be lost. For instance, given a powershell script "fail.ps1" with contents "exit 123", when I run the script, $? is $false while $lastexitcode is 0. If I instead run powershell without loading my profile with the custom prompt, after running fail.ps1 then $lastexitcode is 123.
Has anyone seen this problem before? Is there a way to preserve $lastexitcode as the prompt is generated?
I ran into this when using Posh-git, https://github.com/dahlbyk/posh-git, a nice powershell prompt for git.
Issue can be resolved by capturing $LASTEXITCODE at the start of the prompt and restoring it at the end:
function prompt {
$realLASTEXITCODE = $LASTEXITCODE
# ...
$LASTEXITCODE = $realLASTEXITCODE
}
You need to do this to make it work:
function prompt {
$realLASTEXITCODE = $global:LASTEXITCODE
# ...
$global:LASTEXITCODE = $realLASTEXITCODE
# cleanup
Remove-Variable realLASTEXITCODE
}
Related
At the beginning of my script, I add a piece of code to make sure the shell run with full admin privileges:
# Running with full admin privileges
param([switch]$Elevated)
function Test-Admin {
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Test-Admin) -eq $false) {
if ($elevated) {
# tried to elevate, did not work, aborting
} else {
Start-Process pwsh -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
}
exit
}
# Other stuff below...
After that is a normal script (the Other stuff below section). The problem is, I want to automatically close the shell window after the script is done. Before I add the full privilege code, putting exit in the bottom of the script works fine, but not now anymore. I don't understand why, because everything else in this section works fine.
Normally scripts auto exit at the end.
Try to omit -noexit parameter from argumentlist and delete exit at the bottom of the script.
For me, there is no sense to call a script with -noexit and put exit at the end of it. Without these all will automagically work.
If you want to examine what is going on during script writing, you can put eg. a timeout /t 90 command at the end, so you get 90 seconds to examine output before it auto closes.
I have a powershell Script that runs fine in the ISE, but when I run it directly from the .ps1 file it stops at this point:
Write-Host "Attempting to start FFMPEG Process with arguments:$ArgumentList::::" -ForegroundColor Green
Start-Process $SCRIPT:FFMPEGLocation $ArgumentList -Wait
I get the Write-Host printout, with the correct Argument List, but no window pops up for the start-process or anything. I made sure to use the SCRIPT scope variable for anything that is outside the function. It runs fine in ISE just not when I run it in Console. No errors either, and the $SCRIPT:FFMPEGLocation variable is a direct path to the exe to be executed.
Any help would be greatly appreciated, and if you need more let me know.
PSVersion 5.1.14393.1066
OSVersion 10.0.14393
As per comments, check the value passed to Start-Process to ensure it contains what you expect as the issue is likely to be with $SCRIPT:FFMPEGLocation.
The difference between the ISE and Console with regard to scope can be frustrating.
See this SuperUser post which explains the behaviour further
. You may also find this post helpful;
BartekB explains that F5 in the ISE actually dot-sources the script instead of calling it, and provides a function to run a clean session.
An alternative method which will also wait on the process that needs to be run:
& $EXE /arg 1 /arg 2 -etc 2>&1 | % { $_ }
This will execute the command and pass the args, redirect any errors to the output stream and then print that output (for some reason, I've had bad luck with actually getting output from commands)
All , I am trying to execute a external power shell script file in the MSBuild. But every time when PS run the cmdlet Read-Host. The MSBuild seems stop. and doesn't prompt me to input . I don't know what happen to it . Seems the console is in deadlock..thanks.
The testloop.ps1 code is shown below.
$ErrorActionPreference = 'Stop'
$error.clear()
function GetAzureSubScription()
{
read-host "please input something :"
}
write-host "Get into script"
GetAzureSubScription
The MSBuild code is below (wrapped for clarity):
<Exec WorkingDirectory="$(MSBuildProjectDirectory)"
Command="$(windir)\system32\WindowsPowerShell\v1.0\powershell.exe -f
E:\Work\AutoDeploy\testloop.ps1" />
So yes, the console (just a minor point - powershell.exe does not run under cmd.exe - they are separate processes, but they both use a console window) window is hidden so it will appear to freeze when prompting for input. The simplest option here is to override the read-host function with a version that will prompt using a graphical window. Add the start of your script, add the following function:
# override the built in prompting, just for this script
function read-host($prompt) {
$x = 0; $y = 0;
add-type -assemblyname microsoft.visualbasic
[Microsoft.VisualBasic.Interaction]::InputBox($prompt,
"Interactive", "(default value)", $x, $y)
}
Now your script will be able to prompt for values. Also, you should run powershell.exe with the -noninteractive argument to catch any other places where you are accidentally calling interactive host functions. It will not stop the above function from working though.
The MSBuild Exec tasks starts cmd.exe and let that execute the command. MSbuild has to channel the writes to the console through since the cmd.exe window itself is invisible. It seems the writes do get through, but the reads do not. You can see the same effect if instead of calling powershell you exec a command like "del c:\temp\somefile.txt /p" which asks for confirmation. Although that way it doesn't block, but there is also no way of giving an answer.
That it does not handle reads properly is not that strange. It is a build script, so it should just build and not ask questions. My advice is to have the MSBuild script run without asking questions. If you really need to ask questions, then ask them before calling MSBuild.
We use PowerShell for some of our automated build scripts. Unfortunately, by default, PowerShell continues after an error.
Ordinarily, I can change this behaviour by setting $ErrorActionPreference = Stop.
I can't see a corresponding command line switch for PowerShell.exe, and we (deliberately) run the commands with -noprofile, so I can't put it in there.
How do I do this for a build script?
Put it at the top of the script you're running?
$ErrorActionPreference = 'Stop'
Alternatively, you can also get similar control at the cmdlet level using the ErrorAction parameter.
There doesn't seem to be a way to set:
powershell -erroractionpreference stop ...
The following would work:
powershell -command { $ErrorActionPreference = "stop"; .\test.ps1 } -noprofile
There is of course nothing to stop the script (re)setting ErrorActionPreference.
I have a lot of scripts that are running as scheduled tasks. So they do a $host.setshouldexit(1) on any failure, which shows up in the task scheduler as the return code.
I also want to be able to run these scripts interactively while debugging and testing. So the $host.setshouldexit() kills my powershell or ISE session.
My question is: how can I detect if a script is running non-interactively? If it is, then I'll use setshouldexit, otherwise it will print the error code or something nondestructive. (Note that I don't want to use [environment]::userinteractive because these scripts are not always running in what the OS thinks is a non-interactive session.)
There is a -noninteractive switch that I'm using for the scheduled tasks. Is there some way I can query that from powershell?
The $Host.SetShouldExit method should not be necessary, and is actually inconsistent, depending on how you are calling your scripts. Using the keyword exit should get you your exit status.
Using powershell -F script.ps1:
exit - works
SetShouldExit - ignored
Using powershell -c '.\script.ps1':
exit - status reduced to 0 or 1, for success or failure of the script, respectively.
SetShouldExit - exits with correct status, but remaining lines in script are still run.
Using powershell -c '.\script.ps1; exit $LASTEXITCODE' [1]:
exit - works
SetShouldExit - exits with status == 0, and remaining lines in script are still run.
Calling directly from powershell (> .\script.ps1):
exit - works
SetShouldExit - terminates calling powershell host with given exit status
Why not just have it take a parameter "testing" which sets the right behavior during your tests? You have a history buffer so it will be hardly any more typing to run.
I had the same issue. The following works for me:
# Exit with Return Code when NOT using PowerShell ISE
if ($psise -eq $Null)
{
$host.SetShouldExit(1)
}
Upon finding your question I have taken the issue a bit further and found $MyInvocation.MyCommand.Name. This is False in both the command interpreter and the ISE command interpreter. And when a script (mine is jest.ps1) containing just the line: Write-Host $MyInvocation.MyCommand.Name is run from cmd.exe call to powershell.exe as:
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy RemoteSigned -NoExit -NonInteractive -NoProfile -File "M:\WindowsPowerShell\Jest.ps1"
Output is simply:
Jest.ps1
Check $Host.Name. If your script is running outside of an IDE, it will return a value of ConsoleHost. Otherwise it will return a reference to the IDE such as Windows PowerShell ISE Host or PowerGUIScriptEditorHost.
I have to use SetShouldExit because the scheduler that runs my scripts usually ignores other methods of indicating a failure. I add a function to allow SetShouldExit when $Host.Name is ConsoleHost. It saves a lot of IDE crashes during testing.
This directly answers the question poster had about whether or not you can query if the powershell console was launched with -NonInteractive switch.
Function Test-IsPowerShellConsoleNonInteractive { [Boolean]([Environment]::GetCommandLineArgs() -Match '-NonInteractive') }
$IsPSConsoleNonInteractive = Test-IsPowerShellConsoleNonInteractive
If ($IsPSConsoleNonInteractive) { $Host.SetShouldExit(2) }