When you’re given a task or you have a task you want to automate, what approach do you take? What kind of steps do you do? Let’s say you want to copy files from a directory to another.
The first thing I like to do is to see what kind of cmdlets are available in regards to the task you want to solve. This can be done by running the following command:
# This outputs a list of all available commands PS C:\>Get-Command
With this command, you can filter on parameters like name, noun, and verb to find the most suitable cmdlet to use. Since we are going to create a script that copies files, we can get all commands containing copy with the examples below. Note, that I might have different modules and cmdlets installed, therefore some cmdlets may be different. From the result below, we will use the cmdlet
.Copy-Item
# Filtering using -Verb parameter PS C:\>Get-Command -Verb Copy # Outputs: CommandType Name Version Source ----------- ---- ------- ------ Function Copy-NetFirewallRule 2.0.0.0 NetSecurity Function Copy-NetIPsecMainModeCryptoSet 2.0.0.0 NetSecurity Function Copy-NetIPsecMainModeRule 2.0.0.0 NetSecurity Function Copy-NetIPsecPhase1AuthSet 2.0.0.0 NetSecurity Function Copy-NetIPsecPhase2AuthSet 2.0.0.0 NetSecurity Function Copy-NetIPsecQuickModeCryptoSet 2.0.0.0 NetSecurity Function Copy-NetIPsecRule 2.0.0.0 NetSecurity Cmdlet Copy-Item 3.1.0.0 Microsoft.PowerShell.Management Cmdlet Copy-ItemProperty 3.1.0.0 Microsoft.PowerShell.Management Cmdlet Copy-VMFile 2.0.0.0 Hyper-V # Filtering using -Name parameter, to find commands that contains the word Copy Get-Command -Name "*Copy*" # Output: CommandType Name Version Source ----------- ---- ------- ------ Alias copy -> Copy-Item Function Copy-NetFirewallRule 2.0.0.0 NetSecurity Function Copy-NetIPsecMainModeCryptoSet 2.0.0.0 NetSecurity Function Copy-NetIPsecMainModeRule 2.0.0.0 NetSecurity Function Copy-NetIPsecPhase1AuthSet 2.0.0.0 NetSecurity Function Copy-NetIPsecPhase2AuthSet 2.0.0.0 NetSecurity Function Copy-NetIPsecQuickModeCryptoSet 2.0.0.0 NetSecurity Function Copy-NetIPsecRule 2.0.0.0 NetSecurity Cmdlet Copy-GPO 1.0.0.0 GroupPolicy Cmdlet Copy-Item 3.1.0.0 Microsoft.PowerShell.Management Cmdlet Copy-ItemProperty 3.1.0.0 Microsoft.PowerShell.Management Application Robocopy.exe 10.0.14... C:\Windows\system32\Robocopy.ex Application xcopy.exe 10.0.14... C:\Windows\system32\xcopy.exe
# Examples of how to get different commands # by specifying the verb parameter. Get-Command -Verb Get Get-Command -Verb Set Get-Command -Verb Remove Get-Command -Verb Add Get-Command -Verb Update Get-Command -Verb New Get-Command -Verb Disable Get-Command -Verb Enable Get-Command -Verb Test
When we have found the proper cmdlet, we need to know how it can help us, by asking for help. This can be done by using the cmdlet
. This gives you a manual on how the cmdlet works. and there are many different ways you can ask for help, that is often useful when learning about a new cmdlet. See some examples below.Get-Help
# This opens a separate window with all the help information Get-Help Copy-Item -ShowWindow # This opens a new browser tab with on Microsofts documentation page forthe cmdlet Get-Help Copy-Item -Online # Displays usage examples in the PowerShell console Get-Help Copy-Item -Examples # Display more in dept details about the cmdlet Get-Help Copy-Item -Detail # If you know a parameter to a cmdlet and need help for that specificparameter # ask for that parameter spesifically with the following for instance Get-Help Copy-Item -Parameter Destination
Now that we know a thing or two on how to get help, we can start by testing out the cmdlet in the command line. Let’s say you want to take a copy of your Documents folder to a Backup folder, we can do this by running the following command.
# Copies the CopyMe.txt file in C:\Documents to the C:\Backup folder PS C:\> Copy-Item -Path C:\Documents\CopyMe.txt -Destination C:\Backup # If you want to experiment with the same folders and file, # you can run the commands below to create them. PS C:\>New-Item -Path C:\ -Name Documents -ItemType Directory PS C:\>New-Item -Path C:\ -Name Backup -ItemType Directory PS C:\>New-Item -Path C:\Documents -Name CopyMe.txt -ItemType File
This command does the trick, but typing this command for each item in a folder manually isn’t very efficient. Now, let’s see how we can do this for each item within a folder now that we have the core command in place.
Looping through folders
To find how we can loop through each item in a folder we can use
.Get-Help foreach
PS C:\>Get-Help foreach
The foreach
Let us repeat a section of code, in this case repeating the Copy-Item cmdlet for each item in a specified directory, but first, we need to know how to list out all the items in a folder. To find out, we can use the
again to look for something that lists items in a current directory.Get-Command
# This will output every command that have the verb Get PS C:\>Get-Command -Verb Get # Look for the cmdlet "Get-ChildItem" this will list out all items i current directory
Now that we know how to list items in a directory and we know how to repeat a task for each item in a directory, we can now combine these two cmdlets with the following.
# One way to do it foreach ($Item in Get-ChildItem -Name C:\Documents) { Copy-Item -Path $Item -Destination C:\Backup\$Ite } # Another way to do it, the easiest in my opinion and the one used as an example in rest of the post Get-ChildItem -Path "C:\Documents" | foreach { Copy-Item -Path $_ -Destination "C:\Backup" }
The first example code says for each object in the parentheses, perform the action that’s defined within the curly braces. The
in the Get-ChildItem
foreach
statement grabs a list of items in the specified path and stores them temporarily in the
variable. Inside the loop, the $Item
variable is used to refer to the single object returned by this particular iteration of the loop. For each iteration, the $Item
command is called and the item is moved to the specified destination.Copy-Item
The second example pipes the result from the
cmdlet to the Get-ChildItem
foreach
command and for each item copy the result to the specified destination. The
variable can be seen as the $_
variable in the first example, it refers to the single object returned by the particular iteration of the loop. This is one way we can repeat tasks for multiple items.$Item
Adding variables
Furthermore, that we can copy all items in a folder, we can further enhance the script by adding variables. This makes it easier to edit the parameters in the script, by separating the functionality and values, it’s also a step towards avoiding hardcoded values. If you save and run the script below you will be able to copy items specified in the $Path variable to the path specified in the $Destination variable. By creating variables you can edit the value without editing the action of the command. Save the file as Copy-Items.ps1 and then run it from the command line and the script will execute the script (if you have the same file and folders) otherwise it will display an error message that the current path doesn’t exist. Execute the script by running for instance PS C:\Scripts>Copy-Items.ps1.
$Path = "C:\Document" $Destination = "C:\Backup" Get-ChildItem -Path $Path | foreach { Copy-Item -Path $_ -Destination $Destination }
Parameterizing the script
To enhance the script, even more, we can add parameters to the script, this allows us to specify the value of the variables, which let’s us specify the directory we want to copy and the destination we want to copy, each time we run the script. To execute the script below with parameters, you can do as follow: PS C:\Scripts> Copy-Item -Path C:\Documents -Destination C:\Backup. It allows us to specify the values of the variables more interactively than in the previous step and is starting to become a more useful tool.
param( $Path, $Destination ) Get-ChildItem -Path $Path | foreach { Copy-Item -Path $_ -Destination $Destination }
To further improve the script we can explicitly define the data type of the parameters. This helps validate that the input parameter has the correct data format.
param( [string]$Path, [string]$Destination ) Get-ChildItem -Path $Path | foreach { Copy-Item -Path $_ -Destination $Destination }
Creating a function
Furthermore, we can create a function out of the script. A PowerShell function is a list of statements that has a name assigned to it, in this case we call it
, since we are copying all items in a specified folder. To call a function you need to load the script into memory by using dot source like this Copy-Items
, then you can run the function just like a PowerShell cmdlet, .
Copy-Items.ps1PS C:\Scripts>
.Copy-Items -Path C:\Documents -Destination C:\Backup
Remember when you’re working with a function you must load it into memory each time you make an adjustment to it, otherwise you can be quite confused when you run the script and nothing different is happening. It can be useful to open a new PowerShell instance with or using the up arrow on the keyboard to reuse the dot source command, this is quite effective when you get use to it.
function Copy-Items { param( [string]$Path, [string]$Destination ) Get-ChildItem -Path $Path | foreach { Write-Verbose "Copying item $_ to $Destination folder.." Copy-Item -Path $_ -Destination $Destination Write-Verbose "$_ copied to $Destin } } #end Copy-Items function
Naming convention
When creating function and PowerShell tools, use the same naming convention as the PowerShell cmdlets. This makes it more intuitive and easy to use by keeping it consistent. You don’t need to invent the wheel again. For instance, in the script we have created I’ve used the same variable names as the parameters to keep it consistent and intuitive, and easier for others to get started when there is one way to name variables and parameter names.
More, when creating a function use the Verb-Noun naming standard. To get a list of all available verbs in PowerShell, you can use the cmdlet
. Use a verb that matches to the action of the script and make sure to use descriptive names that explain the functionality of the script. Also use the PowerShell case standard, Pascal case.Get-Verb
In short the reason you want to follow the PowerShell naming convention is that you want to build your tools and functions to be exactly like a PowerShell command.
Creating a cmdlet
Adding
to the script. When PowerShell see this attribute, it treats the command like a cmdlet. A cmdlet has a lot of neat features, one of them is the cmdlet [CmdletBindng()]
, this allows you to verify what the script is doing while running, by outputting a descriptive text you can activate when the function runs, by adding the -Verbose parameter, like so Copy-Items -Path C:\Documents -Destination -Verbose. The [CmdletBindng()] also has other features like Debug, Write-Verbose, ErrorAction, and more. In this post, we focus on Write-Verbose
.-Verbose
function Copy-Items { [CmdletBinding()] param( [string]$Path, [string]$Destination ) Get-ChildItem -Path $Path | foreach { Write-Verbose "Copying item $_ to $Destination folder.." Copy-Item -Path $_ -Destination $Destination Write-Verbose "$_ copied to $Destination" } } #end Copy-Items function
When you run the function, this will output
PS C:\> Copy-Items -Path C:\Documents\CopyMe.txt -DestinationC:\Backup\ -Verbose VERBOSE: Copying C:\Documents\CopyMe.txt to C:\Backup folder.. VERBOSE: C:\Documents\CopyMe.txt copied to C:\Backup PS C:\>
A final touch
Now we have a full-worthy script that lets us copy files within a folder to another folder, the last thing we need to do is create documentation of the script.
<# .SYNOPSIS Copies items from one location to another. .DESCRIPTION The Copy-Items cmdlet copies all itemsspecified in the Path variable to the pathspecified in the Destination parameter. Insimple terms it copies items from onelocation to another location. .EXAMPLE PS C:\> Copy-Items -Path C:\Documents-Destination C:\Backup This example copies all items in theC:\Documents folder to the C:\Backup folder. .PARAMETER Path Specifies the folder you want to copy itemsfrom. .PARAMETER Destination Specifies the folder you want to copy itemsto. #> function Copy-Items { [CmdletBinding()] param( [string]$Path, [string]$Destination ) Get-ChildItem -Path $Path | foreach { Write-Verbose "Copying item $_ to$Destination folder.." Copy-Item -Path $_ -Destination$Destination Write-Verbose "$_ copied to $Destination" } } #end Copy-Items function
If you now run
, you can get the help information about the cmdlet, this is useful if you have created the script once, and then need to run in a later state, also if others are using the tool.Get-Help Copy-Items
PS C:\> Get-Help Copy-Items NAME Copy-Items SYNOPSIS Copies items from one location to another. SYNTAX Copy-Items [[-Path] <String>] [[-Destination] <String>] [<CommonParameters>] DESCRIPTION The Copy-Items cmdlet copies all items specified in the Path variable to the path specified in the Destination para meter. In simple terms it copies items from one location to another location. RELATED LINKS REMARKS To see the examples, type: "get-help Copy-Items -examples". For more information, type: "get-help Copy-Items -detailed". For technical information, type: "get-help Copy-Items -full".
To wrap up
Now we have created a simple script using simple steps to create a fully working script. I hope you find the steps useful and understandable. I’ve also added a list below with some other tips that came to mind when writing this post. Hope you enjoyed and learned something from it.
Tips:
- Don’t start creating a script, the first step is to test and figure out the core command in the console. Then when you’re troubleshooting you only have one problem to relate to, then take baby steps until you have a fully working tool, like we did in this post.
- Use double quotations when using cmdlets like
,Write-Verbose
andWrite-Host
, then you can use variables without have to concatenate.Write-Output
- If you us PowerShell ISE you can put your cursor on a cmdlet, and hit F1 and then a new window with the help manual for that cmdlet opens up.
- Don’t use
if you plan to make modifications to the object you write to the console, this cmdlet will only output plain text, whileWrite-Host
keeps the object in place, and you can make modification to the object. TheWrite-Output
is useful when writing status updates when scripts are run by using it’s parameterWrite-Host
where you add colors, for instance; red for errors, green for success, yellow for warning.-Foregroundcolor