How does the Get-ChildItem -Exclude Parameter work? - powershell

How does the Get-ChildItem -Exclude parameter work? What rules does it follow?
The Get-Help for Get-ChildItem isn't detailed at all:
Omits the specified items. The value of this parameter qualifies the
Path parameter. Enter a path element or pattern, such as "*.txt".
Wildcards are permitted.
And on Stackoverflow and elsewhere the general consensus seems to be it's too difficult to use and we should all just pipe the output of Get-ChildItem to Where-Object instead.
While I'm willing to use Where-Object I'm curious as to the rules -Exclude follows.
For example, I have a folder with the following sub-folders:
HsacFixtures
HsacFixturesBuild
RestFixture
RestFixtureBuild
If I execute the following command:
Get-ChildItem $rootFolderPath -Exclude HsacFixturesBuild -Directory
it returns the results expected:
HsacFixtures
RestFixture
RestFixtureBuild
However, if I add a -Recurse parameter:
Get-ChildItem $rootFolderPath -Exclude HsacFixturesBuild -Directory -Recurse
Then it returns sub-folders in the HsacFixturesBuild folder.
I've also tried HsacFixturesBuild\ and HsacFixturesBuild\*, which have the same results.
So does -Exclude only apply to immediate children, and not to grand-children or deeper sub-folders?

Exclude omits child objects based on the Name,
For files, gets the name of the file. For directories, gets the name
of the last directory in the hierarchy if a hierarchy exists.
Otherwise, the Name property gets the name of the directory
not the FullName,
which gets the full path of the directory or file
So even though the object is a grandchild in a recursive call, the exclude only looks at the object's Name, not the FullName so the exclude wont affect omission unless the child objects share a common substring of the name that happens to be part of the exclude parameter
Source Get-ChildItem
Example 3: Get all child items using an inclusion and exclusion
This command lists the .txt files in the Logs subdirectory, except for
those whose names start with the letter A. It uses the wildcard
character (*) to indicate the contents of the Logs subdirectory, not
the directory container. Because the command does not include the
Recurse parameter, the command does not include the content of
directory automatically; you need to specify it.
Windows PowerShell
PS C:\> Get-ChildItem –Path "C:\Windows\Logs\*" -Include "*.txt" -Exclude "A*"

With respect to Get-ChildItem,-exclude parameter works on the objects name
It could be understood better from the following example:
Consider the following folder structure
First we use -exclude parameter without recurse.
As we could see in the above image, based on objects name , folder was excluded
Now we add recurse parameter to the above statement as follows
Now we see could see that sub-folders of folder is still present, because the exclusion was applied at objects name
Hope this HElps.

Related

Pulling a dir and all its contents from every client on a domain and copy to file share?

I am looking to copy a bunch of dir's "C:\Users\userOne\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates" and all of their contents onto a share with the folder being named the client and the user name.
I am sure a for loop is the best way to go about this but I am hung up on what variables to use to get what I want.
The array will surely contain a list of every workstation on the AD, but I need to specify a bunch of directories per workstation. Maybe a wildcard like this will work? C:\Users*\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates
I've seen tons of useful examples on here but most of them involve copying from one location to many instead of my situation where its from many to one.
This is my first post, thanks in advance for the help!
Yes, you can use ...\*\... in a wildcard-based path to represent all directories at a given level of a directory hierarchy.
In your scenario, you could so something like the following (using a local path on a single machine for simplicity) - be sure to run the command elevated (as administrator) so you're permitted to access other users' home directories:
Get-ChildItem C:\Users\*\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates |
Select-Object #{ name='User'; expression={ ($_.FullName -split '\\')[2] } }, FullName |
ForEach-Object {
Copy-Item -WhatIf -Recurse -Force -LiteralPath $_.FullName "\\some\share\$($_.User)"
}
Get-ChildItem C:\Users\*\... implicitly loops over all user home directories and returns the directory at the specified remaining path for each, if present.
The Select-Object call transforms the System.IO.DirectoryInfo instances emitted by Get-ChildItem into custom objects with .FullName (the full directory path) and .User properties (the username implied by the path), by way of a calculated property.
The ForEach-Object call then uses Copy-Item to copy each directory to a destination directory on the target share named for the user; -Recurse copies the directory and all its contents (the directory's whole subtree), and -Force includes hidden items that would be excluded by default.
The -WhatIf common parameter in the Copy-Item command previews the operation. Remove -WhatIf once you're sure the operation will do what you want.

Why get-childitem with wildcards are not easy to use? [duplicate]

From documentation:
-Include
Retrieves only the specified items.
The value of this parameter qualifies
the Path parameter. Enter a path
element or pattern, such as "*.txt".
Wildcards are permitted.
The Include parameter is effective only when the command
includes the Recurse parameter or the
path leads to the contents of a
directory, such as C:\Windows*, where
the wildcard character specifies the
contents of the C:\Windows directory.
My first understanding was:
c:\test\a.txt
c:\test\b.txt
So to get 'a.txt' and 'b.txt' I can write:
gci -Path "c:\test\*" -Include "*.txt"
And this works. But now consider such hierarchy:
c:\test\a.txt
c:\test\b.txt
c:\test\c.txt\c.txt
The same command returns:
a.txt, b.txt, c.txt
The actual logic seems to be:
-Include used to match all entities specified by -Path. If matched element
is a file - return it. If matched
element is a folder, look inside and
return matching first level children.
Also, the documentation say:
The Include parameter is effective only when the command
includes the Recurse parameter or the
path leads to the contents of a
directory...
This is wrong as well. E.g.
gci -Path "c:\test" -Include "*.txt"
It returns nothing, while without -Include I get folder content. So -Include is definitely "effective". What really happens here? The -Path specify the "c:\test", and the -Include tries to match this path. As "*.txt" does not match "test", so nothing returned. But look at this:
gci -Path "c:\test" -Include "*t"
It returns a.txt, b.txt and c.txt as "*t" matched "test" and matched all child items.
After all, even knowing how Include works now, I don't understand when to use it. Why do I need it look to inside subfolders? Why should it be so complex?
You're confusing the use of -include. The -include flag is applied to the path, not the contents of the path. Without the use of the recursive flag, the only path that is in question is the path you specify. This is why the last example you gave works, the path c:\test has a t in the path and hence matches "*t".
You can verify this by trying the following
gci -path "c:\test" -in *e*
This will still produce all of the children in the directory yet it matches none of their names.
The reason that -include is more effective with the recurse parameter is that you end up applying the wildcard against every path in the hierarchy.
Try the -filter parameter (it has support for only one extension):
dir -filter *.txt
Tacking on to JaredPar's answer, in order to do pattern matching with Get-ChildItem, you can use common shell wildcards.
For example:
get-childitem "c:\test\t?st.txt"
where the "?" is a wildcard matching any one character or
get-childitem "c:\test\*.txt"
which will match any file name ending in ".txt".
This should get you the "simpler" behavior you were looking for.
I just asked a similar question and got three quick replies concerning the Get-Help for Get-ChildItem.
The answer is in the full description
of the command (Get-Help Get-ChildItem
-full):
The Include parameter is effective only when the command includes the
Recurse parameter or the path leads to
the contents of a directory, such as
C:\Windows*, where the wildcard
character specifies the contents of
the C:\Windows directory.
So the following would work without
recurse.
PS C:\foo> Get-childitem -path
"c:\foo*" -Include *.txt
From Stack Overflow question PowerShell Scripting - Get-ChildItem.
I hope this helps :-)
Including \* at the end of the path should work around the issue
PS C:\logfiles> Get-ChildItem .\* -include *.log
This should return .log files from the current working directory (C:\logfiles)
Alex's example above indicates that a directory with the name foo.log would also be returned. When I tried it, it wasn't but it's 6 years later and that could be from PS updates.
However, you can use the child item Mode to exclude directories I think.
PS C:\logfiles> Get-Childitem .\* -include *.log | where-object {$_.mode -notmatch "d"}
This should exclude anything with the 'directory' mode set.
get-childitem -include only works with -recursive or a wildcard in the path. I consider this a bug [Thought it was different in PS 6].

Get-ChildItem Exclude and File parameters don't work together

I can't figure out why these two parameters of the Get-ChildItem cmdlet don't work together. To make my question as clear as possible, look at the following example. From the Powershell ISE command pane:
Type 'dir' --> All files and sub-folders in the current directory are displayed.
Type 'dir -File' --> Original list minus sub-folders is displayed.
Type 'dir -Exclude "*.txt"' --> Original list minus .txt files is displayed.
Type 'dir -File -Exclude "*.txt"' --> NOTHING is displayed.
I would expect the original list minus sub-folders and .txt files. But regardless of what argument I use for '-Exclude', I get no items listed.
I have looked at the Get-ChildItem -full documentation, and the related articles here (Stack Overflow) and at other reliable resources, and still don't understand why this fails. Even the classic "-Include '*.txt' -Exclude 'A*'" example fails when you add "-File". How can I use -File and -Exclude together?
Whilst dir is an alias for Get-ChildItem, I find it best to use the full cmdlets when providing answers.
To use proper PowerShell cmdlets it would be best for you to use the following:
Get-ChildItem * -Exclude "*.txt" -File
What you see above is the PowerShell cmdlet to get all items in the path specified (using the * assumes you want all items from the current location)
You can also use -Path and provide the location of the path to where you want to get the items as well, such as:
Get-ChildItem -Path "C:\Path\Folder" -Exclude "*.txt" -File

Get fsrmquota path with a wildcard

I'm currently to get the quota details using the following powershell command.
get-fsrmquota -Path "C:Temp\ID\1500-1"
This works great. I'm trying to get a wildcard path to look for all the IDs that have a ID of 1500. I tried the following sets of commands but they return error
get-fsrmquota -Path "C:Temp\ID\1500-*"
get-fsrmquota -Path "C:Temp\ID\1500-?"
get-fsrmquota -Path "C:Temp\ID\*1500-"
get-fsrmquota -Path "C:Temp\ID\?1500-"
The error that I get is the following,
0x80045306, The specified path is invalid
I have got another way but it loops through all the folders and then filters the folders out, which takes the same time as looping through all the folder.
get-fsrmquota -Path "C:Temp\ID\..." | Where-Object {$_.Path - Like "C:Temp\ID\1500-*"}
I'm looking for another way to get the folder details for the same Id.
Any help would be greatly appreciated.
Thanks.
Get-Help on Get-FsrmQuota says:
-Path
Specifies the local folder that contains the quota.
This parameter supports recursive and wildcard paths. To specify a recursive path, add ... to a path. For example, C:\ Share01... indicates all of the quotas in C:\ Share1 plus all the quotas in any and all subfolders of C:\ Share01. To specify a wildcard in a path, you can add the asterisk (*) and the question mark (?) to a path. For example, C:\ Share01*A indicates all of the quotas in C:\ Share01 plus all the quotas in subfolders of C:\ Share01 that have a name that begins with the letter A.
So I'd expect
get-fsrmquota -Path 'C:\Temp\ID\1500-*'
should work.

Confused with -Include parameter of the Get-ChildItem cmdlet

From documentation:
-Include
Retrieves only the specified items.
The value of this parameter qualifies
the Path parameter. Enter a path
element or pattern, such as "*.txt".
Wildcards are permitted.
The Include parameter is effective only when the command
includes the Recurse parameter or the
path leads to the contents of a
directory, such as C:\Windows*, where
the wildcard character specifies the
contents of the C:\Windows directory.
My first understanding was:
c:\test\a.txt
c:\test\b.txt
So to get 'a.txt' and 'b.txt' I can write:
gci -Path "c:\test\*" -Include "*.txt"
And this works. But now consider such hierarchy:
c:\test\a.txt
c:\test\b.txt
c:\test\c.txt\c.txt
The same command returns:
a.txt, b.txt, c.txt
The actual logic seems to be:
-Include used to match all entities specified by -Path. If matched element
is a file - return it. If matched
element is a folder, look inside and
return matching first level children.
Also, the documentation say:
The Include parameter is effective only when the command
includes the Recurse parameter or the
path leads to the contents of a
directory...
This is wrong as well. E.g.
gci -Path "c:\test" -Include "*.txt"
It returns nothing, while without -Include I get folder content. So -Include is definitely "effective". What really happens here? The -Path specify the "c:\test", and the -Include tries to match this path. As "*.txt" does not match "test", so nothing returned. But look at this:
gci -Path "c:\test" -Include "*t"
It returns a.txt, b.txt and c.txt as "*t" matched "test" and matched all child items.
After all, even knowing how Include works now, I don't understand when to use it. Why do I need it look to inside subfolders? Why should it be so complex?
You're confusing the use of -include. The -include flag is applied to the path, not the contents of the path. Without the use of the recursive flag, the only path that is in question is the path you specify. This is why the last example you gave works, the path c:\test has a t in the path and hence matches "*t".
You can verify this by trying the following
gci -path "c:\test" -in *e*
This will still produce all of the children in the directory yet it matches none of their names.
The reason that -include is more effective with the recurse parameter is that you end up applying the wildcard against every path in the hierarchy.
Try the -filter parameter (it has support for only one extension):
dir -filter *.txt
Tacking on to JaredPar's answer, in order to do pattern matching with Get-ChildItem, you can use common shell wildcards.
For example:
get-childitem "c:\test\t?st.txt"
where the "?" is a wildcard matching any one character or
get-childitem "c:\test\*.txt"
which will match any file name ending in ".txt".
This should get you the "simpler" behavior you were looking for.
I just asked a similar question and got three quick replies concerning the Get-Help for Get-ChildItem.
The answer is in the full description
of the command (Get-Help Get-ChildItem
-full):
The Include parameter is effective only when the command includes the
Recurse parameter or the path leads to
the contents of a directory, such as
C:\Windows*, where the wildcard
character specifies the contents of
the C:\Windows directory.
So the following would work without
recurse.
PS C:\foo> Get-childitem -path
"c:\foo*" -Include *.txt
From Stack Overflow question PowerShell Scripting - Get-ChildItem.
I hope this helps :-)
Including \* at the end of the path should work around the issue
PS C:\logfiles> Get-ChildItem .\* -include *.log
This should return .log files from the current working directory (C:\logfiles)
Alex's example above indicates that a directory with the name foo.log would also be returned. When I tried it, it wasn't but it's 6 years later and that could be from PS updates.
However, you can use the child item Mode to exclude directories I think.
PS C:\logfiles> Get-Childitem .\* -include *.log | where-object {$_.mode -notmatch "d"}
This should exclude anything with the 'directory' mode set.
get-childitem -include only works with -recursive or a wildcard in the path. I consider this a bug [Thought it was different in PS 6].