Pass / Pipe / Loop All processes from Get-Process to Powershell PoshInternals script - windows

How would I pass all processes with Get-Process matching a certain process name to another PowerShell script one by one ?
pseudocode
for each process in matchingprocesses:
myScript(process)
Planned usage Set-WorkingSetToMin script: https://www.powershellgallery.com/packages/PoshInternals/1.0/Content/Set-WorkingSetToMin.ps1
This works great as there is only one notepad++ process:
get-process notepad++ | Set-WorkingSetToMin
However for VS Code this only only gets the first code process and ignores the rest:
get-process code | Set-WorkingSetToMin
How do I pipe each process matching a certain name to powershell script ?
Alternative would be to modify PoshInternals script to accept multiple processes:
# Dont run Set-WorkingSet on sqlservr.exe, store.exe and similar processes
# Todo: Check process name and filter
# Example - get-process notepad | Set-WorkingSetToMin
Function Set-WorkingSetToMin {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True, Mandatory=$true)]
[System.Diagnostics.Process] $Process
)
if ($Process -ne $Null)
{
$handle = $Process.Handle
$from = ($process.WorkingSet/1MB)
$to = [PoshInternals.Kernel32]::SetProcessWorkingSetSize($handle,-1,-1) | Out-Null
Write-Output "Trimming Working Set Values from: $from"
} #End of If
} # End of Function
ANSWER in one line without extra variables:
foreach ($process in Get-Process myprocessname) { Set-WorkingSetToMin ($process) }

You could add a where clause to pull out just the processes you want, then pipe that set to Set-WorkingSetToMin. An example is below, but adjust as needed to pull exactly what you're looking for.
get-process | where {$_.ProcessName -like "*code*"} | Set-WorkingSetToMin
Update:
I see what you're saying, the issue is not with the set of processes being sent, but how they're handled once they're there. To get around that, you could set a variable equal to the process set, then loop through them, calling the cmdlet each time. Something like this:
$processes = get-process | where {$_.ProcessName -like "code"} | Set-WorkingSetToMin
foreach ($process in $processes)
{
Set-WorkingSetToMin ($process)
}
ANSWER thanks to help from Landon: foreach ($process in Get-Process myprocessname) { Set-WorkingSetToMin ($process) }

Related

Fetching value of processId from list of process Ids

I am running the below cmdlet to get the process ids used by a file:
Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq $lockedFile){$processVar.Name + " PID:" + $processVar.id}}}
The above cmdlet is generating the output process1 PID:5260
Now, I need to pipe the cmdlet so as to kill the above process id for which I have written below cmdlet:
Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq $lockedFile){$processVar.Name + " PID:" + $processVar.id}}} | Stop-Process $processVar.id
However, it is not stopping the process.
I basically want to print out the process name and process id and then kill the process.
The process name and process id are already printing out correctly but need help to pipe the process id into the cmdlet and then kill the process.
Maybe you could do it this way, I believe outputting an object to the console would be more readable. The only change you need to do is move the Stop-Process inside the if condition.
$lockedFile = 'defineThisVariable'
Get-Process | ForEach-Object {
if($_.Modules.FileName.where({$_ -eq $lockedFile})) {
# This is the ouput object to the console
[pscustomobject]#{
Process = $_.Name
PID = $_.id
}
# Here you stop the Process, you can add
# `-Verbose` for more output to console
Stop-Process -Id $_.Id
}
}

Powershell: How do I continue running script after never-ending script line?

I have a line in my script that downloads a large video file. After the download starts I want to already open the file while is it downloading. The problem is, is that the download command hasn't finished yet so the script stays stuck on the same line.
(Download-File command)
$allFiles = Get-ChildItem | Select-Object -Property name,LastAccessTime | measure-object -Property LastAccessTime -Maximum
$videoFile = Get-ChildItem | Where-Object { $_.LastAccessTime -eq $allFiles.Maximum}
Start-Process $videoFile
(I want this to run in a loop while the download-file command is running)
That should be easy. All you need to do is make it run on a different thread. Use background jobs or Runspaces. Below example is Background Job.
$ScriptBlock = {(Download-File command)}
Start-Job -ScriptBlock $ScriptBlock
do
{
$allFiles = Get-ChildItem | Select-Object -Property name,LastAccessTime | measure-object -Property LastAccessTime -Maximum
$videoFile = Get-ChildItem | Where-Object { $_.LastAccessTime -eq $allFiles.Maximum}
Start-Process $videoFile
}
while (1 -gt 0)
Although, I am not sure if you would want to open the video file in a loop. If it does support opening an incomplete video file, you will have just as many instances of it. Better enclose it in an if (!(Get-Process -name $VideoFile)){} loop to prevent that.
Use the .waitforexit method.
Example:
$proc = Start-Process cmd.exe -PassThru
$proc.WaitForExit()
After the $proc.WaitForExit() line you can open your file.
Its look much better than a loop

Powershell GUI script combobox usage

I'm programming a Powershell GUI script that shows a list of all the processes names running on the system using a combobox.
I would like to show process name followed by the proccess ID in parenthesis.
i.e.
Internet explorer (4)
Chrome (100)
Skype (33)
This is the code I've got. So far I can just show the process name, I don't know how to show the process id as well, any advise?
$MainForm_Load={
#TODO: Initialize Form Controls here
$processes = Get-Process
foreach ($process in $processes)
{
Load-ComboBox $combobox1 $process.ProcessName -Append
}
}
You can use Format String (-f)
$MainForm_Load={
#TODO: Initialize Form Controls here
$processes = Get-Process
foreach ($process in $processes)
{
$CurrentProcess = "{0} ({1})" -f $process.Name,$process.id
Load-ComboBox $combobox1 $CurrentProcess -Append
}
Regarding your Comment Question,
first it should be: get-process $combobox1.selecteditem.text but it still not going to work because for example the name "Chrome (100)" is not a valid process name, so you need to split it first to remove the process id (100),
you can try this
$ProcessName = ($combobox1.selecteditem.text) -replace "\(.*\)"
then use
$textbox1.text = (get-process $ProcessName) | Out-String
Simple solution:
get-process | %{$combobox.items.add("$($_.name)($($_.id))")}

Make a Powershell function/task run in the background

I have a function that lets me write the file-path of files to a text file, depending on your input. That sounds confusing, but I don't know of a better way to put it, so here's the function:
Function writeAllPaths([string]$fromFolder,[string]$filter,[string]$printfile) {
Get-ChildItem -Path $fromFolder -Recurse $filter | Select-Object -Property FullName > $printfile
}
First argument being the folder you start your search from.
Second argument, the filter. *.zip for instance, will list all zip files.
Third argument, you have to provide where the text file will end up.
Sample usage: writeAllPaths c:\ *.zip c:\allZips.txt
The thing is, when I do this, Powershell won't accept commands until it's done. Which isn't very productive. Is there a way to have this run in the background when started. And preferably, give a little message when it's done. I could be opening any file that's being created in the middle of the process...
Also, I'm on Windows 7, so I'm guessing that I have Powershell 2.0
Yeah, I'm not sure about it :p
EDIT:
I used Start-Job, as suggested, as follows:
Function writeAllPaths([string]$fromFolder,[string]$filter,[string]$printfile) {
Start-Job -ScriptBlock {Get-ChildItem -Path $fromFolder -Recurse $filter | Select-Object -Property FullName > $printfile}
}
However, the file isn't created. The old function does create a file.
EDIT2: it would be preferable to have this function in my Powershell profile. This way, I can execute it whenever I want, instead of having to load in the specific ps1 file every time I boot Powershell.
More info on the Powershell profile can be found here
You can summon your own profile by typing: notepad $profile
In the new scope you create for the background job, your parameters defined for you WriteAllPaths function are not defined. Try this and you'll see they aren't:
Function writeAllPaths([string]$fromFolder,[string]$filter,[string]$printfile)
{
Start-Job { "$fromFolder, $filter, $printFile" }
}
$job = WriteAllPaths .\Downloads *.zip zips.txt
Wait-Job $job
Receive-Job $job
, ,
Try this instead:
Function writeAllPaths([string]$fromFolder, [string]$filter, [string]$printfile)
{
Start-Job {param($fromFolder,$filter,$printfile)
"$fromFolder, $filter, $printfile" } `
-ArgumentList $fromFolder,$filter,$printfile
}
$job = WriteAllPaths .\Downloads *.zip z.txt
Wait-Job $job
Receive-Job $job
.\Downloads, *.zip, z.txt
Now you see your output because we passed the parameters through to the scriptblock via -ArgumentList. What I would recommend is a function that can optionally use a background job. Just stick this function definition in your profile and you're set:
function WriteAllPaths([string]$FromFolder, [string]$Filter,
[string]$Printfile, [switch]$AsJob)
{
if (![IO.Path]::IsPathRooted($FromFolder)) {
$FromFolder = Join-Path $Pwd $FromFolder
}
if (![IO.Path]::IsPathRooted($PrintFile)) {
$PrintFile = Join-Path $Pwd $PrintFile
}
$sb = {
param($FromFolder, $Filter, $Printfile)
Get-ChildItem $FromFolder -r $filter | Select FullName > $PrintFile
}
if ($AsJob) {
Start-Job $sb -ArgumentList $FromFolder,$Filter,$PrintFile
}
else {
& $sb $FromFolder $Filter $PrintFile
}
}
Test the function (synchronously) like so:
$job = WriteAllPaths Downloads *.zip z.txt -AsJob
Wait-Job $job
Receive-Job $job
Note that I'm testing if the path is root and if not I'm prepending the current dir. I do this because the background job's starting dir is not always the same as where you executed Start-Job from.
Which version of powershell? In Powershell 2.0 I think you can use background jobs for this Start-Job.
Starts a Windows PowerShell background job.

PowerShell equivalent of LINQ Any()?

I would like to find all directories at the top level from the location of the script that are stored in subversion.
In C# it would be something like this
Directory.GetDirectories(".")
.Where(d=>Directories.GetDirectories(d)
.Any(x => x == "_svn" || ".svn"));
I'm having a bit of difficulty finding the equivalent of "Any()" in PowerShell, and I don't want to go through the awkwardness of calling the extension method.
So far I've got this:
Get-ChildItem | ? {$_.PsIsContainer} | Get-ChildItem -force | ? {$_.PsIsContainer -and $_.Name -eq "_svn" -or $_.Name -eq ".svn"
This finds me the svn directories themselves, but not their parent directories - which is what I want. Bonus points if you can tell me why adding
| Select-Object {$_.Directory}
to the end of that command list simply displays a sequence of blank lines.
To answer the immediate question with a PowerShell v3+ solution:
(Get-ChildItem -Force -Directory -Recurse -Depth 2 -Include '_svn', '.svn').Parent.FullName
-Directory limits the matches to directories, -Recurse -Depth 2 recurses up to three levels (children, grandchildren, and great-grandchildren), Include allows specifying multiple (filename-component) filters, and .Parent.FullName returns the full path of the parent dirs. of the matching dirs., using member-access enumeration (implicitly accessing a collection's elements' properties).
As for the bonus question: select-object {$_.Directory} does not work,
because the \[System.IO.DirectoryInfo\] instances returned by Get-ChildItem have no .Directory property, only a .Parent property; Select-Object -ExpandProperty Parent should have been used.
In addition to only returning the property value of interest, -ExpandProperty also enforces the existence of the property. By contrast, Select-Object {$_.Directory} returns a custom object with a property literally named $_.Directory, whose value is $null, given that the input objects have no .Directory property; these $null values print as empty lines in the console.
As for the more general question about a PowerShell equivalent to LINQ's .Any() method, which indicates [with a Boolean result] whether a given enumerable (collection) has any elements at all / any elements satisfying a given condition:
Natively, PowerShell offers no such equivalent, but the behavior can be emulated:
Using the PowerShell v4+ intrinsic.Where() method:
Caveat: This requires collecting the entire input collection in memory first, which can be problematic with large collections and/or long-running input commands.
(...).Where({ $_ ... }, 'First').Count -gt 0
... represents the command of interest, and $_ ... the condition of interest, applied to each input object, where PowerShell's automatic $_ variable refers to the input object at hand; argument 'First' ensures that the method returns once the first match has been found.
For example:
# See if there's at least one value > 1
PS> (1, 2, 3).Where({ $_ -gt 1 }, 'First').Count -gt 0
True
Using the pipeline: Testing whether a command produced at least one output object [matching a condition]:
The advantage of a pipeline-based solution is that it can act on a command's output one by one, as it is being produced, without needing to collect the entire output in memory first.
If you don't mind that all objects are enumerated - even if you only care if there is at least one - use Paolo Tedesco's helpful extension to JaredPar's helpful answer.
The down-side of this approach is that you always have to wait for a (potentially long-running) command to finish producing all output objects, even though - logically - the determination whether there are any output objects can be made as soon as the first object is received.
If you want to exit the pipeline as soon as one [matching] object has been encountered, you have two options:
[Ad-hoc: Easy to understand, but cumbersome to implement]
Enclose the pipeline in a dummy loop and use break to break out of the pipeline and that loop (... represents the command whose output to test, and $_ ... match the condition):
# Exit on first input object.
[bool] $haveAny = do { ... | % { $true; break } } while ($false)
# Exit on first input object that matches a condition.
[bool] $haveAny = do { ... | % { if ($_ ...) { $true ; break } } } while ($false)
[Use a PowerShell v3+ self-contained utility function that is nontrivial to implement]
See the implementation of function Test-Any below.
It can be added to scripts or, for use in interactive sessions, to your $PROFILE file.
PowerShell v3+: Optimized utility function Test-Any
The function is nontrivial, because as of PowerShell (Core) v7.2.x, there is no direct way to exit a pipeline prematurely, so a workaround based on .NET reflection and a private type is currently necessary.
If you agree that there should be such a feature, take part in the conversation in GitHub issue #3821.
#requires -version 3
Function Test-Any {
[CmdletBinding()]
param(
[ScriptBlock] $Filter,
[Parameter(ValueFromPipeline = $true)] $InputObject
)
process {
if (-not $Filter -or (Foreach-Object $Filter -InputObject $InputObject)) {
$true # Signal that at least 1 [matching] object was found
# Now that we have our result, stop the upstream commands in the
# pipeline so that they don't create more, no-longer-needed input.
(Add-Type -Passthru -TypeDefinition '
using System.Management.Automation;
namespace net.same2u.PowerShell {
public static class CustomPipelineStopper {
public static void Stop(Cmdlet cmdlet) {
throw (System.Exception) System.Activator.CreateInstance(typeof(Cmdlet).Assembly.GetType("System.Management.Automation.StopUpstreamCommandsException"), cmdlet);
}
}
}')::Stop($PSCmdlet)
}
}
end { $false }
}
if (-not $Filter -or (Foreach-Object $Filter -InputObject $InputObject)) defaults to true if $Filter wasn't specified, and otherwise evaluates the filter (script block) with the object at hand.
The use of ForEach-Object to evaluate the filter script block ensures that $_ binds to the current pipeline object in all scenarios, as demonstrated in PetSerAl's helpful answer here.
The (Add-Type ... statement uses an ad-hoc type created with C# code that uses reflection to throw the same exception that Select-Object -First (PowerShell v3+) uses internally to stop the pipeline, namely [System.Management.Automation.StopUpstreamCommandsException], which as of PowerShell v5 is still a private type.
Background here:
http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx
A big thank-you to PetSerAl for contributing this code in the comments.
Examples:
PS> #() | Test-Any false
PS> Get-EventLog Application | Test-Any # should return *right away* true
PS> 1, 2, 3 | Test-Any { $_ -gt 1 } # see if any object is > 1 true
Background information
JaredPar's helpful answer and Paolo Tedesco's helpful extension fall short in one respect: they don't exit the pipeline once a match has been found, which can be an important optimization.
Sadly, even as of PowerShell v5, there is no direct way to exit a pipeline prematurely.
If you agree that there should be such a feature, take part in the conversation in GitHub issue #3821.
A naïve optimization of JaredPar's answer actually shortens the code:
# IMPORTANT: ONLY EVER USE THIS INSIDE A PURPOSE-BUILT DUMMY LOOP (see below)
function Test-Any() { process { $true; break } end { $false } }
The process block is only entered if there's at least one element in the pipeline.
Small caveat: By design, if there's no pipeline at all, the process block is still entered, with $_ set to $null, so calling Test-Any outside of a pipeline unhelpfully returns $true. To distinguish between between $null | Test-Any and Test-Any, check $MyInvocation.ExpectingInput, which is $true only in a pipeline: Thanks, PetSerAl
function Test-Any() { process { $MyInvocation.ExpectingInput; break } end { $false } }
$true, written to the output stream, signals that at least one object was found.
break then terminates the pipeline and thus prevents superfluous processing of additional objects. HOWEVER, IT ALSO EXITS ANY ENCLOSING LOOP - break is NOT designed to exit a PIPELINEThanks, PetSerAl
.
If there were a command to exit the pipeline, this is where it would go.
Note that return would simply move on to the next input object.
Since the process block unconditionally executes break, the end block is only reached if the process block was never entered, which implies an empty pipeline, so $false is written to the output stream to signal that.
Unfortunately there is no equivalent in PowerShell. I wrote a blog post about this with a suggestion for a general purpose Test-Any function / filter.
function Test-Any() {
begin {
$any = $false
}
process {
$any = $true
}
end {
$any
}
}
Blog post: Is there anything in that pipeline?
A variation on #JaredPar's answer, to incorporate the test in the Test-Any filter:
function Test-Any {
[CmdletBinding()]
param($EvaluateCondition,
[Parameter(ValueFromPipeline = $true)] $ObjectToTest)
begin {
$any = $false
}
process {
if (-not $any -and (& $EvaluateCondition $ObjectToTest)) {
$any = $true
}
}
end {
$any
}
}
Now I can write "any" tests like
> 1..4 | Test-Any { $_ -gt 3 }
True
> 1..4 | Test-Any { $_ -gt 5 }
False
You can use the original LINQ Any:
[Linq.Enumerable]::Any($list)
It's actually quite simple - just select first $true (formatted for clarity):
[bool] ($source `
| foreach { [bool] (<predicate>) } `
| where { $_ } `
| select -first 1)
Alternative way:
($source `
| where { <predicate> } `
| foreach { $true } `
| select -first 1)
My approach now was:
gci -r -force `
| ? { $_.PSIsContainer -and $_.Name -match "^[._]svn$" } `
| select Parent -Unique
The reason why
select-object {$_.Directory}
doesn't return anything useful is that there is no such property on a DirectoryInfo object. At least not in my PowerShell.
To elaborate on your own answer: PowerShell can treat most non-empty collections as $true, so you can simply do:
$svnDirs = gci `
| ? {$_.PsIsContainer} `
| ? {
gci $_.Name -Force `
| ? {$_.PSIsContainer -and ($_.Name -eq "_svn" -or $_.Name -eq ".svn") }
}
I ended up doing it with a count:
$directoryContainsSvn = {
(Get-ChildItem $_.Name -force | ? {$_.PsIsContainer -and $_.Name -eq "_svn" -or $_.Name -eq ".svn"} | Measure-Object).Count -eq 1
}
$svnDirs = Get-ChildItem | ? {$_.PsIsContainer} | ? $directoryContainsSvn
You can tighten this up a bit:
gci -fo | ?{$_.PSIsContainer -and `
(gci $_ -r -fo | ?{$_.PSIsContainer -and $_ -match '[_.]svn$'})}
Note - passing $__.Name to the nested gci is unnecessary. Passing it $_ is sufficent.
I recommend the following solution:
<#
.SYNOPSIS
Tests if any object in an array matches the expression
.EXAMPLE
#( "red", "blue" ) | Where-Any { $_ -eq "blue" } | Write-Host
#>
function Where-Any
{
[CmdletBinding()]
param(
[Parameter(Mandatory = $True)]
$Condition,
[Parameter(Mandatory = $True, ValueFromPipeline = $True)]
$Item
)
begin {
[bool]$isMatch = $False
}
process {
if (& $Condition $Item) {
[bool]$isMatch = $true
}
}
end {
Write-Output $isMatch
}
}
# optional alias
New-Alias any Where-Any
This is the best method that I found so far (does not iterate over all elements if already found a true, and does not break the pipeline):
From LINQ Any() equivalent in PowerShell
It’s possible to use a built-in $input variable that contains the whole pipeline in a scope of function.
So, the desired code could look like the following:
function Test-Any([scriptBlock] $scriptBlock = {$true}, [scriptBlock] $debugOut = $null)
{
if ($debugOut)
{
Write-Host(“{0} | % {{{1}}}” -f $input, $scriptBlock)
}
$_ret = $false;
$_input = ($input -as [Collections.IEnumerator])
if ($_input)
{
while ($_input.MoveNext())
{
$_ = $_input.Current;
Write-Host $_
if ($debugOut)
{
Write-Host(“Tested: [{0}]” -f (&$debugOut))
}
if (&$scriptBlock)
{
if ($debugOut)
{
Write-Host(“Matched: [{0}]” -f (&$debugOut))
}
$_ret = $true
break
}
}
}
$_ret
}
I think that the best answer here is the function proposed by #JaredPar, but if you like one-liners as I do I'd like to propose following Any one-liner:
# Any item is greater than 5
$result = $arr | %{ $match = $false }{ $match = $match -or $_ -gt 5 }{ $match }
%{ $match = $false }{ $match = $match -or YOUR_CONDITION }{ $match } checks that at least one item match condition.
One note - usually the Any operation evaluates the array until it finds the first item matching the condition. But this code evaluates all items.
Just to mention, you can easily adjust it to become All one-liner:
# All items are greater than zero
$result = $arr | %{ $match = $false }{ $match = $match -and $_ -gt 0 }{ $match }
%{ $match = $false }{ $match = $match -and YOUR_CONDITION }{ $match } checks that all items match condition.
Notice, that to check Any you need -or and to check All you need -and.
I took a more linq-style approach.
I know this question is probably super old. I used this to accomplish my needs:
PS> $searchData = "unn"
PS> $StringData = #("unn", "dew", "tri", "peswar", "pymp")
PS> $delegate = [Func[string,bool]]{ param($d); return $d -eq $searchData }
PS> [Linq.Enumerable]::Any([string[]]$StringData, $delegate)
Taken from here:
https://www.red-gate.com/simple-talk/dotnet/net-framework/high-performance-powershell-linq/#post-71022-_Toc482783751

Resources