Reusing powershell cmdlet parameters - powershell

I was wondering if it was possible to call a parameter twice within a cmdlet, such as:
cmdlet-test -myCommand input1, input2 -myCommand input3, input 4
myCommand being the same command twice. Is that possible or will a user just have to write it all in one comma separated list?

If the user attempts to use the same parameter twice, they will receive an error.
get-process | select -first 2
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
201 16 3324 3456 109 0.17 9972 acrotray
409 16 3904 4948 101 0.97 10520 AdobeARM
Let's try it the conventional way:
get-process -pid 9972,10520
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
201 16 3324 3456 109 0.17 9972 acrotray
409 16 3904 4948 101 0.97 10520 AdobeARM
And your proposed method:
get-process -pid 9972 -pid 10520
Get-Process : Cannot bind parameter because parameter 'Id' is specified more than once. To provide multiple values to
parameters that can accept multiple values, use the array syntax. For example, "-parameter value1,value2,value3".
At line:1 char:23
+ get-process -pid 9972 -pid 10520
+ ~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-Process], ParameterBindingException
+ FullyQualifiedErrorId : ParameterAlreadyBound,Microsoft.PowerShell.Commands.GetProcessCommand

For those who would like to separate a really long function argument into several lines, I suggest this approach:
# Just an example function
function PrintFileName {
param (
[Parameter(Mandatory = $true)] [System.String[]] $Files = #()
)
foreach ($File in $Files) {
Write-Host "${File}"
}
}
Then, the function call like this:
PrintFileName -Files C:\SomeDirectory\SomeFile.txt1,C:\SomeDirectory\SomeFile2.txt,`
C:\SomeDirectory\SomeFile2.txt,C:\SomeDirectory\SomeFile3.txt,`
C:\SomeDirectory\SomeFile4.txt,C:\SomeDirectory\SomeFile5.txt
Will output:
C:\SomeDirectory\SomeFile1.txt
C:\SomeDirectory\SomeFile2.txt
C:\SomeDirectory\SomeFile2.txt
C:\SomeDirectory\SomeFile3.txt
C:\SomeDirectory\SomeFile4.txt
C:\SomeDirectory\SomeFile5.txt

Related

How do I select Process ID (PID) in powershell Get-Counter script?

This script is giving me the CPU for each node process. Which is great. But I need to know the PID for each process so that I can match specific process to CPU usage.
$ProcessName = "node"
$CpuCores = (Get-WMIObject Win32_ComputerSystem).NumberOfLogicalProcessors
$Samples = (Get-Counter "\Process($Processname*)\% Processor Time").CounterSamples
$Samples | Select InstanceName,#{Name="CPU %";Expression={[Decimal]::Round(($_.CookedValue / $CpuCores), 4)}}
I've searched everywhere I can think. I've tried PID, ID, ProcessID, InstanceID and many other variants.
Seems like it should be simple?
You are dot referencing specific properties and that's all you'll get back. Thus, you cannot ask for what is not supplied.
# Using variable squeezing to output results to the screen while assigning results to the variable.
($ProcessName = "dllhost")
($CpuCores = (Get-WMIObject Win32_ComputerSystem).NumberOfLogicalProcessors)
($Samples = (Get-Counter "\Process($Processname*)\% Processor Time").CounterSamples)
($Samples | Select InstanceName,#{Name="CPU %";Expression={[Decimal]::Round(($_.CookedValue / $CpuCores), 4)}})
# Results
<#
dllhost
8
Path InstanceName CookedValue
---- ------------ -----------
\\lab01\process(dllhost#2)\% processor time dllhost 0
\\lab01\process(dllhost#1)\% processor time dllhost 0
\\lab01\process(dllhost)\% processor time dllhost 0
dllhost
dllhost
dllhost
#>
Get-Process -Name 'dllhost'
# Results
<#
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
120 7 1580 6760 0.11 8612 2 dllhost
200 17 3620 11332 0.09 14176 0 dllhost
229 16 3984 12216 0.36 15940 2 dllhost
#>
Get-Counter -Counter "\Process($Processname*)\% Processor Time"
# Results
<#
Timestamp CounterSamples
--------- --------------
23-Nov-19 17:52:10 \\lab01\process(dllhost#2)\% processor time :
3.11758575755139
\\lab01\process(dllhost#1)\% processor time :
0
\\lab01\process(dllhost)\% processor time :
0
#>
So, you need to combine the above for a single result. There are differ ways to do this of course, For example:
Clear-Host
Get-Process -Name 'WUDFHost' |
ForEach {
[PSCustomObject]#{
'ProcessName' = $PSItem.ProcessName
'ProcessId' = $PSItem.Id
'Path' = $PSItem.Path
'Cookedvalue' = ((Get-Counter -Counter "\Process($($PSItem.Name))\% Processor Time").CounterSamples).CookedValue
}
}
# Results
<#
ProcessName ProcessId Path Cookedvalue
----------- --------- ---- -----------
WUDFHost 1100 C:\Windows\System32\WUDFHost.exe 21.8216373679803
WUDFHost 4020 C:\Windows\System32\WUDFHost.exe 6.23866621508705
WUDFHost 4644 C:\Windows\System32\WUDFHost.exe 9.36077443109706
WUDFHost 10280 C:\Windows\System32\WUDFHost.exe 3.11837874640775
#>

Assign data to a string

When I type Get-Process in PowerShell, I get this:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
1056 15 2952 1844 49 124.29 580 csrss
1158 78 63620 42976 356 593.47 5784 explorer
Is there a way I can concatenate all the data from row 1 onto string 1, and row 2 onto string 2, etc.
For example:
$str1 = "csrss 580 124.29 49 ..."
$str2 = "explorer 5784 593.47 356 ..."
You can combine each line fairly easily using a foreach loop.
$processes = Get-Process
$processes | % {[string[]]$processLines += $_.ProcessName + "," + $_.Id}
$processLines
The above code will give you an array of strings for each process. You will have to decide what properties you want to include on each string. If you want to create separate variable names instead of using an array that would require a bit more work.

Powershell killing processes - what's wrong with my string?

I know that working code is
Get-Process firefo* | Stop-Process
But my first guess was
Get-Process | findstr firefox | Stop-Process
It didn't work.
Stop-Process : The input object cannot be bound to any parameters for the command
either because the command does not take pipeline input or the input and its
properties do not match any of the parameters that take pipeline input.
At line:1 char:33
+ Get-Process | findstr firefox | Stop-Process
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: ( 1379 317... :PSObject) [Stop-Process], ParameterBindingException
+ FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.StopProcessCommand
I understand that string
1342 306 1228412 1279864 -1671 ...71,42 35912 firefox
is bad for process killing, but why?
PS C:\Users\adamg> Get-Process firefo*
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
1342 306 1228412 1279864 -1671 ...71,42 35912 firefox
The above works just fine, even with column headers in reply.
findstr is a commandline utility that produces string output. Get-Process outputs Process objects, which is what Stop-Process expects as input. It could also handle a list of process IDs, but it can't parse the formatted string from findstr.
In PowerShell you normally wouldn't use findstr anyway. Use a Where-Object filter instead:
Get-Process | Where-Object { $_.ProcessName -like '*firefox*' } | Stop-Process

How to get the process start time in PowerShell

How do I get the process start time in PowerShell?
I tried this in the PowerShell prompt:
(Get-Process MyProcessName).StartTime.ToString('yyyyMMdd')
And this is the error I got:
(Get-Process MyProcessName).StartTime.ToString('yyyyMMdd')
You cannot call a method on a null-valued expression.
At line:1 char:1
+ (Get-Process MyProcessName).StartTime.ToString('yyyyMMdd_h ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
But when I do Get-Process MyProcess, I see my process:
> Get-Process MyProcess
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
2906 132 81136 79132 889 20656 myprocess
And when I do 'Get-Date -format 'yyyyMMdd', it returns '20151207', so I think the date format is correct.
As a one-liner you could use:
Get-Process ProcessName | select starttime
Do it like this:
(Get-Date (Get-Process explorer).StartTime).ToString('yyyyMMdd')
Run as admin:
Get-Process | select name, starttime | findstr your_process_name_here
It looks like you're not inputting the process name correctly ("MyProcessName" vs. "MyProcess"). You have
(Get-Process MyProcessName).StartTime.ToString('yyyyMMdd')
But it should be
(Get-Process MyProcess).StartTime.ToString('yyyyMMdd')
per your examples.
if you want to get the start time of a specific process use the following substituting "process name" with its name:
Get-Process ProcessName | select Name, StartTime
or to get all running processes' start times
Get-Process | select Name, StartTime

In Powershell, how can I create a mulitple records hashtable?

In Powershell, how can I create a mulitple records hashtable?
Like these:
> Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
> ------- ------ ----- ----- ----- ------ -- -----------
> 260 25 98568 109684 263 37.80 7896 AcroRd32
> 67 8 1236 3800 30 0.03 2052 atashost
> 122 7 1812 4496 29 0.08 1188 atiesrxx
> 126 10 16204 15936 49 7236 audiodg
> 170 21 12888 21352 106 0.14 5016 CallControl
It will be a list of objects in that case and not an hashtable. You can use New-Object to create a set of objects with those properties. Something like below:
$obj = new-object -type psobject
$obj | add-member -name Handles -MemberType NoteProperty -Value 123
and so on for each property. Look at new-object for simpler ways of doing the same.
If you already have a list of objects and want a few properties from them, use Select-Object
If your goal is to be able to utilize the Hashtables ability to use Key/Value pairs to easily lookup your data, like by the "ProcessName", then you can do this. Remember that Powershell is an Object Oriented shell. You can store objects within objects. Or in this case, Hashtables within Hashtables.
[hashtable] $ProcessDetails = #{Handles=260; NPM=25;
PM= 98568; WS=109684; VM=263; CPU=37.80; ID=7896}
$ProcessDetails
[hashtable] $Processes = #{}
$Processes.Add("AcroRd32", $ProcessDetails)
""
$Processes
""
# You can then use the various methods to extract the data.
$Processes.Get_Item("AcroRd32").ID
$Processes.AcroRd32.CPU
$Processes["AcroRd32"].VM
$Processes["AcroRd32"]["ws", "pm", "NPM", "Handles"]
Would output:
Name Value
---- -----
ID 7896
WS 109684
NPM 25
Handles 260
VM 263
PM 98568
CPU 37.8
AcroRd32 {ID, WS, NPM, Handles...}
7896
37.8
263
109684
98568
25
260
You could still create a New-Object to store you Processes details as the Hashtables Value. A PSObject has an advantage when sending output to a file, the screen, etc. But if you are doing data lookups for processing, then I like to stick with Hashtables.