Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

i basically want to run:

C:\>xcopy [0-9]{13}\.(gif|jpg|png) s:\TargetFolder /s

i know xcopy doesn't support regular-expression filename searches.

i can't find out how to find out if PowerShell has a Cmdlet to copy files; and if it does, how to find out if it supports regular expression filename matching.

Can anyone think of a way to perform a recursive file copy/move with regex filename matching?

share|improve this question
1  
this should be moved to stackoverflow right? –  user33788 Jun 7 '10 at 18:59
1  
@smoknheap : It could be both, I find that Powershell scripting is becoming more and more a Power User tool. This is more of a xcopy replacement question then a scripting question. –  Doltknuckle Jun 7 '10 at 19:07

3 Answers 3

PowerShell is an excellent tool for that task. You can use Copy-Item cmdlet for the copying process. You can pipeline it with other cmdlets for complex copy commands, here is someone that did exactly that :)

Regular expressions use the .NET RegEx class from the System.Text.RegularExpressions namespace, there are quick how-to on these class

PowerShell also have the -match and -replace operators that can be used when pipelining with copy-item

There are also tools to help you create the RegEx itself, e.g. RegEx buddy

share|improve this answer
    
Imho, -match and -replace should be mentioned before going into System.Text.RegularExpressions.RegEx. At least for me, I rarely use [regex] directly; however I do frequently use the -match and -replace operators. As for creating the regular expressions I found PowerShell to be quite useful in testing and refining a regex you're writing, too. –  Joey Jun 7 '10 at 1:21
    
i've asked for the answer to your answer in this question: superuser.com/questions/149808/… –  Ian Boyd Jun 7 '10 at 14:23

as an idea but needs some work

dir -r | ?{$_ -match '[0-9]{13}\.(gif|jpg|png)'} | %{xcopy $_.fullname c:\temp}

share|improve this answer

I like using all Powershell commands when I can. After a bit of testing, this is the best I can do.

$source = "C:\test" 
$destination = "C:\test2" 
$filter = [regex] "^[0-9]{6}\.(jpg|gif)"

$bin = Get-ChildItem -Path $source | Where-Object {$_.Name -match $filter} 
foreach ($item in $bin) {Copy-Item -Path $item.FullName -Destination $destination}

The first three lines are just to make this easier to read, you can define the variables inside the actual commands if you want. The key to this code sample is the the "Where-Object" command which is a filter that accepts regular expression matching. It should be noted that regular expression support is a little weird. I found a PDF reference card here that has the supported characters on the left side.

[EDIT]

As "@Johannes Rössel" mentioned, you can also reduce the last two lines down to a single line.

((Get-ChildItem -Path $source) -match $filter) | Copy-Item -Destination $destination

The main difference is that Johannes's way does object filtering and my way does text filtering. When working with Powershell, it's almost always better to use objects.

[EDIT2]

As @smoknheap mentioned, the above scripts will flatten out the folder structure and put all your files in one folder. I'm not sure if there is a switch that retains folder structure. I tried the -Recurse switch and it doesn't help. The only way I got this to work is to go back to string manipulation and add in folders to my filter.

$bin = Get-ChildItem -Path $source -Recurse | Where-Object {($_.Name -match $filter) -or ($_.PSIsContainer)}
foreach ($item in $bin) {
    Copy-Item -Path $item.FullName -Destination $item.FullName.ToString().Replace($source,$destination).Replace($item.Name,"")
    }

I'm sure that there is a more elegant way to do this, but from my tests it works. It gather s everything and then filters for both name matches and folder objects. I had to use the ToString() method to gain access to the string manipulation.

[EDIT3]

Now if you want to report the pathing to make sure you have everything correct. You can use the "Write-Host" Command. Here's the code that will give you some hints as to what's going on.

cls
$source = "C:\test" 
$destination = "C:\test2" 
$filter = [regex] "^[0-9]{6}\.(jpg|gif)"

$bin = Get-ChildItem -Path $source -Recurse | Where-Object {($_.Name -match $filter) -or ($_.PSIsContainer)}
foreach ($item in $bin) {
    Write-Host "
----
Obj: $item
Path: "$item.fullname"
Destination: "$item.FullName.ToString().Replace($source,$destination).Replace($item.Name,"")
    Copy-Item -Path $item.FullName -Destination $item.FullName.ToString().Replace($source,$destination).Replace($item.Name,"")
    }

This should return the relevant strings. If you get nothing somewhere, you'll know what item is having problems with.

Hope this helps

share|improve this answer
    
Note that you don't need to use $item.FullName there – the appropriate property is taken automatically if you pass a FileInfo object (imho, you shouldn't, since PowerShell's power comes from passing structured objects, not string). Furthermore, you can put everything into a single pipeline: ((gci $source) -match $filter) | cp -dest $destination (slightly adapted for brevity – feel free to change; I'm just saying that the foreach is unnecessary there). –  Joey Jun 7 '10 at 22:25
    
When I was testing it, I couldn't get the objects to pipe correctly. That is turn forces me to use the foreach command. I'll give your code a try and see what happens. Thanks for pointing that out. –  Doltknuckle Jun 8 '10 at 16:31
    
does this have the same problem as mine where it flattens the destination directory out? xcopy would normally preserve the directory structure on the destination directory, right? –  user33788 Jun 8 '10 at 16:52
    
Copy-Item : Cannot bind argument to parameter 'Path' because it is null –  Ian Boyd Jun 13 '10 at 16:33
    
Please note that Trim is not intended to remove a string from the end of another string, instead it removes all instances of the characters in the parameter string from the beginning and end of the string it is called on. In your case if $item.Name contains a capital C it will also remove the drive letter C from the beginning of the string. –  Andris Jan 18 '12 at 11:17

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.