Why does Powershell inline VersionInfo on Write-Output delivers a different output? - windows

I build the following script for debugging:
# Store file in var
$File = (Get-ChildItem -Path [ANY_EXE].exe)
# Get VerionInfo
$Version = $File.VersionInfo
# Print VersionInfo
Write-Output "$Version"
which results in this output:
File: [ANY_EXE].exe
InternalName: [ANY_EXE]
OriginalFilename: [ANY_EXE]
FileVersion: 8.20.1.14183
FileDescription: [DESCRIPTION_FOR_EXE]
Product: [PRODUCT_FOR_EXE]
ProductVersion: 8.20
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language: Englisch (Vereinigte Staaten)
Putting the script in this inline function:
Write-Output (Get-ChildItem [ANY_EXE].exe).VersionInfo
results in a completely different output:
ProductVersion FileVersion FileName
-------------- ----------- --------
8.20 8.20.1.14183 [ANY_EXE].exe
How can I get the first output, using an inline function?

The following code fixes my issue:
(Get-ChildItem [ANY_EXE]exe).VersionInfo -as [string]
Thanks to Mathias R. Jessen and Jeroen Mostert
for explaining the reason and solving my issue.

Related

add process names that contains space in powershell

i have listed all the processes with handle.exe that access one directory.
function Get-FileHandle ($HPath){
$handles = handle $HPath
}
the output seems like:
Nthandle v4.11 - Handle viewer Copyright (C) 1997-2017 Mark
Russinovich Sysinternals - www.sysinternals.com
jabra-direct.exe pid: 12716 type: File 838:
C:\Windows\System32\drivers\etc
Creative Cloud.exe pid: 4280 type: File 9D0:
C:\Windows\System32\drivers\etc
Adobe CEF Helper.exe pid: 12916 type: File 494:
C:\Windows\System32\drivers\etc
brave.exe pid: 2920 type: File 690:
C:\Windows\System32\drivers\etc
brave.exe pid: 13828 type: File 344:
C:\Windows\System32\drivers\etc
Now I am trying to list only the processnames without all the other values.
tha code i have, is:
foreach ($handle in $gethandle) {
$handle.Split(" ") | ?{$_ -like "*exe"}
}
The output is:
jabra-direct.exe
Cloud.exe
Helper.exe
brave.exe
brave.exe
everything fine untill the processname contains a space. It should be creative cloud.exe and not cloud.exe.
How can i make it work ? sothat creative cloud.exe will be shown and not only cloud.exe ?
Here is a way using the Select-String cmdlet.
$handles = handle $HPath # assuming handle.exe can be found via PATH env var
$selected = $handles | Select-String -Pattern '.*?(?= +pid:)'
$processNames = $selected.Matches.Value # array of process names
The Select-String line extracts the process names from the output using a regular expression:
.*? - everything up to the following pattern (as little as possible to trim trailing whitespace)
(?= - starts a positive lookahead pattern
+ - one ore more space characters
pid: - literal "pid:"
) - ends the positive lookahead pattern
The positive lookahead makes sure that we only find sub strings followed by " pid:", without including " pid:" in the result.
The expression $selected.Matches.Value is a shortcut for:
$processNames = #()
foreach( $sel in $selected ) {
foreach( $match in $sel.Matches ) {
$processNames += $match.Value
}
}
When PowerShell can't find a property on an object that is an array, it automatically searches each array member for that property and returns an array of all values that is has found. This is called member enumeration.
Try the following code and adapt if necessary.
Regex is (.*)(?=pid:)pid:\s(\d+)\stype:\s([^\d]+)\s([\dABCDEF]+):\s(.*) that combine a positive lookahead and capturing groups. Then just use the auto property $Matches to extract capturing group values.
$output = #"
Nthandle v4.11 - Handle viewer Copyright (C) 1997-2017 Mark Russinovich Sysinternals - www.sysinternals.com
jabra-direct.exe pid: 12716 type: File 838: C:\Windows\System32\drivers\etc
Creative Cloud.exe pid: 4280 type: File 9D0: C:\Windows\System32\drivers\etc
Adobe CEF Helper.exe pid: 12916 type: File 494: C:\Windows\System32\drivers\etc
brave.exe pid: 2920 type: File 690: C:\Windows\System32\drivers\etc
brave.exe pid: 13828 type: File 344: C:\Windows\System32\drivers\etc
"#
$output = $output.Split("`r`n")
$output | ForEach-Object {
if ($_ -match "(.*)(?=pid:)pid:\s(\d+)\stype:\s([^\d]+)\s([\dABCDEF]+):\s(.*)") {
$props = [ordered]#{
Name = $Matches[1]
PID = $Matches[2]
Type = $Matches[3]
Handle = $Matches[4]
Path = $Matches[5]
}
New-Object -TypeName PSObject -Property $props
}
} | Format-Table -AutoSize

Logging the current executing command within the Action Block for Set-PSBreakpoint

I am attaching a command breakpoint for every "Get*" in my powershell session. This command breakpoint involves calling an Action which invokes Write-Host "We are trapped in this code". As you can see below
Eg.
PS /Users/test> Set-PSBreakpoint -Command "Get*" -Action {
>> Write-Host "We are trapped in this code"
>> }
ID Script Line Command Variable Action
-- ------ ---- ------- -------- ------
1 Get* …
PS /Users/test> Get-Runspace
We are trapped in this code
Id Name ComputerName Type State Availability
-- ---- ------------ ---- ----- ------------
1 Runspace1 localhost Local Opened Busy
PS /Users/test>
Now instead of printing
We are trapped in this code
we want to print
We are trapped in this code: Get-Runspace
In this specifically we want to print the command ie. Get-Runspace on which breakpoint was applied.
Is there any way to get the hold of the Executing Command within the ScriptBlock.
PS: We have tried $MyInvocation.MyCommand.Name within the script block and various other methods but all of them return null.
Assuming that you're interested in the command line that triggered the breakpoint, use $MyInvocation.Line:
Set-PSBreakpoint -Command "Get-conten*" -Action {
Write-Host "We are trapped in this code: $($MyInvocation.Line)"
}
If you want to inspect the specific values of the automatic $MyInvocation variable (which is an instance of type System.Management.Automation.InvocationInfo), pipe it to the Out-Host cmdlet:
Set-PSBreakpoint -Command "Get-conten*" -Action {
$MyInvocation | Out-Host
}

How to run a PowerShell / ImapX script manually or in Task Scheduler?

I'm running this script in PowerShell ISE and it works great and it downloads the attachments to the folder I've specified.
But if I run it manually in PowerShell using
PS C:\tmp> .\pdftofolder.ps1
it doesn't download the attachments and the log says this:
**********************
Windows PowerShell transcript start
Start time: 20200517211912
Username: MON1\Administrator
RunAs User: MON1\Administrator
Configuration Name:
Machine: MON1 (Microsoft Windows NT 10.0.17763.0)
Host Application: C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe
Process ID: 1708
PSVersion: 5.1.17763.1007
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.17763.1007
BuildVersion: 10.0.17763.1007
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is C:\tmp\log.txt
GAC Version Location
--- ------- --------
False v4.0.30319 C:\tmp\ImapX.dll
True
True
**********************
Windows PowerShell transcript end
End time: 20200517211912
**********************
And if I run it through the Task Scheduler (which is the ultimate goal) it just gets stuck. I run it with this command (its ran with highest privileges btw):
Start-Transcript -path C:\tmp\log.txt -append
$dll = 'C:\tmp\ImapX.dll'
[Reflection.Assembly]::LoadFile($dll)
$Username = "email#email.com"
$Password = "foo"
# Initialize the IMAP client
$client = New-Object ImapX.ImapClient
###set the fetching mode to retrieve the part of message you want to retrieve,
###the less the better
$client.Behavior.MessageFetchMode = "Full"
$client.Host = "mail.foo.com"
$client.Port = 993
$client.UseSsl = $true
$client.Connect()
$client.Login($Username, $Password)
foreach($m in $messages){
$m.Subject
foreach($r in $m.Attachments){
Write-Host $m.Attachments
$r.Download()
$r.Save('C:\tmp\dl')
}
}
Stop-Transcript
What part of running a PowerShell script made after its built in ISE have I missed? Thanks in advance.
You've skipped
$messages = $client.Folders.Inbox.Search("ALL", $client.Behavior.MessageFetchMode, 1000)

PowerShell equivalent of BASH (etc) 'type' command?

On *nix, using BASH (etc) you ask the system where a command is located (etc) by using the 'type' shell built-in like this:
$ type cat
cat is /bin/cat
Is there an equivalent of this 'type' command in Microsoft PowerShell 2.0 ?
An equivalent is Get-Command.
PS C:\> Get-Command ls
CommandType Name Definition
----------- ---- ----------
Alias ls Get-ChildItem
Application ls.exe D:\usr\local\wbin\ls.exe
Application ls.exe C:\Program Files (x86)\Git\bin\ls.exe
Windows 10 Update:
Since I've posted this answer, it appears that the behavior of Get-Command has changed. To include all results (in the style of Un*x) type), now I need to pass the -All flag, like so:
PS C:\> Get-Command -All ls
CommandType Name Version Source
----------- ---- ------- ------
Alias ls -> Get-ChildItem
Application ls.exe 0.0.0.0 C:\Program Files (x86)\Git\usr\bin\ls.exe
As noted in a comment, this doesn't include the Definition column as was the previous behavior. I can't determine a command-line argument to add the definition column, but as noted by #voutasaurus in the comment below, one can use:
PS C:\> (Get-Command -All ls).Definition
Get-ChildItem
C:\Program Files (x86)\Git\usr\bin\ls.exe
Version information for reference (I odn't have the version information associated with the original answer text, but I'm guessing that it was Windows 7):
PS C:\> [System.Environment]::OSVersion.Version
Major Minor Build Revision
----- ----- ----- --------
10 0 15063 0
Get-Command has a -ShowCommandInfo parameter that does this. It also works for functions defined in $profile :
PS C:\Users\vp937ll> Get-Command l -ShowCommandInfo
Name : l
ModuleName :
Module : #{Name=}
CommandType : Function
Definition : Get-ChildItem | Sort-Object -Property LastWriteTime -Descending
ParameterSets : {#{Name=__AllParameterSets; IsDefault=False; Parameters=System.Management.Automation.PSObject[]}}
Since you tagged this with Shell, in addition to PowerShell's Get-Command, there's where.exe:
PS C:\> where.exe notepad
C:\Windows\System32\notepad.exe
C:\Windows\notepad.exe
The command just looks for a file with the specified name through the path:
PS C:\> where.exe readme.*
C:\Python31\README.txt
C:\Program Files (x86)\wget\README
C:\Program Files (x86)\SysinternalsSuite\readme.txt
Note that when calling this command from PowerShell, you must call it as where.exe because Where-Object is aliased to where.
Get-Command seems right, aliased as gcm
Just pipe it to Select-Object * aliased as select *
E.g., in $profile I have a function that opens total commander from pwd (it silently kills the existing instance first)
function start-totalCommanderhere {
$here = (Get-Location).path
kill -n TOTALCMD64 -ErrorAction Ignore
start "c:\totalcmd\TOTALCMD64.EXE" $here
}
Set-Alias tc start-totalCommanderhere
Here is all the info about the function — it however does not tell you directly that it is a function, you would need to query its proper name not alias to get that information from CommandType attribute. (than it would be same as type in bash which tells you hello_world is a function in case it is one)
▶ gcm tc | select *
HelpUri :
ResolvedCommandName : start-totalCommanderhere
DisplayName : tc -> start-totalCommanderhere
ReferencedCommand : start-totalCommanderhere
ResolvedCommand : start-totalCommanderhere
Definition : start-totalCommanderhere
Options : None
Description :
OutputType : {}
Name : tc
CommandType : Alias
Source :
Version :
Visibility : Public
ModuleName :
Module :
RemotingCapability : PowerShell
Parameters : {}
ParameterSets :
gcm <module_name> | select * gives you the path if you ask for a CommandType which is Application
▶ gcm ping | select *
HelpUri :
FileVersionInfo : File: C:\Windows\system32\PING.EXE
InternalName: ping.exe
OriginalFilename: ping.exe.mui
FileVersion: 10.0.19041.320 (WinBuild.160101.0800)
FileDescription: TCP/IP Ping Command
Product: Microsoft® Windows® Operating System
ProductVersion: 10.0.19041.320
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language: English (United States)
Path : C:\Windows\system32\PING.EXE
Extension : .EXE
Definition : C:\Windows\system32\PING.EXE
Source : C:\Windows\system32\PING.EXE
Version : 10.0.19041.1
Visibility : Public
OutputType : {System.String}
Name : PING.EXE
CommandType : Application
ModuleName :
Module :
RemotingCapability : PowerShell
Parameters :
ParameterSets :
But with gcm you do not get paths for imported modules — for that you need to use Get-Module | Select-Object Name, Path aliased as gmo | select name,path
▶ gmo | select name, path
Name Path
---- ----
assign-vault-keys-to-env-vars C:\Users\Admin\Documents\workspace\work.log\kb\powershell\assign-vault-keys-to-env-vars.ps1
CimCmdlets C:\Program Files\PowerShell\7\Microsoft.Management.Infrastructure.CimCmdlets.dll
decode-base64 C:\Users\Admin\Documents\workspace\work.log\kb\powershell\decode-base64.ps1
DnsClient C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\DnsClient\DnsClient.psd1
...

Windows Equivalent of 'nice'

Is there a Windows equivalent of the Unix command, nice?
I'm specifically looking for something I can use at the command line, and not the "Set Priority" menu from the task manager.
My attempts at finding this on Google have been thwarted by those who can't come up with better adjectives.
If you want to set priority when launching a process you could use the built-in START command:
START ["title"] [/Dpath] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED]
[/LOW | /NORMAL | /HIGH | /REALTIME | /ABOVENORMAL | /BELOWNORMAL]
[/WAIT] [/B] [command/program] [parameters]
Use the low through belownormal options to set priority of the launched command/program. Seems like the most straightforward solution. No downloads or script writing. The other solutions probably work on already running procs though.
If you use PowerShell, you could write a script that let you change the priority of a process. I found the following PowerShell function on the Monad blog:
function set-ProcessPriority {
param($processName = $(throw "Enter process name"), $priority = "Normal")
get-process -processname $processname | foreach { $_.PriorityClass = $priority }
write-host "`"$($processName)`"'s priority is set to `"$($priority)`""
}
From the PowerShell prompt, you would do something line:
set-ProcessPriority SomeProcessName "High"
Maybe you want to consider using ProcessTamer that "automatize" the process of downgrading or upgrading process priority based in your settings.
I've been using it for two years. It's very simple but really effective!
from http://techtasks.com/code/viewbookcode/567
# This code sets the priority of a process
# ---------------------------------------------------------------
# Adapted from VBScript code contained in the book:
# "Windows Server Cookbook" by Robbie Allen
# ISBN: 0-596-00633-0
# ---------------------------------------------------------------
use Win32::OLE;
$Win32::OLE::Warn = 3;
use constant NORMAL => 32;
use constant IDLE => 64;
use constant HIGH_PRIORITY => 128;
use constant REALTIME => 256;
use constant BELOW_NORMAL => 16384;
use constant ABOVE_NORMAL => 32768;
# ------ SCRIPT CONFIGURATION ------
$strComputer = '.';
$intPID = 2880; # set this to the PID of the target process
$intPriority = ABOVE_NORMAL; # Set this to one of the constants above
# ------ END CONFIGURATION ---------
print "Process PID: $intPID\n";
$objWMIProcess = Win32::OLE->GetObject('winmgmts:\\\\' . $strComputer . '\\root\\cimv2:Win32_Process.Handle=\'' . $intPID . '\'');
print 'Process name: ' . $objWMIProcess->Name, "\n";
$intRC = $objWMIProcess->SetPriority($intPriority);
if ($intRC == 0) {
print "Successfully set priority.\n";
}
else {
print 'Could not set priority. Error code: ' . $intRC, "\n";
}

Resources