Windows: copier / déplacer un fichier avec des expressions régulières de nom de fichier?


10

je veux essentiellement courir:

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

je sais xcopyne prend pas en charge les recherches de noms de fichiers d'expressions régulières.

je ne peux pas savoir comment savoir si PowerShell a un Cmdletpour copier des fichiers; et si c'est le cas, comment savoir s'il prend en charge la correspondance des noms de fichiers d'expressions régulières.

Quelqu'un peut-il penser à un moyen d'effectuer une copie / déplacement de fichier récursif avec une correspondance de nom de fichier regex?


1
cela devrait être déplacé vers stackoverflow à droite?
user33788

1
@smoknheap: Cela pourrait être les deux, je trouve que les scripts Powershell deviennent de plus en plus un outil Power User. Il s'agit plus d'une question de remplacement xcopy que d'une question de script.
Doltknuckle

Réponses:


5

J'aime utiliser toutes les commandes Powershell quand je le peux. Après un peu de test, c'est le mieux que je puisse faire.

$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}

Les trois premières lignes sont juste pour faciliter la lecture, vous pouvez définir les variables à l'intérieur des commandes réelles si vous le souhaitez. La clé de cet exemple de code est la commande "Where-Object" qui est un filtre qui accepte la correspondance d'expressions régulières. Il convient de noter que le support des expressions régulières est un peu bizarre. J'ai trouvé une carte de référence PDF ici qui a les caractères pris en charge sur le côté gauche.

[ÉDITER]

Comme "@Johannes Rössel" l'a mentionné, vous pouvez également réduire les deux dernières lignes à une seule ligne.

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

La principale différence est que la manière de Johannes fait le filtrage d'objets et ma façon fait le filtrage de texte. Lorsque vous travaillez avec Powershell, il est presque toujours préférable d'utiliser des objets.

[EDIT2]

Comme @smoknheap l'a mentionné, les scripts ci-dessus vont aplatir la structure des dossiers et mettre tous vos fichiers dans un dossier. Je ne sais pas s'il existe un commutateur qui conserve la structure des dossiers. J'ai essayé le commutateur -Recurse et cela n'aide pas. La seule façon pour que cela fonctionne est de revenir à la manipulation des chaînes et d'ajouter des dossiers à mon filtre.

$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,"")
    }

Je suis sûr qu'il existe une façon plus élégante de le faire, mais d'après mes tests, cela fonctionne. Il rassemble tout, puis filtre les correspondances de noms et les objets de dossier. J'ai dû utiliser la méthode ToString () pour accéder à la manipulation des chaînes.

[EDIT3]

Maintenant, si vous souhaitez signaler le cheminement pour vous assurer que tout est correct. Vous pouvez utiliser la commande "Write-Host". Voici le code qui vous donnera quelques conseils sur ce qui se passe.

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,"")
    }

Cela devrait renvoyer les chaînes pertinentes. Si vous n'obtenez rien quelque part, vous saurez avec quel article vous rencontrez des problèmes.

J'espère que cela t'aides


Notez que vous n'avez pas besoin de l'utiliser $item.FullName- la propriété appropriée est prise automatiquement si vous passez un objet FileInfo (à mon humble avis, vous ne devriez pas, car la puissance de PowerShell vient du passage d'objets structurés, pas de chaîne). De plus, vous pouvez tout mettre dans un seul pipeline: ((gci $source) -match $filter) | cp -dest $destination(légèrement adapté pour plus de concision - n'hésitez pas à changer; je dis simplement que ce foreachn'est pas nécessaire là).
Joey

Quand je le testais, je n'arrivais pas à faire correctement canaliser les objets. C'est ce tour qui m'oblige à utiliser la commande foreach. Je vais essayer votre code et voir ce qui se passe. Merci d'avoir fait remarquer cela.
Doltknuckle

cela a-t-il le même problème que le mien où il aplatit le répertoire de destination? xcopy conserverait normalement la structure du répertoire sur le répertoire de destination, non?
user33788

Copy-Item : Cannot bind argument to parameter 'Path' because it is null
Ian Boyd

Veuillez noter que ce Trimn'est pas destiné à supprimer une chaîne de la fin d'une autre chaîne, mais qu'il supprime toutes les instances des caractères de la chaîne de paramètres du début et de la fin de la chaîne sur laquelle il est appelé. Dans votre cas, s'il $item.Namecontient un C majuscule, il supprimera également la lettre de lecteur C au début de la chaîne.
Andris

3

PowerShell est un excellent outil pour cette tâche. Vous pouvez utiliser l' applet de commande Copy-Item pour le processus de copie. Vous pouvez le canaliser avec d'autres applets de commande pour des commandes de copie complexes, voici quelqu'un qui a fait exactement cela :)

Les expressions régulières utilisent la classe .NET RegEx de l'espace de noms System.Text.RegularExpressions, il existe des procédures rapides sur ces classes

PowerShell possède également les opérateurs -match et -replace qui peuvent être utilisés lors du pipelining avec copy-item

Il existe également des outils pour vous aider à créer le RegEx lui-même, par exemple copain RegEx


À mon humble avis, -matchet -replacedoit être mentionné avant d'entrer dans System.Text.RegularExpressions.RegEx. Du moins pour moi, j'utilise rarement [regex]directement; mais je ne l' utilise fréquemment -matchet les -replaceopérateurs. En ce qui concerne la création des expressions régulières, j'ai trouvé PowerShell très utile pour tester et affiner une expression régulière que vous écrivez également.
Joey

J'ai demandé la réponse à votre réponse dans cette question: superuser.com/questions/149808/…
Ian Boyd

0

comme une idée mais a besoin d'un peu de travail

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.