Welcome › Forums › General PowerShell Q&A › Copy script - right usage ?
This topic contains 7 replies, has 3 voices, and was last updated by
-
AuthorPosts
-
April 8, 2015 at 3:49 am #24064
Hi,
I've written a copy script which i am going to use in an SCCM 2012 R2 task sequence. Would like to know if i have used things in the correct way as part of my learning process..Thanks !
#Set Locations
#$StartLocation = "%windir%\temp"
#$Destination = "C:\ProgramData\Microsoft\User Account Pictures"#Remove unrequired file(s)
[CmdletBinding(SupportsShouldProcess=$true)]
Param
()
Process
{#Set Variables
$ErrorAction = 'Stop'
$StartLocation = "%windir%\temp"
$Destination = "C:\ProgramData\Microsoft\User Account Pictures"
Try
{
$remove = get-childitem "$Destination" -Include *.dat -Recurse | Remove-item
}
Catch
{}
If (!$remove)
{
Write-Verbose -Message "An error occurred whilst attempting to remove $remove"
Break
}
#Copy Avatar files to locationTry
{
Get-ChildItem -path $StartLocation -Recurse -include "*.bmp","*.png" |
Foreach-Object { Copy-Item -path $_ -Destination $Destination }
}
Catch
{
Write-Verbose -Message "Failed to copy files to $Destination"
Break
}
} -
April 8, 2015 at 6:47 am #24072
Ah... well, did you have any more specific questions? I mean, if it works, that's 90% of the battle!
Usually, you can pipe directly from Get-ChildItem to Copy-Item, which would probably run a tiny bit faster than using ForEach object.
I don't think your Catch block will ever run, because Copy-Item normally doesn't throw a terminating error. You'd need to add -ErrorAction Stop to have it generate a catchable, terminating exception. That's true for your earlier Try/Catch, too – you might review "The Big Book of PowerShell Error Handling" (free ebook from our Resources menu) to learn more about that.
Oh, I see – you set $ErrorAction to 'Stop.' That won't work. It's $ErrorActionPreference, and setting it globally is considered a poor practice in most situations. Certainly where you're not catching it globally.
Also, I'm not sure your intention, but as-is a single file copy failure will abort all remaining files, once your Try/Catch is working with -ErrorAction. If you were to get the files and then enumerate them, you could have it fail one file and continue trying others. Again, I'm not sure if that's your intent or not, so this isn't "wrong," it's just an observation.
You don't technically need PROCESS{} because you're not accepting pipeline input.
-
April 8, 2015 at 7:07 am #24075
So, multiple observations:
Why are you trying to catch the errors? The reason you would stop doing a command is if your logic would be step X has to occur before you do step y. For instance, in the example below, if the removal fails, then the copy will not start because you are globally stopping the command if an error occurs. In your posted script you are just trying to catch errors just for logging?
I think a best practice is to use variables as much as possible. CommonApplicationData is a Special Folder that resolves to C:\ProgramData. So, in Windows 10, if you they move the directory to C:\ProgramMagicMSRules, your script would still work because you are using a provided variable that will resolve to that location. If you hardcode the path to C:\ProgramData, your script wouldn't work. Make sense? Also, Powershell will not resolve %VAR%, you need to use $Env:VAR.
For Copy-Item, you don't need to loop through each file. Run "Get-Help Copy-Item". You will see that -Path accepts a String array (i.e. [string[]]). So, you should be able to run the command below and it will perform a Foreach for each column named "Path" in the return from Get-ChildItem
If you are trying to just capture errors, make them useful. If you were sitting at a computer and received an error, "An error occurred whilst attempting to remove...", what would you do with that? You don't have an actual error. Make sure if you do capture errors they are useful. See the examples that will show what the command is doing and the returned exception message that Catch captured
The below code was not tested, it's just an example to illustrate some of the points above:
[CmdletBinding()] $ErrorAction = 'Stop' $Destination = "{0}\Microsoft\User Account Pictures" -f ([Environment]::GetFolderPath('CommonApplicationData')) $StartLocation = "{0}\temp" -f $env:windir try { Get-Childitem "$Destination" -Include *.dat -Recurse | Remove-item try { Copy-Item -Path (Get-ChildItem -Path $StartLocation -Recurse -include "*.bmp","*.png") -Destination $Destination } catch { Write-Verbose -Message ("Error copying *.bmp and *.png to {0}. {1}" -f $StartLocation, $_.Exception.Message) } } Catch { Write-Verbose -Message ("Error occured removing *.dat from {0}. {1}" -f $Destination, $_.Exception.Message) }
-
April 8, 2015 at 9:56 am #24081
Thanks for your replies. I'm about 3 weeks into my learning powershell. I try and use it as much as I can. Must admit I'm getting confused when and where I should be using commands, like the try and catch.
Maybe I'm over complicating things. I want to be able to do best practice and people to look and say, good script !Rob, your variables for destination and startlocation are great. But I'm not sure how it works together. I know what it is doing it's the understanding. How and why did you come to that command? I'm impressed and would love to get to that level.
By asking the forum to check my work, I want to get into good habits from the start and not bad practice.
-
April 8, 2015 at 11:27 am #24085
I've worked it out, by referring to Don's great book, "Powershell in depth".
Its quite clever ! In this example, "{0}\temp" -f $env:windir, the {0} is the first place hold. the -f means formatting, so the command after goes in the first {0} displaying C:\windows\temp !
Think thats about right ?
PowerShell is tricky, but awesome !!!
-
April 8, 2015 at 11:37 am #24087
Sorry, can you help explain this line, Write-Verbose -Message ("Error occured removing *.dat from {0}. {1}" -f $Destination, $_.Exception.Message)
How come the {0} has a . (dot) after it ? What does the $_.Exception.Message do and how is it called ? -
April 8, 2015 at 1:25 pm #24089
{0} is a place holder for $Destination. So, basically the message would be: Error occured removing *.dat from C:\ProgramData\Microsoft\User Account Pictures. Exception Message
As for exceptions, when Powershell generates errors, there is a lot of information you can get which you can explore with Get-Member and other methods. Let's look at what happens when I try to do something with C:\Windows\Temp when I'm logged in as a standard user. I get an Access Denied and other information like the type of exception, category, etc. when the error is shown. As I said earlier, we need to know what happened when the error occurred but there is also overkill. So, I just want the message "Access to the path 'C:\Windows\Temp' is denied.", which would be $_.Exception.Message. If you look at this, it's 3 parts. The $_ is a variable representing the current context of the script, which is what Catch is returning. $_ is an object which contains properties and methods. $_.Exception is a property just like CategoryInfo, ErrorDetails, etc. (see below). We can use can use Get-Member to see what properties and methods are available with a returned object. Typically, the $_.Exception.Message is descriptive enough to understand what happened with a command. If not, you have all of the information returned in Catch to give you as much information as you would need.
PS C:\Users\rsimmers> Get-ChildItem ("{0}\Temp" -f $env:windir) Get-ChildItem : Access to the path 'C:\Windows\Temp' is denied. At line:1 char:1 + Get-ChildItem ("{0}\Temp" -f $env:windir) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : PermissionDenied: (C:\Windows\Temp:String) [Get-ChildItem], UnauthorizedAccessException + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand PS C:\Users\rsimmers> try{Get-ChildItem ("{0}\Temp" -f $env:windir) -ErrorAction Stop}catch{"You got an error: {0}" -f $_.Exception.Message} You got an error: Access to the path 'C:\Windows\Temp' is denied. PS C:\Users\rsimmers> try{Get-ChildItem ("{0}\Temp" -f $env:windir) -ErrorAction Stop}catch{$_ | Get-Member} TypeName: System.Management.Automation.ErrorRecord Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), void ISer... GetType Method type GetType() ToString Method string ToString() CategoryInfo Property System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;} ErrorDetails Property System.Management.Automation.ErrorDetails ErrorDetails {get;set;} Exception Property System.Exception Exception {get;} FullyQualifiedErrorId Property string FullyQualifiedErrorId {get;} InvocationInfo Property System.Management.Automation.InvocationInfo InvocationInfo {get;} PipelineIterationInfo Property System.Collections.ObjectModel.ReadOnlyCollection[int] PipelineIterationInfo {get;} ScriptStackTrace Property string ScriptStackTrace {get;} TargetObject Property System.Object TargetObject {get;} PSMessageDetails ScriptProperty System.Object PSMessageDetails {get=} PS C:\Users\rsimmers> try{Get-ChildItem ("{0}\Temp" -f $env:windir) -ErrorAction Stop}catch{$_.Exception | Get-Member} TypeName: System.UnauthorizedAccessException Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj), bool _Exception.Equals(System.Object obj) GetBaseException Method System.Exception GetBaseException(), System.Exception _Exception.GetBaseException() GetHashCode Method int GetHashCode(), int _Exception.GetHashCode() GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), void ISerializable... GetType Method type GetType(), type _Exception.GetType() ToString Method string ToString(), string _Exception.ToString() Data Property System.Collections.IDictionary Data {get;} HelpLink Property string HelpLink {get;set;} HResult Property int HResult {get;} InnerException Property System.Exception InnerException {get;} Message Property string Message {get;} Source Property string Source {get;set;} StackTrace Property string StackTrace {get;} TargetSite Property System.Reflection.MethodBase TargetSite {get;}
-
April 8, 2015 at 10:15 pm #24094
Rob, thank you so much for taking the time to reply. So the lesson here is use get-member as much as possible and play with results of get-member, I guess.
Using things in the right way is important to me. I do get frustrated with my own knowledge, would love to get to your level of understanding. Just need to keep going with it.
Powershell is so cool !
-
AuthorPosts
The topic ‘Copy script - right usage ?’ is closed to new replies.