Skip to content

Instantly share code, notes, and snippets.

@sba923
Last active July 28, 2024 11:13
Show Gist options
  • Save sba923/d0d5ad16f2b12d7785adf830b0395dc2 to your computer and use it in GitHub Desktop.
Save sba923/d0d5ad16f2b12d7785adf830b0395dc2 to your computer and use it in GitHub Desktop.
*nix work-alike's for PowerShell: df, dirname, whatis, whence
# this is one of Stéphane BARIZIEN's public domain scripts
# the most recent version can be found at:
# https://gist.github.com/sba923/d0d5ad16f2b12d7785adf830b0395dc2#file-df-ps1
function Format-AsKMG
{
param ($bytes,$precision='0')
foreach ($i in ("","KB","MB","GB","TB"))
{
if (($bytes -lt 1000) -or ($i -eq "TB"))
{
$bytes = ($bytes).tostring("F0" + "$precision")
if ($i -eq '')
{
return $bytes
}
else
{
return $bytes + " $i"
}
}
else
{
$bytes /= 1KB
}
}
}
Get-CimInstance -ClassName win32_logicaldisk | `
Select-Object DeviceID, VolumeName, Size, Freespace | `
ForEach-Object {
$psdrive = Get-PSDrive -name $_.DeviceID.Substring(0,1) -ErrorAction SilentlyContinue | Where-Object { $_.Name -ceq $_.Name.ToUpper() }
try
{
# work around issue in PowerShell 7.4 where the mount point is not returned correctly for network-mounted drive letters
# see https://github.com/PowerShell/PowerShell/issues/19903
if ($psdrive.DisplayRoot -match 'System\.Span')
{
$uncpath = @((net use) -match ('\s+' + $psdrive.Name + ':\s+.*Microsoft Windows Network'))[0] -replace ('.*' + $psdrive.Name + ':\s+(\\\\.*\S)\s+Microsoft Windows Network'), '$1'
Add-Member -InputObject $_ -MemberType NoteProperty -Name MountedOn -Value $uncpath
}
elseif ($psdrive.DisplayRoot -ne ($psdrive.Name + ':\'))
{
# work around issue in PowerShell 7.4.1 where the DisplayRoot property is "padded with NULs"
# see https://github.com/PowerShell/PowerShell/issues/21064
$displayroot = $psdrive.DisplayRoot
if ($displayroot.Contains([char]0))
{
$displayroot = $displayroot.Substring(0,$displayroot.IndexOf([char]0))
}
Add-Member -InputObject $_ -MemberType NoteProperty -Name MountedOn -Value $displayroot
}
else
{
Add-Member -InputObject $_ -MemberType NoteProperty -Name MountedOn -Value $null
}
}
catch
{
Add-Member -InputObject $_ -MemberType NoteProperty -Name MountedOn -Value $null
}
$_
} | `
Format-Table -AutoSize `
DeviceID, `
VolumeName, `
MountedOn, `
@{Name='Size'; Expression={Format-AsKMG -bytes $_.size -precision 1}; Align='right'}, `
@{Name='Freespace'; Expression={Format-AsKMG -bytes $_.Freespace -precision 1}; Align='right'}, `
@{Name='PercentFree'; Expression={"{0:n2}" -f ($_.freespace / $_.size * 100)}; Align='right'}, `
@{Name='PercentUsed'; Expression={"{0:n2}" -f ((1-($_.freespace / $_.size)) * 100)}; Align='right'}
$null = $true
# this is one of Stéphane BARIZIEN's public domain scripts
# the most recent version can be found at:
# https://gist.github.com/sba923/d0d5ad16f2b12d7785adf830b0395dc2#file-dirname-ps1
param([string] $literalpath)
try
{
$item = Get-Item -LiteralPath $literalpath -ErrorAction SilentlyContinue
$parent = Split-Path -Path $item.FullName -Parent
$parent
}
catch
{
$null
}
# this is one of Stéphane BARIZIEN's public domain scripts
# the most recent version can be found at:
# https://gist.github.com/sba923/d0d5ad16f2b12d7785adf830b0395dc2#file-whatis-ps1
param([string] $Name, [switch] $ReturnPath, [switch] $Quiet)
Set-StrictMode -Version Latest
#region Get-ExecutableType
# derived from https://gallery.technet.microsoft.com/scriptcenter/Identify-16-bit-32-bit-and-522eae75
function Get-ExecutableType
{
<#
.Synopsis
Determines whether an executable file is 16-bit, 32-bit or 64-bit.
.DESCRIPTION
Attempts to read the MS-DOS and PE headers from an executable file to determine its type.
The command returns one of four strings (assuming no errors are encountered while reading the
file):
"Unknown", "16-bit", "x86", "x64", "ARM64", "ARM", "IA64" or if another PE machine field value, 0xNNNN
.PARAMETER Path
Path to the file which is to be checked.
.EXAMPLE
Get-ExecutableType -Path C:\Windows\System32\more.com
.INPUTS
None. This command does not accept pipeline input.
.OUTPUTS
String
.LINK
http://msdn.microsoft.com/en-us/magazine/cc301805.aspx
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[ValidateScript( { Test-Path -LiteralPath $_ -PathType Leaf })]
[string]
$Path
)
try
{
try
{
$stream = New-Object System.IO.FileStream(
$PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path),
[System.IO.FileMode]::Open,
[System.IO.FileAccess]::Read,
[System.IO.FileShare]::Read
)
}
catch
{
throw "Error opening file $Path for Read: $($_.Exception.Message)"
}
$exeType = 'Unknown'
if ([System.IO.Path]::GetExtension($Path) -eq '.COM')
{
# 16-bit .COM files may not have an MS-DOS header. We'll assume that any .COM file with no header
# is a 16-bit executable, even though it may technically be a non-executable file that has been
# given a .COM extension for some reason.
$exeType = '16-bit'
}
$bytes = New-Object byte[](4)
if ($stream.Length -ge 64 -and
$stream.Read($bytes, 0, 2) -eq 2 -and
$bytes[0] -eq 0x4D -and $bytes[1] -eq 0x5A)
{
$exeType = '16-bit'
if ($stream.Seek(0x3C, [System.IO.SeekOrigin]::Begin) -eq 0x3C -and
$stream.Read($bytes, 0, 4) -eq 4)
{
if (-not [System.BitConverter]::IsLittleEndian) { [Array]::Reverse($bytes, 0, 4) }
$peHeaderOffset = [System.BitConverter]::ToUInt32($bytes, 0)
if ($stream.Length -ge $peHeaderOffset + 6 -and
$stream.Seek($peHeaderOffset, [System.IO.SeekOrigin]::Begin) -eq $peHeaderOffset -and
$stream.Read($bytes, 0, 4) -eq 4 -and
$bytes[0] -eq 0x50 -and $bytes[1] -eq 0x45 -and $bytes[2] -eq 0 -and $bytes[3] -eq 0)
{
$exeType = 'Unknown'
if ($stream.Read($bytes, 0, 2) -eq 2)
{
if (-not [System.BitConverter]::IsLittleEndian) { [Array]::Reverse($bytes, 0, 2) }
$machineType = [System.BitConverter]::ToUInt16($bytes, 0)
switch ($machineType) # see https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
{
0x014C { $exeType = 'x86' }
0x0200 { $exeType = 'IA64' }
0x8664 { $exeType = 'x64' }
0xAA64 { $exeType = 'ARM64' }
0x01C4 { $exeType = 'ARM' }
default { $exeType = ('0x{0:X4}' -f $machineType) }
}
}
}
}
}
return $exeType
}
catch
{
throw
}
finally
{
if ($null -ne $stream) { $stream.Dispose() }
}
}
#endregion
$gcm = $null
try
{
$gcm = @(Get-Command -Name $Name -ErrorAction SilentlyContinue -All)
}
catch
{
$gcm = $null
}
if ($null -eq $gcm -or $gcm.Count -eq 0)
{
if (!$Quiet)
{
Write-Host("'{0}' is not a known command" -f $Name)
}
else
{
$null
}
Exit(1)
}
if ($gcm.Count -gt 1)
{
Write-Host("WARNING: there is more than one resolution for '{0}', using the default one as reported by Get-Command" -f $Name)
$gcm = Get-Command -Name $Name -ErrorAction SilentlyContinue
}
else
{
$gcm = $gcm[0]
}
$cmdtype = $gcm.CommandType
if (($cmdtype -eq 'Application') -and ($gcm.Path -match '\.cmd$'))
{
$cmdtype = 'CMD script'
}
$cmdtype_s = $cmdtype.ToString().ToLower()
if ($cmdtype_s[0] -in @('a', 'e', 'i', 'o', 'u'))
{
$article = 'an'
}
else
{
$article = 'a'
}
if ($cmdtype -eq 'CMD script')
{
$cmdtype_s = $cmdtype
}
if (!$ReturnPath)
{
Write-Host("'{0}' is {1} {2}" -f $Name, $article, $cmdtype_s)
}
switch ($cmdtype)
{
'Application'
{
if (!$ReturnPath)
{
$itempath = $gcm.Path
$item = Get-Item -LiteralPath $itempath
if ($item.Length -ne 0)
{
Write-Host("Executable path is '{0}'" -f $gcm.Path)
Write-Host("Description: '{0}'" -f $item.VersionInfo.FileDescription)
Write-Host("Product version: '{0}'" -f $item.VersionInfo.ProductVersion)
Write-Host("Executable type: {0}" -f (Get-ExecutableType -Path $itempath))
}
else
{
if ($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint)
{
if ($null -ne $item.Target)
{
$target = Get-Item -LiteralPath $item.Target -ErrorAction SilentlyContinue
if ($null -ne $target)
{
Write-Host("Executable path is '{0}'" -f $item.Target)
Write-Host("Description: '{0}'" -f $target.VersionInfo.FileDescription)
Write-Host("Product version: '{0}'" -f $target.VersionInfo.ProductVersion)
Write-Host("Executable type: {0}" -f (Get-ExecutableType -Path $item.Target))
}
else
{
Write-Error ("Cannot access reparse point target '{0}'" -f $item.Target)
}
}
else
{
Write-Error ("Cannot determine target for AppX reparse point '{0}'" -f $item.FullName)
}
}
else
{
Write-Host -ForegroundColor Magenta ("'{0}' is a 0-byte file!!!???" -f $gcm.Path)
}
}
}
else
{
$itempath = $gcm.Path
$item = Get-Item -LiteralPath $itempath
if ($item.Length -ne 0)
{
$gcm.Path
}
else
{
if ($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint)
{
if ($null -ne $item.Target)
{
$target = Get-Item -LiteralPath $item.Target -ErrorAction SilentlyContinue
if ($null -ne $target)
{
$item.Target
}
else
{
Write-Error ("Cannot access reparse point target '{0}'" -f $item.Target)
$null
}
}
else
{
Write-Error ("Cannot determine target for AppX reparse point '{0}'" -f $item.FullName)
$null
}
}
else
{
Write-Host -ForegroundColor Magenta ("'{0}' is a 0-byte file!!!???" -f $gcm.Path)
$null
}
}
}
}
'Alias'
{
if (!$ReturnPath)
{
Write-Host("Resolves to: '{0}'" -f $gcm.Definition)
}
}
'Cmdlet'
{
if (($gcm.Module -ne '') -and ($gcm.Module -notmatch '^Microsoft\.PowerShell'))
{
if (!$ReturnPath)
{
Write-Host("Implemented in module '{0}'" -f $gcm.Module)
}
}
}
'ExternalScript'
{
if (!$ReturnPath)
{
Write-Host("Path is '{0}'" -f $gcm.Path)
}
else
{
$gcm.Path
}
}
'Function'
{
if ($ReturnPath)
{
"function:{0}" -f $Name
}
}
'CMD script'
{
if (!$Name)
{
Write-Host("Script path is '{0}'" -f $gcm.Path)
}
else
{
$gcm.Path
}
}
}
# this is one of Stéphane BARIZIEN's public domain scripts
# the most recent version can be found at:
# https://gist.github.com/sba923/d0d5ad16f2b12d7785adf830b0395dc2#file-whence-ps1
param([string] $Name, [switch] $quiet)
. whatis.ps1 -Name $Name -ReturnPath -Quiet:$quiet
@sba923
Copy link
Author

sba923 commented Mar 10, 2022

Hi everyone,

Coming from a long Korn Shell history (with a lot of (using, beta-testing, advocacy for) MKS in it @BrucePay 😋), I used to miss some key tools everyday, so I wrote crude versions (as scripts, not yet modules or anything fancy).

For now I have:

  1. dirname: enables oneliners such as ii (dirname (dirname (gmo oh-my-posh).Path))
  2. basename
  3. whence: for instance, makes it a snap to edit a script you've just run and had an issue with:

code (whence Set-WindowsTerminalProgressState)

(where code resolves to my own code.ps1, but that's another discussion altogether)

  1. whatis: outputs information about a command in a human-readable format, adding value on top of Get-Command in the process, such as version information for AppX reparse points:
PS❯ whatis whatis
'whatis' is an externalscript
Path is 's:\PowerShell\whatis.ps1'


PS❯ whatis swtps
'swtps' is an alias
Resolves to: 'Set-WindowsTerminalProgressState'


PS❯ whatis winget
'winget' is an application
Executable path is 'C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.11.10191.0_x64__8wekyb3d8bbwe\AppInstallerCLI.exe'
Description: 'AppInstallerCLI.exe'
Product version: '1.11.210119001-AppxClickHandlerstor'
Executable type: 64-bit


PS❯ whatis procexp
'procexp' is an application
Executable path is 'c:\usr\wbin\procexp.exe'
Description: 'Sysinternals Process Explorer'
Product version: '16.32'


PS❯ whatis runprocexp
'runprocexp' is a CMD script
c:\usr\wbin\runprocexp.cmd


PS❯ whatis sc
'sc' is an application
Executable path is 'C:\WINDOWS\system32\sc.exe'
Description: 'Service Control Manager Configuration Tool'
Product version: '10.0.19041.1'
Executable type: 64-bit


PS❯ whatis gc
'gc' is an alias
Resolves to: 'Get-Content'


PS❯ whatis Get-Content
'Get-Content' is a cmdlet

you get the idea

  1. df: outputs human-readable information about local and network-mounted drives:

PS❯ df

DeviceID VolumeName         MountedOn        Size Freespace PercentFree PercentUsed
-------- ----------         ---------        ---- --------- ----------- -----------
C:       <REDACTED>                         857.9 GB  230.8 GB       26.90       73.10
D:       <REDACTED>_D                         1.8 TB  237.5 GB       12.75       87.25
E:
F:
G:
H:
I:       WD4TB_202103211456                3.6 TB    3.1 TB       85.11       14.89
L:       HGST4000-35                       3.6 TB    2.2 TB       61.19       38.81
S:       sto                \\pnjnas\sto   3.6 TB    1.4 TB       39.41       60.59
V:       V                  \\pnjnas\V     3.6 TB    1.4 TB       39.41       60.59

@sba923
Copy link
Author

sba923 commented Mar 10, 2022

FWIW: The discussion at PowerShell/PowerShell#15079 didn't end up anywhere 😢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment