PowerShell Tools For Linux
In this issue:
- Get-LxUptime
- Get-LxTotalMemory
- Get-LxMemoryInfo
- Get-LxTotalDiskSize
- Get-LxDiskInfo
- Get-LxStatus
- Get-LxService
- Get-LxOSInfo
- Get-LxTools
- Aliases
- Summary
For me, one of the advantages of having PowerShell work cross-platform, is that I can use native PowerShell tools. My Linux skills are basic, so being able to use PowerShell to get information is a great timesaver. However, there are obvious limitations. PowerShell commands that have no basis for support are unavailable. This means no CIM cmdlets on Linux, and presumably MacOS.
> I don't have access to a Mac to test anything so I am going to assume that any statement I make regarding Linux applies to Macs.
That's not to say that CIM-like information isn't available. I can get information on a Linux host's memory, but it requires parsing to create an equivalent PowerShell object. And that's what I want. Yes, I know that Linux experts can easily parse. But I want an object that I can work with in a PowerShell pipeline.
I set out to create a set of PowerShell tools that can provide system information. I'll state up front that for a few commands I needed AI to help me use native Linux tools to parse the information. In some cases, I didn't even know where to look to find the information. I think this is a terrific use-case for AI in your scripting. Let it fill in the gaps and accelerate your work.
I thought I'd share how I worked through my toolset.
Get-LxUptime
Uptime information is stored in proc/uptime. The first value in the output is the number of seconds of uptime. AI gave me this code to parse the file.
cat /proc/uptime | cut -d. -f1
There's nothing wrong with that since the command is running in Linux. However, once I understood what information I needed, I could use a PowerShell equivalent.
$seconds = (Get-Content /proc/uptime).split(".")[0]
From here it is trivial to create the uptime.
$uptime = New-TimeSpan -Seconds $seconds
Get-LxTotalMemory
Memory information is stored in proc/meminfo. AI suggested I parse the file to get the total memory with this command:
$bytes = [int64](cat /proc/meminfo | grep MemTotal | awk '{print $2}') * 1024
The PowerShell equivalent is a little more complicated.
[int]$k = (Get-Content /proc/meminfo | Select-String MemTotal:).Line.Split().where({[int]($_ -match "\d+")})[0]
[int64]$bytes = $k * 1KB
Although I could have used a regular expression pattern.
Because the value in the file is stored as a KB value, multiplying the value by 1024 gives me the value in bytes. I always recommend providing values like this in bytes. Don't presume to know how the user might want to format the data. Let them do that. Although, I did add parameters to the function to let the user specify the format.
[int]$size = Switch ($as) {
"kb" { $bytes/1kb}
"mb" { $bytes/1mb}
"gb" {$bytes/1gb}
default {$bytes}
}
$size
Get-LxMemoryInfo
After looking at meminfo I realized there was more information I could return. The native Linux commands to parse the data are more succinct, so I decided not to spend time creating PowerShell alternatives.
$total = [int64](cat /proc/meminfo | grep MemTotal | awk '{print $2}') * 1024
$free = [int64](cat /proc/meminfo | grep MemFree | awk '{print $2}') * 1024
$avail = [int64](cat /proc/meminfo | grep MemAvailable | awk '{print $2}') * 1024
$Used = $total - $free
Once I have the data, I can easily create a custom object.
[PSCustomObject]@{
PSTypeName = 'lxMemoryInfo'
TotalSize = $total
FreeMemory = $free
InUseMemory = $used
Available = $avail
PctFree = [math]::Round(($free / $total) * 100,2)
Computername = [System.Environment]::MachineName
}
I provided a typename so that I could create a custom format file.
PS /home/jeff> Get-LxMemoryInfo
Hostname: Cadenza
MemoryGB FreeMemGB InUseGB PctFree
-------- --------- ------- -------
31 29.26 1.77 94.3
Get-LxTotalDiskSize
Likewise, I often want to get disk information. In Linux, the df command will provide the necessary information.
PS /home/jeff> df /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sdd 1055762868 14305796 987753600 2% /
I can parse this information to get the total disk size.
#stored size is in KB so convert it down to bytes
$bytes = [int64](df / | Select-Object -Skip 1 | ForEach-Object {
$fields = $_ -split '\s+'
$fields[1]
}) * 1024
[int64]$size = switch ($As) {
'kb' { $bytes / 1kb }
'mb' { $bytes / 1mb }
'gb' { $bytes / 1gb }
default { $bytes }
}
$size
The output is a single value
PS /home/jeff> Get-LxTotalDiskSize
1081101176832
PS /home/jeff> Get-LxTotalDiskSize -as gb
1007
As an alternative, I could parse the entire output of the df command. I can't treat the multiple spaces as a delimiter, but I can replace them with on and then convert from CSV data.
PS /home/jeff> (df).replace("Mounted on","Mounted") -replace "\s+","," | ConvertFrom-Csv | Format-Table
Filesystem 1K-blocks Used Available Use% Mounted
---------- --------- ---- --------- ---- -------
none 16268292 0 16268292 0% /usr/lib/modules/6.6.87.2-microsoft-standard-WSL2
none 16268292 4 16268288 1% /mnt/wsl
drivers 497775612 250245780 247529832 51% /usr/lib/wsl/drivers
/dev/sdd 1055762868 14305796 987753600 2% /
none 16268292 100 16268192 1% /mnt/wslg
none 16268292 0 16268292 0% /usr/lib/wsl/lib
rootfs 16254004 2664 16251340 1% /init
none 16268292 932 16267360 1% /run
none 16268292 0 16268292 0% /run/lock
none 16268292 0 16268292 0% /run/shm
none 16268292 76 16268216 1% /mnt/wslg/versions.txt
none 16268292 76 16268216 1% /mnt/wslg/doc
C:\ 497775612 250245780 247529832 51% /mnt/c
tmpfs 16268296 4 16268292 1% /tmp
tmpfs 1024 0 1024 0% /run/credentials/systemd-journald.service
tmpfs 1024 0 1024 0% /run/credentials/systemd-resolved.service
tmpfs 1024 0 1024 0% /run/credentials/console-getty.service
tmpfs 3253656 48 3253608 1% /run/user/1000
The heading has a space in the last column, so I strip that off first. Keep in mind that ConvertFrom-Csv will treat all values as strings which could effect any sorting you might want to do. That said, it wouldn't take that much effort to create a function to create typed PowerShell output from the df command.
Get-LxDiskInfo
It didn't take that much more effort to create a function to provide meaningful disk usage information
df / | Select-Object -Skip 1 | ForEach-Object {
$fields = $_ -split '\s+'
}
[int64]$total = [int32]$fields[1] * 1024
[int64]$free = [int32]$fields[3] * 1024
[PSCustomObject]@{
PSTypeName = 'lxDiskInfo'
TotalSize = $total
Used = ([int32]$fields[2] * 1024) -as [int64]
Free = $free
PctFree = [math]::Round(($free / $total) * 100, 2)
Computername = [System.Environment]::MachineName
}
This too I created a custom format file.
PS /home/jeff> Get-LxDiskInfo
Hostname: Cadenza
TotalDiskGB UsedDiskGB FreeDiskGB PctFree
----------- ---------- ---------- -------
1007 13.64 942.00 93.56