It seems like the command history of PowerShell is stored separately from PowerShell ISE.
But I have one question is that where is the commands history of PowerShell ISE stored at and how do I view them?
As for the location of history files; they are located here:
Get-ChildItem -Path "$env:USERPROFILE\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine" |
Format-Table -AutoSize
# Results
<#
Directory: C:\Users\YourUserName\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 17-May-21 02:23 258925 ConsoleHost_history.txt
-a---- 11-May-21 01:20 120222 Visual Studio Code Host_history.txt
-a---- 27-Jun-20 18:58 0 Windows PowerShell ISE Host_history.txt
#>
As noted, in the ISE, you can just leverage the Start-Transcript cmdlet to capture all you do, but again, it is not an out-of-box/console/ISE immediate lookup of previous commands to execute like PSReadLine. Yet, you can do that, sort of, with some creativity. Yet, that's just a kludge.
A note about using Start-Transcript. It will default to your "$env:USERPROFILE\Documents" folder.
So, I'd recommend you set to go to a specific folder. Also, though the files can be small, there will be tons of them over time, thus, you need to manage that.
They of course can just be opened in the ISE directly:
psEdit -filenames ((Get-ChildItem -Path "$env:USERPROFILE\Documents" | Sort-Object -Property LastWriteTime -Descending)[0]).FullName
Yet, since you are already in the ISE, you can just type and run all commands from the editor pane, and as needed, select anyone and just run it again.
Yet if you are just using the ISE console, and thinking it is the same as the PowerShell consolehost, then that's wrong. It's really an output window with some console skills. Back in the early ISE days, there were 3 panes. Editor, output window and true console.
The true console got removed in later ISE versions. Why, who knows? VSCode almost got us back there.
If you want to do the console stuff, then use the PowerShell console, or shell to the PowerShell console using runspaces to stay in the ISE.
For the runspaces thing, here is an example to run PowerShell Core (v6+), while still in the ISE.
Using PowerShell 7 in the Windows PowerShell ISE
https://old.ironmansoftware.com/using-powershell-core-6-and-7-in-the-windows-powershell-ise
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Clear()
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Switch to PowerShell 7", {
function New-OutOfProcRunspace {
param($ProcessId)
$ci = New-Object -TypeName System.Management.Automation.Runspaces.NamedPipeConnectionInfo -ArgumentList #($ProcessId)
$tt = [System.Management.Automation.Runspaces.TypeTable]::LoadDefaultTypeFiles()
$Runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($ci, $Host, $tt)
$Runspace.Open()
$Runspace
}
$PowerShell = Start-Process PWSH -ArgumentList #("-NoExit") -PassThru -WindowStyle Hidden
$Runspace = New-OutOfProcRunspace -ProcessId $PowerShell.Id
$Host.PushRunspace($Runspace)
}, "ALT+F5") | Out-Null
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Switch to Windows PowerShell", {
$Host.PopRunspace()
$Child = Get-CimInstance -ClassName win32_process | where {$_.ParentProcessId -eq $Pid}
$Child | ForEach-Object { Stop-Process -Id $_.ProcessId }
}, "ALT+F6") | Out-Null
So, with a bit of tweaking, you can do the same for Windows PowerShell 5 to get the raw console
But again,m avoid all the hoops, and use VSCode, if you are allowed. Yet on your servers, we all know ISE is still a thing. ;-}
Related
I am using the following code to uninstall google chrome software from my remote machines, But this script is executed with no output. And when i check the control panel, still the google chrome program exists. Can someone check this code?
foreach($computer in (Get-Content \path\to\the\file))
{
$temp1 = Get-WmiObject -Class Win32_Product -ComputerName $computer | where { $_.name -eq "Google Chrome"}
$temp1.Uninstall()
}
You shouldn't use the Win32_Product WMI class, one of the side effects of enumeration operations is it checks the integrity of each installed program and performs a repair installation if the integrity check fails.
It is safer to query the registry for this information instead, which also happens to contain the uninstall string for removing the product with msiexec. The uninstall string here will be formatted like MsiExec.exe /X{PRODUCT_CODE_GUID}, with PRODUCT_CODE_GUID replaced with the actual product code for that software.
Note: This approach will only work for products installed with an MSI installer (or setup executables which extract and install MSIs). For pure executable installers which make no use of MSI installation, you'll need to consult the product documentation for how to uninstall that software, and find another method (such as a well-known installation location) of identifying whether that software is installed or not.
Note 2: I'm not sure when this changed but ChromeSetup.exe no longer wraps an MSI as it used to. I have modified the code below to handle the removal of both the MSI-installed and EXE-installed versions of Chrome.
# We need to check both 32 and 64 bit registry paths
$regPaths =
"HKLM:\SOFTWARE\Wow6432node\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
# Since technically you could have 32 and 64 bit versions of the same
# software, force $uninstallStrings to be an array to cover that case
# if this is reused elsewhere. Chrome usually should only have one or the
# other, however.
$productCodes = #( $regPaths | Foreach-Object {
Get-ItemProperty "${_}\*" | Where-Object {
$_.DisplayName -eq 'Google Chrome'
}
} ).PSPath
# Run the uninstall string (formatted like
$productCodes | ForEach-Object {
$keyName = ( Get-ItemProperty $_ ).PSChildName
# GUID check (if the previous key was not a product code we'll need a different removal strategy)
if ( $keyName -match '^{[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}}$' ) {
# Use Start-Process with -Wait to wait for PowerShell to finish
# /qn suppresses the prompts for automation
$p = Start-Process -Wait msiexec -ArgumentList "/l*v ""$($pwd.FullName)/chromeuninst.log"" /X${keyName} /qn" -PassThru
# 0 means success, but 1638 means uninstallation is pending a reboot to complete
# This should still be considered a successful execution
$acceptableExitCodes = 0, 1638
}
else {
Write-Host "Stopping all running instances of chrome.exe, if any are running"
Get-Process chrome.exe -EA Ignore | Stop-Process -Force
# The registry key still has an uninstall string
# But cannot be silently removed
# So we will have to get creating and control the uninstall window with PowerShell
# We need to use the undocumented --force-uninstall parameter, added to below command
$uninstallString = "$(( Get-ItemProperty $_).UninstallString )) --force-uninstall"
# Break up the string into the executable and arguments so we can wait on it properly with Start-Process
$firstQuoteIdx = $uninstallString.IndexOf('"')
$secondQuoteIdx = $uninstallString.IndexOf('"', $firstQuoteIdx + 1)
$setupExe = $uninstallString[$firstQuoteIdx..$secondQuoteIdx] -join ''
$setupArgs = $uninstallString[( $secondQuoteIdx + 1 )..$uninstallString.Length] -join ''
Write-Host "Uninstallation command: ${setupExe} ${setupArgs}"
$p = Start-Process -Wait -FilePath $setupExe -ArgumentList $setupArgs -PassThru
# My testing shows this exits on exit code 19 for success. However, this is undocumented
# behavior so you may need to tweak the list of acceptable exit codes or remove this check
# entirely.
#
$acceptableExitCodes = 0, 19
}
if ( $p.ExitCode -notin $acceptableExitCodes ) {
Write-Error "Program exited with $($p.ExitCode)"
$p.Dispose()
exit $p.ExitCode
}
exit 0
}
Incidentally, if you already know the MSI ProductCode of a given program you don't have to find the uninstall string this way. You can simply execute msiexec /X{PRODUCT_CODE_GUID}.
If you have further problems which aren't caused by the syntax of the above this would be an operational issue and would be better troubleshot over at the https://superuser.com site.
Edit
As discovered via our chat conversation, you are installing per user and with the 79.0.3945.130 version. You can remove per user Chrome with the following command if installed per user (if the version is different you will need the correct version path):
&"C:\Users\username\AppData\Local\Google\Chrome\Application\79.0.3945.130\Installer\setup.exe" --uninstall --channel=stable --verbose-logging --force-uninstall
In the future, it is not recommended to use ChromeSetup.exe or ChromeStandaloneSetup64.exe to install and manage Chrome in an enterprise environment, you should instead use the Enterprise MSI and install system-wide so you can manage Chrome more efficiently. This is the supported way to deploy Chrome in an enterprise environment, and the script I provided will work to uninstall Chrome via msiexec and searching the registry for the {PRODUCT_CODE} as provided.
Assuming it's an msi install and remote powershell is enabled:
invoke-command -computername comp001 { uninstall-package 'google chrome' }
For the programs provider (all users), it's something like:
get-package *chrome* | % { $_.metadata['uninstallstring'] }
"C:\Program Files\Google\Chrome\Application\95.0.4638.54\Installer\setup.exe" --uninstall --channel=stable --system-level --verbose-logging
And then run that uninstallstring, but you'd have to figure out the silent uninstall option (--force-uninstall). It also runs in the background.
I have a PowerShell script which has the following steps:
Opens another PowerShell window
Navigates to an angular projects directory
Runs a command to serve the project
Is there a way that I can close all other running PowerShell windows, but keep the currently running script and it's newly created window open? Following the changes, I would like it to behave like this:
Close all other PowerShell windows
Opens another PowerShell window
Navigates to an angular projects directory
Runs a command to serve the project
You could use Get-Process to enumerate all running powershell processes, then filter out the current one by comparing with the value of $PID, before piping the rest to Stop-Process:
Get-Process powershell |? Id -ne $PID |Stop-Process -Force
You can include the child process by ID if necessary:
$childProcess = Start-Process powershell $childProcArgs -PassThru
Get-Process powershell |? Id -notin #($PID;$childProcess.Id) |Stop-Process -Force
... although I would suggest simply killing all other powershell instances first, and then launch the child process after
Is there a way that I can close all other running PowerShell windows,
but keep the currently running script and it's newly created window
open?
$Previous = Get-process -Name *Powershell*;
{YOUR SCRIPT}
Stop-process $previous
I have an update script for running the Dell Command Update tool. In short dcu-cli.exe. The thing now is than when i run the same script code on the computer local then everything runs OK but when i run the exact same code in a script with invoke-command(and yes i have full admin rights) than the exitcode is 2 meaning An unknown application error has occurred instead of 0 (everything OK)
It is a very large script so i created a new one to debug this. This is the shorted code:
Invoke-Command -ComputerName "MyComputer" -ScriptBlock {
$ExitCode = 0
#Declare path and arguments
$DcuCliPath = 'C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe'
$DellCommand = "/applyUpdates -autoSuspendBitLocker=enable -outputLog=C:\Dell_Update.log"
#Verify Dell Command | Update exists
If (Test-Path -Path $DcuCliPath) {
$objWMI = Get-WmiObject Win32_ComputerSystem
Write-Host ("Dell Model [{0}]" -f $objWMI.Model.Trim())
$serviceName = "DellClientManagementService"
Write-Host ("Service [{0}] is currently [{1}]" -f $serviceName, (Get-Service $serviceName).Status)
If ((Get-Service $serviceName).Status -eq 'Stopped') {
Start-Service $serviceName
Write-Host "Service [$serviceName] started"
}
#Update the system with the latest drivers
Write-Host "Starting Dell Command | Update tool with arguments [$DellCommand] dcu-cli found at [$DcuCliPath]"
$ExitCode = (Start-Process -FilePath ($DcuCliPath) -ArgumentList ($DellCommand) -PassThru -Wait).ExitCode
Write-Host ("Dell Command | Update tool finished with ExitCode: [$ExitCode] current Win32 ExitCode: [$LastExitCode] Check log for more information: C:\Dell_Update.log")
}
}
When i remove the Invoke-Command -ComputerName "MyComputer" -ScriptBlock { and then copy + run the script local on the PC then the exitcode = 0
What i also noticed than when i run the command via 'Invoke-Command' then there is also no log file created as i passed along in the arguments... So my best guess is something is going wrong with local an remote paths?
So what am i missing? I'm guessing it is something simple but i spend several hours to get this running without any luck...
Try running it this way. You should be able to see any output or error messages. I typically add to the path first rather than using & or start-process.
invoke-command mycomputer {
$env:path += ';C:\Program Files (x86)\Dell\CommandUpdate';
dcu-cli /applyUpdates -autoSuspendBitLocker=enable -outputLog=C:\Dell_Update.log }
Using start-process inside invoke-command seems pretty challenging. I can't even see the output of findstr unless I save it to a file. And if I didn't wait the output would be truncated. By default start-process runs in the background and in another window. There's a -nonewwindow option too but it doesn't help with invoke-command.
invoke-command localhost { # elevated
start-process 'findstr' '/i word c:\users\joe\file1' -wait -RedirectStandardOutput c:\users\joe\out }
#js2010, thanks for your additional help. Unfortunately this didn't helped either.
So i did some more debugging and it turns out it was a bug in the dcu-cli version running on my test machine, DOH...!!
On the test machine version 3.1.1 was running and on another machine version 4.0 was running and that worked fine via remote Powershell. So i looked for the release notes, which i found here: https://www.dell.com/support/kbdoc/000177325/dell-command-update
And as you can see in version 3.1.3 there was this fix:
A problem was solved where dcu-cli.exe was not executed in an external interactive session of PowerShell.
I'm experiencing with weird case with my Powershell script.
I have written a script that's execute an .exe file
this exe runtime is about 3 hours but constantly crashing after 2 hors (1-2 minutes more or less)
I have break my head try to figure out why the process crashing
eventually I found that the .exe crashing because the powershell crashing.
Here is the process execution command:
$Proc = Start-Process -FilePath $ExePath -ArgumentList $Arguments -NoNewWindow -PassThru
$Proc | Wait-Process -Timeout 28800 -ea 0 -ev timeouted
After I realized this issue cased by the Powershell I have enabled windows powershell logging and find an error message "the pipeline has been stopped"
The script need perform more actions after the process ends and get its exit code, that's why I used the -PassThru flag.
I have tried to run it without using the PassThru flag or the Process-Wait command, the result stayed the same (the process crashed after 2 hours but there wasn't log with the message "The pipeline has been stopped")
Important points:
the .exe file is soured with try;catch blocks with logger but did not logged any thing when crashing- this is not a runtime error in the .exe file
When running the .exe independently from the command line its finish successfully after ~3 hours
The Powershell script run with Administrator privileges
The exe is not casing the crashing due to high CPU/Memory/Disk usage
I will follow up once I will have more updates.
Thanks for all the helpers.
Your help is much appreciated!
In my opinion the Start-Process cmdlet is good for quick things. But, it leaves a lot to be desired when trying to debug why an exe isn't behaving.
To work around this in your case it might be useful to use .Net objects to redirect and change certain things about your instantiation. I put an example function below that I've used when having trouble debugging exe runs.
function Start-Exe
{
param
( [string]$exePath, [string]$args1 )
$returnVal = $false
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $exePath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $args1
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$exitCode = $p.ExitCode
#OutputFile can be some log file, or just use write-host to pump to console
#$stdout | Add-Content $global:OutputFile
#$stderr | Add-Content $global:OutputFile
return $exitCode
}
You can try it without using Wait-Process and see if it is reproducible.
Start-Process with -Wait parameter .
or Create it in a Job without -Wait and wait for the Job using Wait-.Job cmdlet
I had the same symptom when trying to execute an 8 hour process, it would always die after 2 hours, you need to clear the powershell IdleTimeout.
This answer helped me
I have a functions that only works on some scensarios.
It works on Powershell ISE, but when I save the same thing in a .Ps1 file and run it, it doest not work.
I have the function as part of a big script. It works and open the Window when I run it on Windows 7 but doest not run on Windows Server 2008 R2.
Why?
The script with the function and its calling is:
Function Get-SaveFileTxt($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
Out-Null
$SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
$SaveFileDialog.initialDirectory = $initialDirectory
$SaveFileDialog.AddExtension = $true
$SaveFileDialog.DefaultExt = "txt"
$SaveFileDialog.filter = "Text Files (*.txt)| *.txt"
$SaveFileDialog.ShowDialog() | Out-Null
$SaveFileDialog.filename
}
Get-SaveFileTxt
Not sure if this is the case here but I remember a bug that the opened dialog doesn't take focus and appear behind other opened windows. Can you confirm?
UPDATE:
Set the ShowHelp property to $true.
$SaveFileDialog.ShowHelp = $true
It works in the ISE because ISE's apartment state is STA by default and your powershell mode is MTA, you can check it with:
[System.Threading.Thread]::CurrentThread.ApartmentState
Your code will work if you open powershell in STA mode:
powershell.exe -STA