ForEach Synatax Error? Unexpected token 'in' in expression or statement.

This topic contains 10 replies, has 3 voices, and was last updated by Profile photo of Jonathon Olson Jonathon Olson 2 years, 6 months ago.

  • Author
    Posts
  • #15744
    Profile photo of Jonathon Olson
    Jonathon Olson
    Participant

    I am trying to create a script to get/change ACLs for folders named Administration. I can get the ACLs from the console with either of these commands:

    Get-ChildItem 'D:\data\Company Shared Folders\Operations\Current Jobs' -Recurse -Filter "Administration" | Where-Object {$_.mode -eq "d----"} | Convert-Path | get-acl
    Get-ChildItem 'D:\data\Company Shared Folders\Operations\Current Jobs' -Recurse -Filter "Administration" | Where-Object {$_.mode -eq "d----"} | Convert-Path | foreach {get-acl}
    

    But my script gives me the error in the title of this post:

    #get permissions for Administration folders
    $adminfolders = Get-ChildItem 'D:\data\Company Shared Folders\Operations\Current Jobs' -Recurse -Filter "Administration" | Where-Object {$_.mode -eq "d----"} | Convert-Path
    ForEach-Object ($adminfolder in $adminfolders){
            $acl = get-acl $adminfolder
        }
    

    I think I'm missing some concept of the foreach syntax, is $thing in $things correct? Must I define the $thing first?

    The type information from the ISE:
    $adminfolders | gm
    TypeName: System.String

    I'm trying to build this up to eventually modify the ACLs using the methods in Set-ACL.

    PSversion (SBS 2008)
    Major Minor Build Revision
    —– —– —– ——–
    2 0 -1 -1

  • #15745
    Profile photo of Don Jones
    Don Jones
    Keymaster

    In the ForEach-Object cmdlet, you use $_ or $PSItem (v3+) to represent the piped-in object.

    In the ForEach scripting construct, you use the ($x in $y) syntax.

    You're mixing and matching. In the script, you should be using ForEach, not ForEach-Object.

  • #15746
    Profile photo of Jonathon Olson
    Jonathon Olson
    Participant

    Thank you that was the concept I was missing. I had thought ForEach was simply an alias for ForEach-Object. When I changed ForEach-Object to ForEach I get the expected result.

  • #15747
    Profile photo of Don Jones
    Don Jones
    Keymaster

    Well, that's the thing. ForEach-Object does have an alias foreach. But neither of them are the foreach scripting construct ;).

  • #15748
    Profile photo of Matt McNabb
    Matt McNabb
    Participant

    Clarification: If 'Foreach' is at the beginning of a statement then it is the foreach loop keyword. If it is used after a pipe character it's interpreted as 'Foreach-Object'. It was a bit confusing for me at first too. I started a habit of never using the alias of Foreach-Object to prevent this confusion.

  • #15753
    Profile photo of Jonathon Olson
    Jonathon Olson
    Participant

    So if I understand using foreach in any instance should work, but foreach-object should not be used for a loop in a script. What about the @? Is it simply an alias for foreach? It is not listed by get-alias, but I do see it used often. How (literally) is it interpreted by powershell, both in the shell and in a script?

  • #15754
    Profile photo of Don Jones
    Don Jones
    Keymaster

    @ is an array operator, not an alias.

    And you can use the cmdlet in a script; it just needs something piped to it, and it's syntax is different. It has different performance characteristics.

  • #15755
    Profile photo of Matt McNabb
    Matt McNabb
    Participant

    There is no hard and fast rule that says you can't use Foreach-Object in a script, but it's generally considered a best practice. I have certainly used it in scripts but if you are processing a ton of data, particularly data that has already been collected and saved in a variable, then using the foreach keyword will generally yield better performance. The performance differences between the two have been hotly debated by people much smarter than me so don't take my word for it – this is just my preference.

    I am not certain what you mean by the '@'. Do you mean '%'? This is an alias for Foreach-Object and generally aliases should be avoided in scripts just for readability's sake. They mainly exist to reduce typing in the console. With tab expansion and Intellisense in the ISE I typically use full cmdlet names for scripts and in the console.

  • #15756
    Profile photo of Jonathon Olson
    Jonathon Olson
    Participant

    Thank you for the clarification, trying to avoid aliases in scripts was why I had used the full foreach-object. I was mixing % for @, since I have seen both used in scripts. And now I know that foreach-object will work in a script if it has pipleline input, but to use only "foreach" for a scriptblock. Is this correct?

  • #15764
    Profile photo of Matt McNabb
    Matt McNabb
    Participant

    Here's a demonstration that shows how to loop through a collection of files one at a time:

    foreach keyword:
    $Files = Get-ChildItem c:\data
    foreach ($File in $Files)
    {
    # do something with each file here
    }

    Foreach-Object cmdlet:
    Get-Childitem c:\data | Foreach-Object {# do something with each file here}

    Both the Foreach keyword and the Foreach-Object cmdlet take a scriptblock argument and perform that block of code against each item in an array.

    Here is a link to an old discussion of foreach vs. foreach-object that I believe is still relevant:
    http://bsonposh.com/archives/327

  • #15780
    Profile photo of Jonathon Olson
    Jonathon Olson
    Participant

    Thank you Don and Matt for your help. Posting my final script for anyone that might find it useful.


    #cache the desired permissions for use (this folder was configured correctly using gui)
    $perms = (get-item "D:\Data\Company Shared Folders\Operations\Administration").getaccesscontrol('Access')
    #set permissions for folders named Administration
    $adminfolders = Get-ChildItem 'D:\data\Company Shared Folders\Operations\Current Jobs' -Recurse -Filter "Administration" | Where-Object {$_.psiscontainer} | Convert-Path
    ForEach ($adminfolder in $adminfolders){
    Set-Acl -Path $adminfolder -AclObject $perms
    }

You must be logged in to reply to this topic.