• No results found

Scripting Files with PowerShell's Get-Childitem (gci)

Sooner or later you need a script to list the files in a folder. In DOS we would type: 'DIR'; the nearest equivalent in PowerShell is gci. The full name behind the alias of gci is the informative, get-

ChildItem. You can take the comparison further, dir /s in DOS, translates to get-ChildItem -recurse in PowerShell.

Get-ChildItem Topics

• Trusty Twosome (get-Help and get-Member) • Example 1 - List files in the root of the C:\ drive • Example 2 - List ALL files, including hidden and system • Example 3 - Filter to list just the System files

• Example 4 - The famous -recurse parameter

• Problems with -recurse, and how to overcome them Trusty Twosome (get-Help and get-Member)

When you discover a new PowerShell command, it benefits from being probed with what I call the 'Trusty Twosome'. Experiment with get-Help and get-Member and unlock new scripting possibilities for get-childitem. To see what I mean try these two commands:

1) get-help get-childitem

(help gci) If you prefer abbreviations. (help gci -full) If you like examples.

get-help unearths useful parameters such as -recurse, -exclude, it also helps to reveals hidden files with: -force.

2) get-childitem | get-member (gci | gm) If you enjoy aliases.

(gci | gm -membertype property) If you are fond of filters.

get-member reveals properties that you don't normally see in explorer, for example, CreationTime. Example 1 - List files in the root of the C:\ drive

Here is an example which lists all the files in the C:\ root. # Powershell script to list the files in C: root

get-childitem "C:\"

Note 1: In this instance C:\ is the root and get-childitem lists any files in this directory. Note 2: get-childitem "C:\" works equally well without the speech marks.

Example 2 - List ALL files, including hidden and system $i=0

$GciFiles = get-childitem c:\ -force foreach ($file in $GciFiles) {$i++}

$GciFiles |sort |ft name, attributes -auto Write-host "Number of files: " $i

Note 1: The key addition is the parameter -force. What this does is include hidden and system files. Note 2: The additional commands are so that we can count the files, this makes easier to see prove that -force really makes a difference. Double check what I mean by running the script with, and without, the -force switch.

Example 3 - Filter to list just the System files # PowerShell cmdlet to list the System files in the root of C:\ $i=0

$GciFiles = get-ChildItem "c:\" -force |where {$_.attributes -match "System"} foreach ($file in $GciFiles) {$i++}

$GciFiles |sort |ft name, attributes -auto Write-host "Number of files: " $i

Note 1: We need to employ the comparison parameter -match, rather than -eq "System", this is because System files also have Hidden and other attributes. Consequently, their attribute does not equal ''System'', although it does contain, or match the word System.

Note 2: You probably worked it out for your self, but just to emphasise that the variable $i is a counter. Moreover, ++ increments $i each time the 'where' statements makes a match.

Incidentally, omitting $=0 at the beginning produces an unexpected, or undesirable result, should you run the script for a second time.

Challenge 1: Repeat the command with and without -force.

Challenge 2: Substitute this new 'where' clause on line 3: where {$_.attributes -ne "Directory"}. -ne is the opposite of -eq. Thus this command filters out the directory entry.

# PowerShell cmdlet to list the System files in the root of C:\ $i=0

$GciFiles = gci c:\ -force |where {$_.attributes -ne "Directory"} foreach ($file in $GciFiles) {$i++}

$GciFiles |sort |ft name, attributes -auto # $GciFiles |get-Member

Example 4 - The famous -recurse parameter

Outside of PowerShell, recurse is a little known verb meaning rerun. Inside of PowerShell, -recurse is one of the most famous parameters meaning: repeat the procedure on sub-folders.

The point of this script is to list all the executables in the System32 folder. Moreover it will display the CreationTime, which can help determine if a file is up-to-date. This technique is particularly useful for troubleshooting .dll files.

# Powershell script to list the exe files under C:\Windows\System32 $i =0

$DllFiles = gci "C:\Windows\System32" -recurse | ? {$_.extension -eq ".exe"} Foreach ($Dll in $DllFiles) {

$Dll.name + "`t " + $DLL. CreationTime + "`t " + $Dll. Length $i++

}

Write-Host The total number of files is: $i

Note 1: This example uses two aliases; my principle reason was to make line 4 shorter. Gci is an alias for our featured command get-childitem, and the question mark (?) is an alias for 'where'.

Note 2: The -recurse parameter comes directly after the name of the directory. For completeness, the location should be introduced by the -path parameter, but as this is optional, I have omitted the -path parameter in this script. However, this is how to include the -path parameter.

$DllFiles = gci -path "C:\Windows\System32" -recurse.

Note 3: If you investigate the file's properties, then you can spot other assets, for example: LastWriteTime, or even Length. Here is how to employ get-member to list the properties. # Powershell script to investigate file properties

get-ChildItem | get-Member -membertype property The Rest of the Item Family

Copy-Item get-Item (gi) Move-Item New-Item Remove-Item Set-Item

Problems with -recurse, and how to overcome them Case Study

The mission is to list all files containing the word 'Microsoft' in the Windows folder or its sub-folders. For this we use the select-string pattern matching command.

The problem is that this script does not work. All that happens is that we get an error: Cannot find path... Path does not exist.

$i=0

$Path = "C:\windows"

$Full = get-ChildItem $Path -recurse $StringText= "Microsoft"

$List = select-string -pattern $StringText $Full foreach ($file in $List) {$file.Path; $i++} $i

The solution: Add the -include parameter $i=0

$Path = "C:\windows"

$Full = get-ChildItem $Path -include *.txt -recurse $StringText= "Microsoft"

$List = select-string -pattern $StringText $Full foreach ($file in $List) {$file.Path; $i++} $i

Note 1: When we add -include *.txt the cmdlet works as initially planned. Actually, you could swap the famous *. * for *.txt, thus : -include *. *

Note 2: -include only works when you also append the -recurse parameter.

Note 3: -include applies to the filename and extension. Where I made a mistake was thinking that - include would apply to the path.

Simplification

There is no reason why you cannot simplify by removing at least two of the variables, especially on a production script. The only reason that I employed $Path and $StringText is that I like to isolate and control each step of the script.

$i=0

$Full = get-ChildItem C:\windows -include *.txt -recurse $List = select-string -pattern "Microsoft" $Full

foreach ($file in $List) {$file.Path; $i++} $i

Summary of -Recurse

-Recurse is a classic switch to instruct PowerShell commands to repeat for sub directories. Once you learn the name -recurse and the position, directly after the directory, then it will serve you well in scripts that need to search repeatedly for information.