SOLVED

newb question: why is a foreach Item different from a Get-Item Item?!?

%3CLINGO-SUB%20id%3D%22lingo-sub-1085059%22%20slang%3D%22en-US%22%3Enewb%20question%3A%20why%20is%20a%20foreach%20Item%20different%20from%20a%20Get-Item%20Item%3F!%3F%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1085059%22%20slang%3D%22en-US%22%3E%3CP%3EHere's%20my%20simple%20function%20test%20script%20and%20two%20use%20case%20tests%3A%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-markup%22%3E%3CCODE%3E%23%20test%20rig%20for%20new%20function%0A%0Afunction%20Test-FileNewerThanRefDate%7B%0A%20%20%20%20%23%20return%20True%20if%20%24vFile%20is%20new%20than%20%24vRefDate%2C%20else%20return%20False%0A%0A%20%20%20%20param(%0A%20%20%20%20%20%20%20%20%24vFile%2C%0A%20%20%20%20%20%20%20%20%5Bdatetime%5D%20%24vRefDate%0A%20%20%20%20)%0A%20%20%20%20if%20((%24vFile.get_LastWriteTime()%20-gt%20%24vRefDate)%20-or%20(%24vFile.get_CreationTime()%20-gt%20%24vRefDate))%20%7B%0A%20%20%20%20%20%20%20%20return%20%24true%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20return%20%24false%0A%20%20%20%20%7D%0A%0A%7D%0A%0A%24TestRefDate%3D%5Bdatetime%5D%229%2F30%2F2019%22%0A%0A%23%20CASE%20%231%0A%24Test1%3D%22%5C%5CDS218plus%5Chome%5Cother%20PSE%20catalogs%5CCMS%201976-7%5CCMS%20holiday%201976%2000.jpg%22%0A%24Test2%3D%22%5C%5CDS218plus%5Chome%5Cother%20PSE%20catalogs%5CCMS%201976-7%5CIMG-3679.JPG%22%0A%24Test3%3D%22%5C%5CDS218plus%5Chome%5Cother%20PSE%20catalogs%5CCMS%201976-7%5CThumbs.db%22%0A%0Aforeach%20(%24vTestCase%20in%20(%24Test1%2C%20%24Test2%2C%20%24Test3))%20%7B%0A%20%20%20%20if%20(Test-FileNewerThanRefDate%20(Get-Item%20%24vTestCase%20-Force)%20%24TestRefDate)%20%7B%0A%20%20%20%20%20%20%20%20Write-Host(%22newer%22)%0A%20%20%20%20%7D%0A%7D%0A%0A%23%20CASE%20%232%0A%24TestPath%3D%22%5C%5CDS218plus%5Chome%5Cother%20PSE%20catalogs%5CCMS%201976-7%5C%22%0A%0A%24vItems%20%3D%20Get-ChildItem%20-LiteralPath%20%24TestPath%20-Force%0A%0Aforeach%20(%24vItem%20in%20%24vItems)%20%7B%0A%0A%20%20%20%20if%20(-not%20(%24vItem.PSIsContainer))%20%7B%0A%20%20%20%20%20%20%20%20if%20(Test-FileNewerThanRefDate(%24vItem%2C%20%24TestRefDate))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Write-Host(%22%24vItem%20is%20newer%22)%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThe%20first%20use%20case%20works%20as%20desired%3B%20the%20function%20is%20correctly%20able%20to%20evaluate%20the%20creation%20and%20last%20write%20timestamps%20for%20the%20Get-Item()%20returned%20file.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThe%20second%20use%20case%20fails.%20For%20some%20reason%2C%20the%20foreach%20(%24vItem%20in%20%24vItems)%20of%20the%20Get-ChildItems()%20list%20of%20items%20is%20getting%20passed%20to%20the%20function%20as%20a%20%5BSystem.DateTime%5D%20not%20a%20file-like%20object.%20The%20resulting%20error%20is%3A%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-markup%22%3E%3CCODE%3EMethod%20invocation%20failed%20because%20%5BSystem.DateTime%5D%20does%20not%20contain%20a%20method%20named%20'get_LastWriteTime'.%0AAt%20C%3A%5CUsers%5Clgg%5CDesktop%5Ctest.ps1%3A10%20char%3A9%0A%2B%20if%20((%24vFile.get_LastWriteTime()%20-gt%20%24vRefDate)%20-or%20(%24vFile.get_Cr%20...%0A%2B%20%20%20%20%20~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EWhy%20is%20the%20foreach%20item%20a%20different%20type%20of%20object%20than%20the%20Get-Item()%20object%3F%20What%20is%20the%20correct%20way%20to%20do%20this%3F%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThanks%20in%20advance%20for%20any%20suggestions%20or%20advice!%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-LABS%20id%3D%22lingo-labs-1085059%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EWindows%20PowerShell%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1117295%22%20slang%3D%22en-US%22%3ERe%3A%20newb%20question%3A%20why%20is%20a%20foreach%20Item%20different%20from%20a%20Get-Item%20Item%3F!%3F%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1117295%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F503377%22%20target%3D%22_blank%22%3E%40D_Watson2185%3C%2FA%3E%26nbsp%3Bit%20looks%20like%20you're%20calling%20your%20function%20in%20two%20very%20different%2C%20but%20deceptively%20similar%20ways.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EFirst%2C%20let's%20take%20a%20closer%20look%20at%20the%20params%20on%20your%20function%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-markup%22%3E%3CCODE%3Eparam(%0A%20%20%20%20%24vFile%2C%0A%20%20%20%20%5Bdatetime%5D%20%24vRefDate%0A)%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%3CBR%20%2F%3ESo%20we're%20looking%20for%20two%20parameters%2C%20the%20first%20of%20which%20takes%20%3CEM%3Eanything%3C%2FEM%3E%20and%20the%20second%20should%20be%20a%20datetime%20(and%20if%20it%20isn't...%20PowerShell%20will%20try%20to%20cast%20it%20to%20one%20for%20you)%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EIn%20the%20sample%20that%20is%20working%2C%20you're%20passing%20the%20the%20variables%20to%20each%20of%20those%20parameters%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-markup%22%3E%3CCODE%3ETest-FileNewerThanRefDate%20(Get-Item%20%24vTestCase%20-Force)%20%24TestRefDate%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EOr%20seen%20another%20way%2C%20that's%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-markup%22%3E%3CCODE%3ETest-FileNewerThanRefDate%20%24vFile%20%24vRefDate%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThe%20second%2C%20failing%20sample%20is%20different%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-markup%22%3E%3CCODE%3ETest-FileNewerThanRefDate(%24vItem%2C%20%24TestRefDate)%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EWhat%20this%20actually%20doing%20is%20bundling%20up%20the%20two%20parameters%20as%20one%2C%20so%20both%26nbsp%3B%24vItem%20%26amp%3B%20%24TestRefDate%20are%20being%20past%20to%20the%20function's%20vFile%20parameter%20as%20one%20%22Object%22%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThe%20%22quick%20fix%22%20is%20to%20change%20it%20to%20not%20use%20those%20parentheses%20and%20especially%20the%20comma%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-markup%22%3E%3CCODE%3ETest-FileNewerThanRefDate%20%24vItem%20%24TestRefDate%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThis%20still%20doesn't%20sit%20well%20with%20me%2C%20as%20it%20relies%20on%20positioning%20and%20makes%20the%20function%20less%20maintainable%20later%20(e.g.%20adding%20a%20new%20parameter%20would%20break%20existing%20scripts%20that%20rely%20on%20it.)%20The%20better%20option%20is%20to%20specify%20the%20parameters%20by%20name%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-markup%22%3E%3CCODE%3ETest-FileNewerThanRefDate%20-vRefDate%20%24TestRefDate%20-vFile%20%24vItem%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%3CEM%3ENote%2C%20this%20allowed%20me%20to%20put%20the%20date%20first%2C%20and%20it%20still%20works.%3C%2FEM%3E%3C%2FP%3E%3C%2FLINGO-BODY%3E
Highlighted
Occasional Visitor

Here's my simple function test script and two use case tests:

 

 

 

 

# test rig for new function

function Test-FileNewerThanRefDate{
    # return True if $vFile is new than $vRefDate, else return False

    param(
        $vFile,
        [datetime] $vRefDate
    )
    if (($vFile.get_LastWriteTime() -gt $vRefDate) -or ($vFile.get_CreationTime() -gt $vRefDate)) {
        return $true
    } else {
        return $false
    }

}

$TestRefDate=[datetime]"9/30/2019"

# CASE #1
$Test1="\\DS218plus\home\other PSE catalogs\CMS 1976-7\CMS holiday 1976 00.jpg"
$Test2="\\DS218plus\home\other PSE catalogs\CMS 1976-7\IMG-3679.JPG"
$Test3="\\DS218plus\home\other PSE catalogs\CMS 1976-7\Thumbs.db"

foreach ($vTestCase in ($Test1, $Test2, $Test3)) {
    if (Test-FileNewerThanRefDate (Get-Item $vTestCase -Force) $TestRefDate) {
        Write-Host("newer")
    }
}

# CASE #2
$TestPath="\\DS218plus\home\other PSE catalogs\CMS 1976-7\"

$vItems = Get-ChildItem -LiteralPath $TestPath -Force

foreach ($vItem in $vItems) {

    if (-not ($vItem.PSIsContainer)) {
        if (Test-FileNewerThanRefDate($vItem, $TestRefDate)) {
            Write-Host("$vItem is newer")
        }
    }

}

 

 

 

 

The first use case works as desired; the function is correctly able to evaluate the creation and last write timestamps for the Get-Item() returned file.

 

The second use case fails. For some reason, the foreach ($vItem in $vItems) of the Get-ChildItems() list of items is getting passed to the function as a [System.DateTime] not a file-like object. The resulting error is:

 

 

 

 

Method invocation failed because [System.DateTime] does not contain a method named 'get_LastWriteTime'.
At C:\Users\lgg\Desktop\test.ps1:10 char:9
+ if (($vFile.get_LastWriteTime() -gt $vRefDate) -or ($vFile.get_Cr ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

 

 

 

Why is the foreach item a different type of object than the Get-Item() object? What is the correct way to do this?

 

Thanks in advance for any suggestions or advice!

1 Reply
Highlighted
Best Response confirmed by chrisream (Microsoft)
Solution

@D_Watson2185 it looks like you're calling your function in two very different, but deceptively similar ways.

 

First, let's take a closer look at the params on your function:

param(
    $vFile,
    [datetime] $vRefDate
)


So we're looking for two parameters, the first of which takes anything and the second should be a datetime (and if it isn't... PowerShell will try to cast it to one for you)

 

In the sample that is working, you're passing the the variables to each of those parameters:

Test-FileNewerThanRefDate (Get-Item $vTestCase -Force) $TestRefDate

 

Or seen another way, that's:

Test-FileNewerThanRefDate $vFile $vRefDate

 

The second, failing sample is different:

Test-FileNewerThanRefDate($vItem, $TestRefDate)

 

What this actually doing is bundling up the two parameters as one, so both $vItem & $TestRefDate are being past to the function's vFile parameter as one "Object"

 

The "quick fix" is to change it to not use those parentheses and especially the comma:

Test-FileNewerThanRefDate $vItem $TestRefDate

 

This still doesn't sit well with me, as it relies on positioning and makes the function less maintainable later (e.g. adding a new parameter would break existing scripts that rely on it.) The better option is to specify the parameters by name:

Test-FileNewerThanRefDate -vRefDate $TestRefDate -vFile $vItem

 

Note, this allowed me to put the date first, and it still works.