How to parse the output in powershell? - windows

The command Get-Process gives output like below:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
65 6 1152 840 59 77.50 6048 Appx
78 8 2233 444 61 10.11 7878 Application
but I need a solution like below:
PM(K)=1152, ProcessName=Appx ; PM(K)=2233, ProcessName=Application
How do I parse output like above mentioned?

you can use -f to format your string :
PS>$resu=""
PS>gps | foreach {$resu+=("PM(K)= {0},appName={1};" -f ($_.pm/1KB),$_.name) }
PS>$resu

Try something like this:
$p = Get-Process | select #{n='PM(K)';e={$_.PM/1KB}}, ProcessName
($p | fl | Out-String) -replace "`n`n", ' ; ' -replace "`n", ', '

Related

Using Powershell [DateTime]::ParseExact - Funny Strings?

Good afternoon Powershell wizards!
I am hoping someone can explain to me how I can fix this issue, and more importantly what the issue actually is!
I'm attempting to fix an old script I wrote years ago that searches for several dates on a files properties and picks one to use for renaming that file.
The issue I'm having is that when I use parseExact it fails for the date strings read from the files... but it works if I manually type the same string into powershell!
Please note that this script is only going to be ran on my PC and only needs to work with dates from my files formats so I'm not too worried about use of $null unless it's related.
See example below:
Write-Host "TEST 1"
$DateTime = [DateTime]::ParseExact("240720211515","ddMMyyyyHHmm",$null)
Write-Host $DateTime # WORKS!
Write-Host "TEST 2"
$DateTime2 = [DateTime]::ParseExact("‎24‎07‎2021‏1515","ddMMyyyyHHmm",$null)
Write-Host $DateTime2 # FAILS!
Looks the same right?
Here is a more real world example of what I'm up to that fails
$file = Get-Item "C:\SomeFolder\somefile.jpg"
$shellObject = New-Object -ComObject Shell.Application
$directoryObject = $shellObject.NameSpace( $file.Directory.FullName )
$fileObject = $directoryObject.ParseName( $file.Name )
$property = 'Date taken'
for(
$index = 5;
$directoryObject.GetDetailsOf( $directoryObject.Items, $index ) -ne $property;
++$index) { }
$photoDate = $directoryObject.GetDetailsOf($fileObject, $index)
Write-Host $photoDate # <-- This reads ‎03/‎08/‎2021 ‏‎09:15
$output = [DateTime]::ParseExact($photoDate,"dd/MM/yyyy HH:mm",$null) # <-- This fails
Write-Host $output
# If i manually type in here it works.... If I copy and paste from the Write-Host it fails...
$someInput = "03/08/2021 09:15"
$workingOutput = [DateTime]::ParseExact($someInput,"dd/MM/yyyy HH:mm",$null)
Write-Host $workingOutput
For anyone else who comes across this, it seems like there are invisible characters being added. Thanks for the spot #SantiagoSquarzon
This fixes it for my particular purposes:
$photoDate = $directoryObject.GetDetailsOf($fileObject, $index)
$utfFree = $photoDate -replace "\u200e|\u200f", ""
I ran into this issue when I started exploring metadata with PowerShell. My solution was to create a regex "inverted whitelist" character class and delete (replace with '') all non-whitelist characters.
But then I learned of an alternate method avaiable to FolderItem obejcts: the ExtenedProperty() method.
Gets the value of a property from an item's property set. The property can be specified either by name or by the property set's format identifier (FMTID) and property identifier (PID).
It's primary strength being the feturn type corresponds to the property value type:
When this method returns, contains the value of the property, if it exists for the specified item. The value will have full typing—for example, dates are returned as dates, not strings.
Using this method to access date properties eliminates string parsing and the issues you encountered:
PS Pictures> $FileInfo = Get-Item "C:\Users\keith\Pictures\Leland\2009\Leland 191.JPG"
PS Pictures> $Shell = New-Object -ComObject shell.application
PS Pictures> $comFolder = $Shell.NameSpace($FileInfo.DirectoryName)
PS Pictures> $comFile = $comFolder.ParseName($FileInfo.Name)
PS Pictures>
PS Pictures> $comFolder.GetDetailsOf($null,12)
Date taken
PS Pictures> $comFolder.GetDetailsOf($comFile,12)
‎9/‎5/‎2009 ‏‎2:06 PM
PS Pictures> $comFolder.GetDetailsOf($comFile,12).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PS Pictures> $comFile.ExtendedProperty("DateTaken")
PS Pictures> $comFile.ExtendedProperty("System.Photo.DateTaken")
Saturday, September 5, 2009 07:06:41 PM
PS Pictures> $comFile.ExtendedProperty("System.Photo.DateTaken").GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DateTime System.ValueType
It also returns arrays for properties than can contain multiple values (Tags, Contributing Artists, etc/):
PS Pictures> $comFolder.GetDetailsOf($null,18)
Tags
PS Pictures> $comFolder.GetDetailsOf($comFile,18)
Leland; Tim; Jorge
PS Pictures> $comFolder.GetDetailsOf($comFile,18).GetTYpe()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PS Pictures> $comFile.ExtendedProperty("System.KeyWords")
PS Pictures> $comFile.ExtendedProperty("System.Keywords")
Leland
Tim
Jorge
PS Pictures> $comFile.ExtendedProperty("System.Keywords").GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String[] System.Array
But GetDetailsOf() is still useful, particularly for values that use an EnumList:
PS Pictures> $comFolder.GetDetailsOf($null,261)
Flash mode
PS Pictures> $comFolder.GetDetailsOf($comFile,261)
No flash, auto
PS Pictures> $comFile.ExtendedProperty("System.Photo.Flash")
24
Yeah something weird is going on with that string. (paste with control v in the console) It doesn't display right in stackoverflow, but ascii only goes up to code 0x7f (127). You can see the 200e and 200f hex codes. They look like little hooks. (emoji's would require extra handling with the surrogate characters)
$string = '# <-- This reads ‎03/‎08/‎2021 ‏‎09:15'
function chardump {
param($string)
[char[]]$string |
% { [pscustomobject]#{Char = $_; Code = [int]$_ | % tostring x} }
}
chardump $string
Char Code
---- ----
# 23
20
< 3c
- 2d
- 2d
20
T 54
h 68
i 69
s 73
20
r 72
e 65
a 61
d 64
s 73
20
‎ 200e
0 30
3 33
/ 2f
‎ 200e
0 30
8 38
/ 2f
‎ 200e
2 32
0 30
2 32
1 31
20
‏ 200f
‎ 200e
0 30
9 39
: 3a
1 31
5 35
chardump $string | ? {[int]('0x' + $_.code) -gt 0x7f}
Char Code
---- ----
‎ 200e
‎ 200e
‎ 200e
‏ 200f
‎ 200e

Find the interface type and revision of a hard drive using in a windows machine

I'm looking for a command in Windows (either cmd or powershell) which will allow me to get which exact type of bus interface my storage device have, not only the type, but also the revision (SATA3, SATA4, NVME2.0 and so on).
For example:
> wmic diskdrive get InterfaceRevision
InterfaceRevision
SATA3
SATA4
NVME2.0
This will get you the bus but not the version.
Function Get-MaxLength {
<#
.SYNOPSIS
Finds the length of the longest item in collection.
.DESCRIPTION
Use this Function to get the length of the longest item in a
collection for use in format strings or other places where
needed.
.PARAMETER TestObj
The qualified object to be tested. See example!
.Parameter MinLen
The minimum length of the item (if using for formatting) which
should be the Label (title) length. Note if the object item
being tested does not have a Length property you MUST specify
the label length!
.OUTPUTS
Returns a numerical value
.EXAMPLE
$NameLen = Get-MaxLength -TestObj $DotNet.PSChildName
$VerLen = Get-MaxLength -TestObj $DotNet.Version
$RNLen = Get-MaxLength -TestObj $DotNet.Release -MinLen 11
#--- .Net Information ---
$fmtDotNet =
#{Expression={$_.PSChildName};Label=".Net Type";Width=$NameLen},
#{Expression={$_.Version};Label="Version No:";Width=$VerLen},
#{Expression={$_.Release};Label="Release No:";Width=$RNLen}
$Dotnet | Format-Table $fmtDotNet
#>
Param(
[Parameter(Mandatory=$True)]
[object] $TestObj,
[Parameter(Mandatory=$False)]
[int] $MinLen = 0,
[Parameter(Mandatory=$False)]
[int] $MaxLen = 0
)
$ErrorActionPreference = "SilentlyContinue"
foreach ($x in $TestObj) {
If ($x.Trim().length -gt $MinLen) {
$MinLen = $x.Trim().length
}
}
If ($MaxLen -ne 0) {
If ($MinLen -gt $MaxLen) {
$MinLen = $MaxLen
}
}
$ErrorActionPreference = "Continue"
Return ,$MinLen
} #End Function ----------- Get-MaxLength -------------------
Function PhysicalDiskTab {
#Physical Drive Info
$PhyDiskInfo = Get-Disk | Where-Object {$_.size -gt 0 } |
Sort-Object -Property DiskNumber
$SSD = $False
$AMArgs = #{Type = 'NoteProperty'
Name = 'SSD'
Value = 'No'}
$PhyDiskInfo | Add-Member #AMArgs
$AMArgs = #{Type = 'NoteProperty'
Name = 'Speed'
Value = '0'}
$PhyDiskInfo | Add-Member #AMArgs
$GCIArgs = #{
NameSpace = "root\Microsoft\Windows\Storage"
Class = "MSFT_PhysicalDisk"
ErrorAction = "SilentlyContinue"
}
$RotateSpeed = Get-CimInstance #GCIArgs |
Select-Object -Property DeviceID,#{Name="Speed/RPMs";
Expression={(&{If($_.MediaType -eq 0) {[Int]0}
Else {$_.SpindleSpeed/600000 -f "#,###"}})}} |
Sort-Object DeviceID
ForEach ($x in $phydiskinfo) {
ForEach ($Device in $RotateSpeed) {
If ($x.number -eq $Device.DeviceID) {
If ($Device.'Speed/RPMs' -eq 0) {
$SSD = $True
$x.SSD = "Yes"
} #End If
Else {
$x.Speed = $([Int]$Device.'SPeed/RPMs') -f "#,###"
}
} #End If ($x.number...
} #End ForEach ($Device
} #End ForEach $x
$DNLen =
Get-MaxLength -TestObj $PhyDiskInfo.Model -MinLen 4
$SNLen =
Get-MaxLength -TestObj $PhyDiskInfo.SerialNumber -MinLen 13
$fmtPhyDisk1 =
#{Expression={ '{0:N0}' -f $_.Number};
Label="Drive`n No.";Width=5;Align='Center'},
#{Expression={$_.Model};Label="`nName";Width=$DNLen},
#{Expression={$_.SSD};Label="`nSSD";Width=3;Align='left'},
#{Expression={ '{0:#,000.00}' -f ($_.Size/1gb)};
Label="Disk Size`n / GB";Width=9;align='right'},
#{Expression={$_.NumberOfPartitions};
Label="Parti`ntions";Width=5},
#{Expression={$_.PartitionStyle};Label="GPT`nMBR";Width=3},
#{Expression={(&{If ($_.IsBoot) {"Yes"} else {""}})};
Label="`nBoot";Width=5;Align="Left"},
#{Expression={(&{If ($_.BusType.GetType().Name -eq 'UInt16'){
(& {Switch ($_.BusType) {
0 {"Unknown"}
1 {"SCSI"}
2 {"ATAPI"}
3 {"ATA"}
4 {"1394"}
5 {"SSA"}
6 {"Fibre Channel"}
7 {"USB"}
8 {"RAID"}
9 {"iSCSI"}
10 {"SAS"}
11 {"SATA"}
12 {"SD"}
13 {"MMC"}
14 {"MAX"}
15 {"File Backed Virtual"}
16 {"Storage Spaces"}
17 {"NVMe"}
18 {"MS Reserved"}
Default {"Unknown"}}})}
Else{$_.BusType}})};Label="`nData Bus";Width=20}
$fmtPhyDisk2 =
#{Expression={ '{0:N0}' -f $_.Number};
Label="Drive`n No.";Width=5;Align='Center'},
#{Expression={$_.Model};Label="`nName";Width=$DNLen},
#{Expression={$_.SerialNumber.Trim()};
Label="`nSerial Number";Width=$SNLen;Align='left'},
#{Expression={
(&{If ($_.HealthStatus.GetType().Name -eq 'UInt16'){
(& {Switch ($_.HealthStatus) {
0 {"Healthy"}
1 {"Warning"}
2 {"Unhealthy"}
Default {"Unknown"}}})}
Else{$_.HealthStatus}})};Label="`nStatus";Width=7},
#{Expression={$_.Speed};
Label="Rotation`n RPMs ";Width=8;Align='Right'}
$PhyDiskInfo1 = $PhyDiskInfo |
Format-Table -Property $fmtPhyDisk1 -Wrap |
Out-String -Width $OStrWidth
$PhyDiskInfo2 = $PhyDiskInfo |
Format-Table -Property $fmtPhyDisk2 -Wrap |
Out-String -Width $OStrWidth
$PhyDiskTitle = "Physical Disk Information:" | Out-String
Return ,$($PhyDiskTitle + $PhyDiskInfo1 + $PhyDiskInfo2)
} #End Function ---------------- PhysicalDiskTab --------------
$OStrWidth = 79
PhysicalDiskTab
Output:
Physical Disk Information:
Drive Disk Size Parti GPT
No. Name SSD / GB tions MBR Boot Data Bus
----- ----- --- --------- ----- --- ----- ---------
0 Samsung SSD 960 Yes 232.89 4 GPT Yes NVMe
1 Samsung SSD 850 PRO 256GB Yes 238.47 2 GPT SATA
2 Samsung SSD 850 PRO 256GB Yes 238.47 1 GPT SATA
Drive Rotation
No. Name Serial Number Status RPMs
----- ----- -------------- ------- --------
0 Samsung SSD 960 0025_3853_81B0_B2EB. Healthy 0
1 Samsung SSD 850 PRO 256GB S39KNX0J687882W Healthy 0
2 Samsung SSD 850 PRO 256GB S39KNX0J688151N Healthy 0
This code was pulled from my CMsLocalPCInfoW10 program which can be downloaded from my shared OneDrive library.

How to get Monitor Handle from Index or Name?

A C++ version (How can I get an HMONITOR handle from a display device name?) has no solution provided (at least in my circumstances that require non-OOP code such as in AutoIt).
I'm adapting an AutoIt script that uses WinAPI functions to support multi-monitor Windows 7+ systems. I can provide either monitor/device Name or it's Index, but some functions require an HMONITOR handle instead.
I cannot get the HMONITOR by Window or by pixel or point, which would be quite easy. No, I need to get the handle from name or index only, and I need a non-OOP solution (ideally AutoIt & WinAPI calls but non-OOP pseudo-code would be fine).
The function below returns an array of the following structure:
| hMonitor | xPosMonitor | yPosMonitor | widthMonitor | heightMonitor |
| 0x00010001 | 0 | 0 | 1366 | 768 |
| 0x0001024 | 1366 | -236 | 1920 | 1080 |
Code:
#include-once
#include <Array.au3>
#include <WinAPIGdi.au3>
Func _getMonitorInfos()
Local $aPosition, $aMonitorData = _WinAPI_EnumDisplayMonitors()
If IsArray($aMonitorData) Then
ReDim $aMonitorData[$aMonitorData[0][0] + 1][5]
For $i = 1 To $aMonitorData[0][0] Step 1
$aPosition = _WinAPI_GetPosFromRect($aMonitorData[$i][1])
For $j = 0 To 3 Step 1
$aMonitorData[$i][$j + 1] = $aPosition[$j]
Next
Next
Return $aMonitorData
EndIf
EndFunc
Global $aMonitorData = _getMonitorInfos()
_ArrayDisplay($aMonitorData)
hMonitor value is contained by the array $aMonitorData[1][1].

Windows / NTFS: Two files with identical long-names in the same directory?

I have been a lurker at stackoverflow.com for many years (great site and users here), but never had the need to ask a question. Now the time has come :-) Let me begin:
OS: x64 Windows 8.0 to Windows 10 (15063.14) (the issue exists since years, but I have never pursued it fully yet, so we can exclude that it is specific to a specific Windows version)
FS: NTFS
Issue: 2 files with the same (long) name in the same directory and I cannot figure out how this is even possible. This happens to me since years whenever I manually upgrade my Email client. The main .EXE file of it (MailClient.exe) is never asking for replacement if copying the new one over to the same directory. Instead they are both placed there, with the exact same long name.
The issue has nothing to do with a specific directory, I can copy around both .EXE files to freshly created directories on the NTFS drive without issues (also getting no "overwrite" question there).
Let me show you:
C:\temp\2>dir
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
13.04.2017 02:29 <DIR> .
13.04.2017 02:29 <DIR> ..
21.10.2016 17:10 24.742.760 MailClient.exe
27.12.2016 03:26 24.911.872 MailCliеnt.exe
2 File(s) 49.654.632 bytes
2 Dir(s) 78.503.038.976 bytes free
However, if doing a dir /x, this comes up:
C:\temp\2>dir /x
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
13.04.2017 02:29 <DIR> .
13.04.2017 02:29 <DIR> ..
21.10.2016 17:10 24.742.760 MAILCL~2.EXE MailClient.exe
27.12.2016 03:26 24.911.872 MAILCL~1.EXE MailCliеnt.exe
2 File(s) 49.654.632 bytes
2 Dir(s) 78.503.038.976 bytes free
So they obviously have a different 8.3 name, OK, but the exact same long name. Here is another screenshot of the situation. Both files show the same location within the Windows "properties" dialog (right click) too. Unfortunately I am not allowed to post images just yet (it seems) - just tried. So you will have to take my word.
I cannot figure out how this is possible and this is bugging me ;) As soon as I rename both files for example to 1.exe, Windows starts telling me that there is already a file with that name in the same directory. So it obviously has something to do with the filename, but they are both exactly identical, no extra spaces, nothing, as you can see from the DIR command.
I´ve also tried to rename them and re-wrote the exact wording "MailCient.exe" manually for both, to make sure the characters are EXCACTLY the same, Windows still won´t complain, they both go there once again under the same name. However, renaming them to "Mail.exe" and "Mail.exe" will NOT work, then Windows is saying that another file with that name already exists. However, naming them both back to "MailClient.exe" is just absolutely fine, no complains by Windows with that.
Another fun fact about this, if I dir for mailclient.exe directly, this happens:
C:\temp\2>dir mailclient.exe
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
21.10.2016 17:10 24.742.760 MailClient.exe
1 File(s) 24.742.760 bytes
0 Dir(s) 78.501.998.592 bytes free
However, if looking for *.exe, this happens:
C:\temp\2>dir *.exe
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
21.10.2016 17:10 24.742.760 MailClient.exe
27.12.2016 03:26 24.911.872 MailCliеnt.exe
2 File(s) 49.654.632 bytes
0 Dir(s) 78.501.990.400 bytes free
This yields also interesting results:
C:\temp\2>ren mailclient.exe *.bak
C:\temp\2>dir
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
13.04.2017 02:50 <DIR> .
13.04.2017 02:50 <DIR> ..
21.10.2016 17:10 24.742.760 MailClient.bak
27.12.2016 03:26 24.911.872 MailCliеnt.exe
2 File(s) 49.654.632 bytes
2 Dir(s) 78.501.990.400 bytes free
And back:
C:\temp\2>ren mailclient.bak MailClient.exe
C:\temp\2>dir
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
13.04.2017 02:51 <DIR> .
13.04.2017 02:51 <DIR> ..
21.10.2016 17:10 24.742.760 MailClient.exe
27.12.2016 03:26 24.911.872 MailCliеnt.exe
2 File(s) 49.654.632 bytes
2 Dir(s) 78.501.982.208 bytes free
I´ve also checked permissions on the files and took ownership, it changes nothing. Additionally I´ve cleared the NTFS Journal and even the transaction log + run chkdsk, which reveals no errors either.
Any ideas on this mysterious situation? What am I missing?
Thanks so much:)
UPDATE #1:
I´ve just tried this: going to Windows explorer and renaming both files after each other by truncating their names. So I first renamed the first "MailClient.exe" to "MailClien.exe", then the seconds "MailClient.exe" to "MailClien.exe". Again, no message by Windows that they have the same name, it just renamed both fine. I then continued to "MailClie.exe". Worked.
However, as soon as I tried to renamed both to "MailCli.exe", Windows complained and told me that there is already another file with that name. Trying to rename both back from there to "MailClient.exe" also does not work, just for one of them, because then Windows says (and right so too) that a file with that name already exists. So it seems to come down to the "e" possibly having another ANSI-character in both filenames? I, however, wouldn´t know of another one for "e", or am I missing something?
Harry Johnston is right: one of the filenames contains a Unicode character that just looks the same as an ANSI character.
Read Naming Files, Paths, and Namespaces:
On newer file systems, such as NTFS, exFAT, UDFS, and FAT32, Windows
stores the long file names on disk in Unicode, which means that the
original long file name is always preserved. This is true even if a
long file name contains extended characters, regardless of the code
page that is active during a disk read or write operation.
Use the following PowerShell script 43381802b.ps1 to detect and show non-ANSI file names (see different calls below):
param( [string[]]$Path = '.',
[switch]$Cpp, ### list any non-ANSI character in file names like a C++ literal
### i.e. a prefix \u followed by a four digit Unicode code point
[switch]$All ### list all files including pure ANSI-encoded file names
)
Set-StrictMode -Version latest
$strArr = Get-ChildItem -path $Path
$arrDiff = #()
for ($i=0; $i -lt $strArr.Count; $i++) {
$strDiff = 'ANSI'
$strName = ''
$auxName = $strArr[$i].Name
for ( $k=0; $k -lt $auxName.Length; $k++ ) {
if ( [int][char]$auxName[$k] -gt 255 ) {
$strDiff = 'UCS2'
$strName += '\u{0:X4}' -f [int][char]$auxName[$k]
} else {
$strName += $auxName[$k]
}
}
if ( $All.IsPresent -or $strDiff -eq 'UCS2' ) {
$strArr[$i] | Add-Member NoteProperty Code $strDiff
$strArr[$i] | Add-Member NoteProperty CppName $strName
$arrDiff += $strArr[$i]
}
}
if ( $Cpp.IsPresent ) {
$arrDiff | Select-Object -Property Code, Mode, LastWriteTime, Length, CppName | ft
} else {
$arrDiff | Select-Object -Property Code, Mode, LastWriteTime, Length, Name | ft
}
Output:
PS D:\PShell> .\SO\43381802b.ps1 'C:\testC\43381802'
Code Mode LastWriteTime Length Name
---- ---- ------------- ------ ----
UCS2 -a---- 02/05/2017 11:47:53 317 MailCliеnt.txt
UCS2 -a---- 02/05/2017 11:49:04 317 МailClient.txt
UCS2 -a---- 02/05/2017 11:50:16 399 МailCliеnt.txt
PS D:\PShell> .\SO\43381802b.ps1 'C:\testC\43381802' -Cpp
Code Mode LastWriteTime Length CppName
---- ---- ------------- ------ -------
UCS2 -a---- 02/05/2017 11:47:53 317 MailCli\u0435nt.txt
UCS2 -a---- 02/05/2017 11:49:04 317 \u041CailClient.txt
UCS2 -a---- 02/05/2017 11:50:16 399 \u041CailCli\u0435nt.txt
PS D:\PShell> .\SO\43381802b.ps1 'C:\testC\43381802' -Cpp -All
Code Mode LastWriteTime Length CppName
---- ---- ------------- ------ -------
ANSI -a---- 02/05/2017 11:44:05 235 MailClient.txt
UCS2 -a---- 02/05/2017 11:47:53 317 MailCli\u0435nt.txt
UCS2 -a---- 02/05/2017 11:49:04 317 \u041CailClient.txt
UCS2 -a---- 02/05/2017 11:50:16 399 \u041CailCli\u0435nt.txt
Use the following 43381802a.ps1 script to get more info about non-ANSI characters (see the first call bellow) and their position in file names (see the latter call bellow with -Detail switch):
param( [string[]] $strArr = #('ΗGreek', 'НCyril', 'HLatin'),
[switch]$Detail )
Set-StrictMode -Version latest
$auxArr = #()
if ( ( Get-Command -Name Get-CharInfo -ErrorAction SilentlyContinue ) -and
( -not $Detail.IsPresent ) ) {
$auxArr = $strArr | Get-CharInfo |
Where-Object { [int]$_.Codepoint.Replace('U+', '0x') -ge 128 }
} else {
foreach ($strStr in $strArr) {
for ($i = 0; $i -lt $strStr.Length; $i++ ) {
if ( [int][char]$strStr[$i] -ge 128 ) {
$auxArr += [PSCustomObject] #{
Char = $strStr[$i]
CodePoint = 'U+{0:x4}' -f [int][char]$strStr[$i]
Category = $i + 1 ### 1-based index
Description = $strStr ### string itself
}
}
}
}
}
$auxArr
Output:
PS D:\PShell> .\SO\43381802a.ps1 ( Get-childitem -path 'C:\testC\43381802' ).Name
Char CodePoint Category Description
---- --------- -------- -----------
е U+0435 LowercaseLetter Cyrillic Small Letter Ie
М U+041C UppercaseLetter Cyrillic Capital Letter Em
М U+041C UppercaseLetter Cyrillic Capital Letter Em
е U+0435 LowercaseLetter Cyrillic Small Letter Ie
PS D:\PShell> .\SO\43381802a.ps1 ( Get-childitem -path 'C:\testC\43381802' ).Name -detail
Char CodePoint Category Description
---- --------- -------- -----------
е U+0435 8 MailCliеnt.txt
М U+041c 1 МailClient.txt
М U+041c 1 МailCliеnt.txt
е U+0435 8 МailCliеnt.txt
Tested on files:
==> dir /-C /X /A-D C:\testC\43381802\
Volume in drive C has no label.
Volume Serial Number is …
Directory of C:\testC\43381802
02/05/2017 11:44 235 MAILCL~1.TXT MailClient.txt
02/05/2017 11:47 317 MAILCL~2.TXT MailCliеnt.txt
02/05/2017 11:49 317 AILCLI~1.TXT МailClient.txt
02/05/2017 11:50 399 AILCLI~2.TXT МailCliеnt.txt
4 File(s) 1268 bytes
0 Dir(s) 69914857472 bytes free
==>

What it means when PM(K) of ps gets negative values?

I got the following negative PM (The amount of pageable memory that the process is using, in kilobytes) values when execute the ps command. What it means when the values are negative?
PS H:\> ps sqlservr
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
5947 4145 -1218888 1537304 981 ...50.77 8344 sqlservr
PS H:\> ps sqlservr
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
6060 4172 -1218876 1537316 981 ...52.08 8344 sqlservr
PS H:\> ps sqlservr
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
6481 4258 -1218832 1537376 981 ...56.55 8344 sqlservr
The negative value may be the overflow of Int32? However, the following statements shows that the paged memory was 4TB? Which is not possible.
$m = [int32]::MaxValue
($m + ($m -1218832) + 2)/1024/1024
# returns 4094.83763122559 (GB)
This is a simple signed/unsigned overflow error. It's overflowing both the negative space and (in many modern situations) overflowing the unsigned 32-bit range as well.
I believe all PM(K) is doing is showing PagedMemorySize / 1024. They should be showing PagedMemorySize64 / 1024.
Here's what I get on my local server with 16GB of RAM and 14GB reserved for SQL Server:
PS C:\> $x = ps sqlservr
PS U:\> $x
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
1555 1280 -1683524 132568 81 2056 sqlservr
PS U:\> $x.PagedMemorySize / 1024
-1683524
PS C:\> $x.PagedMemorySize
-1723928576
PS C:\> $x.PagedMemorySize.GetType().FullName
System.Int32
The guys who wrote Get-Process should have used unsigned integers, but didn't.
You can do the two's compliment math pretty easily, but it'll be pretty clear that the value still doesn't make sense because you've overflowed out of 32-bit address space entirely:
PS C:\> ($x.PagedMemorySize + [uint32]::MaxValue + 1) / 1GB
2.39446640014648
And you can prove out that the number takes more than 31 bits of address space pretty easily:
PS C:\> [math]::log($x.PagedMemorySize + [uint32]::MaxValue + 1)/[math]::log(2)
31.2597041913968
[Note: I might have that wrong... I can never remember when to add the 1.]
You should use PagedMemorySize64 / 1024:
PS C:\> $x.PagedMemorySize64.GetType().FullName
System.Int64
PS C:\> $x.PagedMemorySize64 / 1KB
15093692
PS C:\> $x.PagedMemorySize64 / 1GB
14.3944664001465
And you can prove out that you need more than 32 bits of address space as easily as we did above:
PS C:\> [math]::log($x.PagedMemorySize64)/[math]::log(2)
33.847442404377
So my system needs 34 of the 64 bits to address all the memory it's using.
You can even see where the 2.39 GB number from above comes from with a little binary math:
PS C:\> ($x.PagedMemorySize64 -band [uint32]::MaxValue) / 1GB
2.39446640014648
Technically, they should have used unsigned 64-bit integers for PagedMemorySize64, but it's pretty unlikely that you'll need that much address space for the foreseeable future ([int64]::MaxValue / 1PB = 8,192 petabytes).

Resources