Objects, cli arg values are not directly accessed by the Where-Object Cmdlet, example's provided

While reading through this, please understand I am not asking for someone to fix my scribbling, I am asking for help to ‘understand’ the behavior, so I can stop tripping over myself. The following snippet works as expected.

Example-00:

$pnam0 = $args[0]
$pnam1 = $args[1]
$pnam2 = $args[2]
$pnam3 = $args[3]
$pnam4 = $args[4]

$Drvrs = Get-WindowsDriver -online -all | ? { $_.providername -like $pnam0 -or $_.providername -like $pnam1 -or $_.providername -like $pnam2 -or $_.providername -like $pnam3 -or $_.providername -like $pnam4 } | select-object driver, version, ProviderName, date | sort date -Descending

From a previous thread (below), it was explained a cmdlet output is a different type of object, compared to a “string” object.

Then I further read, from a Google provided “AI Overview” –

" In PowerShell, an argument before variable assignment is treated as a string.

Here’s why:
–PowerShell’s Dynamic Typing:** PowerShell is dynamically typed, meaning the type of a variable is determined at runtime based on the value assigned to it.
–Default String Interpretation:** If you don’t explicitly specify a type, PowerShell interprets unassigned variables as strings."

In regard to the above “AI Overview”, their use of the word ‘assignment’ seem’s to be used for assigning a data-type. My use of the word ;assignment’ (below), is in regard to assigning a value to a variable-name; please correct where needed.

The main confusion begins …

Example-01

$Drvrs = Get-WindowsDriver -online -all | ? { $_.providername -like $args[0] } | select-object driver, version, ProviderName, date | sort date -Descending

$Drvrs

PS> _Script *amd*

Above returns an empty prompt , compared to the following Example-01a:

$pnam0 = $args[0]
$pnam1 = $args[1]
$pnam2 = $args[2]
$pnam3 = $args[3]
$pnam4 = $args[4]

$Drvrs = Get-WindowsDriver -online -all | ? { $_.providername -like $pnam0 } | select-object driver, version, ProviderName, date | sort date -Descending

$Drvrs

PS> _Script *amd*

Driver      Version             ProviderName Date
------      -------             ------------ ----
oem65.inf   6.0.0.72            AMD          5/31/2023 12:00:00 AM
oem166.inf  6.0.0.42            AMD          5/25/2023 12:00:00 AM
oem71.inf   10.105.6.45         AMD          5/15/2023 12:00:00 AM
oem3.inf    6.0.6.7             AMD          5/4/2023 12:00:00 AM

Example-01a works as expected providing the above results.

I’m obviously not understanding both: a) if $args[0] is both a string argument and a variable, indicated by the preceding ‘$’, b) why is ? { $_.providername -like $args[0] }, from example-01 not expanding it, or otherwise seeing its value until after it is re-assigned; as in $pnam0 = $args[0]?

Please notify if I did not explain clear enough.

I’m not sure if this is really the best way for you to step forward. Instead of trying to understand bad examples like how the thing with the $args array works I’d recommend using a more professional way of passing arguments to functions or scripts. And that would be using “Advanced Parameters”.

My approach for something like this would look like this:

function Get-DriverByProvider {
    param(
        [String[]]$Providername,
        [String[]]$ExcludeProviderName = 'asdfghjkqwertz'
    )
    $ProviderCompareList = $Providername -join '|'
    $ExcludeList = $ExcludeProviderName -join '|'
    Get-WindowsDriver -online -all | 
    Where-Object { 
        $_.providername -match $ProviderCompareList -and 
        $_.providername -notmatch $ExcludeList 
    }  
}

I know that’s not what you asked for but I think it does not make any sense to waste time understanding what you’re not supposed to use anyway.

With this function you can use a list of providers you’re looking for and you can even exclude certain providers with similar names when you’re searching only with partial names. … like you’re looking for Advanced Micro Devices, Inc. but you only remember Micro but you don’t want to see all Microsoft drivers.

Get-DriverByProvider -Providername Micro -ExcludeProviderName Microsoft |
    Select-Object -Property Driver,Prov*,Version, Date

Or you know there is Technology or Technologies in the name.

Get-DriverByProvider -Providername Technolog |
    Select-Object -Property Driver,Prov*,Version, Date

And since you can provide a list of Providernames with [String[]] you could list AMD driver no matter if they are listed as AMD or Advanced Micro Devices, Inc.

Get-DriverByProvider -Providername 'Advanced Micro Devices',AMD |
    Select-Object -Property Driver,Prov*,Version, Date

Here you can (and should) read more about Advanced Parameters:

3 Likes

@Olaf – I’m still a PowerShell infant, have not mastered the basic (yet). ;-\

That’s fine. We all started once and have been at the same stage. :man_shrugging:t3:

So that’s the best occasion to learn how to do it the proper way. :wink:

Is the code you shared just an example or is this the task you’re working on? What’s the actual goal?

Using numbered command line arguments comes with so many disadvantages that I’d like to strongly encourage you not to use this approach.

you’re doing fine. I think what @Olaf is saying is to avoid trying to use the automatic variable $args and use a more “best practice” approach like using a param block in your script.
Here’s another example like Olaf’s. I’m naming my parameter ProviderName and I switched the where-object statement to use the match operator instead of like because I won’t need to provide wildcards to make it work
Get-VendorDriver.ps1

param (
    $ProviderName
)

Get-WindowsDriver -online -all | ? { $_.providername -match $ProviderName } | select-object driver, version, ProviderName, date | sort date -Descending

Would be ran like:

PS > ./Get-VendorDriver.ps1 -ProviderName amd

I’m not on a Windows machine at the moment so I can’t show the results.
I’ve had to untangle a bunch of scripts at work where someone used $args in production and it was a mess. It’s way easier to understand/manage.

I feel a need to clarify. What I was trying to say was that ALL Powershell input/output is objects. Different cmdlets/functions return different types of objects.

1 Like

@Olaf – The code I submitted is an active task being used to get a hold on PS basic’s

Using numbered command line arguments comes with so many disadvantages that I’d like to strongly encourage you not to use this approach. I’ll assume “numbered command line arguments” is referring to passing PS> _script arg, arg, arg, if not please explain. If I assume correctly, that method stuck with once I learned how with cmd.exe.

I work with files and folders that are named yyyymmdd -and- yyyymmddhhmmss. Those are the pattern’s, with and without wildcard’s, that I pass from the cli to my tiny script’s. The goal: to slowly try and migrate from cmd.exe to PS --by first getting a grip on the help-system while simultaneously figuring out how to replace my existing batch file’s with ps1.

The GUI can really be a bit of a chore working with multiple dialogs, --when wanting to view more than three different drivers installed, as well as ‘Event Logs’= search, select then export --each time you need to pull a subset of log entries. I enjoyed Unix variants, but the Software I use only like MS and Mac.

I’m do not yet see myself doing any heavy lifting with PS; no plan’s for a career that involves PS.

@grey0ut – I did understand that :slight_smile:

@grey0ut – No additional clarification was needed, I posted that in an effort to show how my brain was interpreting your post compared to the “Google AI Overview” with its explanation, including the word ‘dynamic’ and its behavior.

Continued Respect

It’s actually not the way you call it - it’s more the way you need to handle it inside your scripts what makes it confusing and error prone - but “yes” that’s what I meant.

PowerShell is not like CMD. It’s like you compare a rotary dial phone with a current smartphone. :man_shrugging:t3:

And what’s the issue with that? :thinking: You make a lot of vague references without getting specific. :man_shrugging:t3:

That comes by itself on the way … don’t waste a lot of time on that.

Since PowerShell is way more advanced/sophisticated you may start over with a fresh approach instead of trying to migrate an old logic.

What GUI? Why do you need miltiple dialogs? Why do you need to view drivers?

I’d urgently recommend to focus on one issue at a time only!

You know you can run Linux on Windows, don’t you? And Mac OS actually is a kind of Unix. What software are we talking about?

The funny thing on that is that with PowerShell you actually don’t need much to do exactly this.

@Olaf With the above two Item’s, a) I was not intending to compare, but rather trying to do the same thing in PS, which you have pointed out, is not the best way when using PS. b) My “vague references” is my sad attempt at shortening the text. c) When I mentioned the pattern’s I work with along with “Event Log’s” and searching for multiple drivers using the Windows GUI = those are my current goal’s with PS.

I most likely will do just that, once I can grasp enough info about operators, script blocks and other notations; then at least remember where the reference material is.

Windows can replace OEM driver’s without warning, because of that I had issue’s. Before using PS, I would use “Windows\Device Manager” and its pop-up dialogs to get driver version’s of installed driver’s via a screenshot, then submit along with other ComputerInfo to generate an OEM ticket. PS retrieves the data in one run, less time, smaller file sizes, without the need to screenshot.

Perhap’s that may help, but there are other unrelated item’s that distract from focusing on PS. Then when I return to PS, I jump in at whatever the priority is at that moment; if I can remember. :-\

I’ve never played with Linux, these machine’s do not yet have enough ram. The software is a proprietary VMS for camera’s; it is a memory hog.

I think I kind-of picked-up on that, but sometimes I feel the full Cmdlet name’s, albeit more descriptive and easier to remember, are causing more keystrokes, until I begin using the aliases. I have already learned --using aliases may not be the best thing to do in scripts when trying to return later, then read your own work; regardless of how small/short.

OK, but that’s not helpful when it lacks details, context or the connecting links. :point_up:t3: :wink:

You may keep in mind: the more specific your question is the better the help will be you get in forums like this.

Cool … so the function I tinkered together in a few minutes should already be helpful for your task … great. :partying_face: :love_you_gesture:t3:

At least until you know the language good enough it’s probably easier to use a simple internet search. For the vast majority of the cases you’re not the very first one anyway. So it is pretty likely that you find something you can adapt easily to your particular needs.

You may use tab completion to reduce key strokes. And it prevents typos as well.

Of course at the end of the day it’s a matter of personal preference but it’s best practice for a good reason to use aliasses only in interactive sessions and not in scripts.

@Olaf – Honestly, I have only reviewed your function; not yet used it. There are a few question’s I need answered, by way of the help-system. You and @grey0ut have provided quite enough with your guidance; thank you.

[quote=“Olaf, post:9, topic:25200, full:true”]

It’s kind-of funny/strange that you said (above), I remember reading in 4E, it is recommended to shy, or otherwise, stay away from Google searches while trying to truly learn PS. I have been using that method, I.E., search and find code that I could adapt, then further search the syntax part’s I did not understand.

I was\am trying to actually learn PS, from the ground up, using MOL both the third and fourth edition’s, with additional supported from powershell.org. I still wind-up back in Google seeking an explanation in a way I can easily digest. Example, the books and some MVA video’s use the word ‘Culture’. I had never seen that word used with VB or batch scripting; if you catch my drift. :-/

Thanks for reminding me, I do remember Jason H. telling us to get used to having our pinky hover over the Tab-key; the Tab-key (completion) is everyone’s friend. Something I do need to get used to using.

For what Its worth, the initial code I posted is now considered a stable draft. I have currently settled on the following:

Set-ExecutionPolicy -ExecutionPolicy remotesigned -Scope currentuser

$myHline = [PSCustomObject]@{
    Hline = "============================================="
    }

$pnam0 = $args[0]
$pnam1 = $args[1]
$pnam2 = $args[2]
$pnam3 = $args[3]
$pnam4 = $args[4]

$MachInfo = Get-ComputerInfo OsLastBootUpTime, Osuptime, csmodel, biosseralnumber, BiosSMBIOSBIOSVersion, "OsName", "OSDisplayVersion", "OsVersion", osinstalldate
$Drvrs = Get-WindowsDriver -online -all | ? { $_.providername -like $pnam0 -or $_.providername -like $pnam1 -or $_.providername -like $pnam2  -or $_.providername -like $pnam3 -or $_.providername -like $pnam4 } | select-object driver, version, ProviderName, date | sort date -Descending | format-table

$MachInfo
$Drvrs | out-host

The code above will output to console, from that point I will manually export from the terminal. In another post I was trying to toss the scripts output >> html; the 5.1 cmdlet has a know bug. To use csv I believe I’ll need to learn ‘advanced parameters’, as the output from both the cmdlet’s, included in the above script, have different `properties’ (headers). PDF is not native to PS 5.1.

This script is not intended for daily use, more likely on an ‘as needed’ basis, for a comparison should I decided to re-enable “Windows Update”. Run the script before and after the update process in order to check whether the update process replaced one or more drivers.

I will also, after the required reading about function’s and advanced parameter’s, dig-in and merge or replace the above draft with your previously posted approach. Another key goal for me is actually understanding the verbiage being used in the publication’s. At some point I will stick my fingers into VS Code as previously suggested.
:+1:

I’m sorry I must be missing something critical. What is _Script? How does the code examples you showed result in anything? Is there a missing function definition? Or is this a script? The issue with your specific example is $args[0] no longer contains what you think it contains, as that automatic variable is empty in the Where-Object scriptblock. (Another point, don’t use aliases in any scripts, or at least in code you are sharing. In the interactive shell, it’s fine. ? is an alias for Where-Object)

You can test this by adding an output in the Where-Object scriptblock.

function _Script {
$Drvrs = Get-WindowsDriver -online -all | ? {Write-Host "args[0] is $($args[0])"; $_.providername -like $args[0] } | select-object driver, version, ProviderName, date | sort date -Descending

$Drvrs

}

Which will output a line showing the empty args[0] as many times as you have drivers returned. Why is it empty? Because each scriptblock, function, script each have their own automatic $args variable. Your function/script has $args[0] populated and then you assign that to a variable. That variable stays defined in the child scriptblock, where the $args for that scriptblock is empty.

I agree with Olaf to not depend on the automatic variable $args, and instead use named variables. In this case, named variables would’ve worked in both examples.

Maybe these examples might be useful to your learning path.

function _Script {

    Write-Host "Top level args0 : $($args[0])"

    $scriptblock = {
        Write-Host "Next level args0: $($args[0])"
    }
    
    . $scriptblock

    Write-Host "Top level args0 after scriptblock: $($args[0])"

    $scriptblock = {
        Write-Host "Next level args0 with argument: $($args[0])"
    }

    . $scriptblock $args[0]

    $scriptblock = {
        Write-Host "Next level args with arguments: $($args)"
    }

    . $scriptblock 'args','contains','all','arguments',$args[0]

}

_Script test

Top level args0 : test
Next level args0:
Top level args0 after scriptblock: test
Next level args0 with argument: test
Next level args with arguments: args contains all arguments test

Named parameters

function _Script {
    Param($toplevel)
    Write-Host "Top level: $toplevel"

    $scriptblock = {
        Param($nextlevel)
        Write-Host "Next level: $nextlevel"
    }
    
    . $scriptblock $toplevel

}

_Script test

Top level: test
Next level: test
1 Like