Comment faire fonctionner Robocopy en PowerShell?

24

J'essaie d'utiliser robocopy à l'intérieur de PowerShell pour mettre en miroir certains répertoires sur mes machines personnelles. Voici mon script:

param ($configFile)

$config = Import-Csv $configFile
$what = "/COPYALL /B /SEC/ /MIR"
$options = "/R:0 /W:0 /NFL /NDL"
$logDir = "C:\Backup\"

foreach ($line in $config)
{
 $source = $($line.SourceFolder)
 $dest = $($line.DestFolder)
 $logfile =  $logDIr 
 $logfile += Split-Path $dest -Leaf
 $logfile += ".log"

 robocopy "$source $dest $what $options /LOG:MyLogfile.txt"
}

Le script prend un fichier csv avec une liste de répertoires source et de destination. Lorsque j'exécute le script, j'obtiens ces erreurs:

-------------------------------------------------------------------------------
   ROBOCOPY     ::     Robust File Copy for Windows                              
-------------------------------------------------------------------------------

  Started : Sat Apr 03 21:26:57 2010

   Source : P:\ C:\Backup\Photos \COPYALL \B \SEC\ \MIR \R:0 \W:0 \NFL \NDL \LOG:MyLogfile.txt\
     Dest - 

    Files : *.*

  Options : *.* /COPY:DAT /R:1000000 /W:30 

------------------------------------------------------------------------------

ERROR : No Destination Directory Specified.

       Simple Usage :: ROBOCOPY source destination /MIR

             source :: Source Directory (drive:\path or \\server\share\path).
        destination :: Destination Dir  (drive:\path or \\server\share\path).
               /MIR :: Mirror a complete directory tree.

    For more usage information run ROBOCOPY /?


****  /MIR can DELETE files as well as copy them !

Une idée de ce que je dois faire pour réparer?

Merci, Mark.

Mark Allison
la source

Réponses:

10

Si les variables pour $whatet $optionsne changent pas, pourquoi sont-elles des variables?

$what = "/COPYALL /B /SEC /MIR" 
$options = "/R:0 /W:0 /NFL /NDL" 

Cela fonctionne bien pour moi:

robocopy   $source $dest /COPYALL /B /SEC /MIR /R:0 /W:0 /NFL /NDL
Tatiana Racheva
la source
Bon point - restez simple. :)
Helvick
3
aussi / sec pas / sec / est critique ici
1
Donc, vous venez de sauter le / Log et cela a fonctionné :-) Gotcha est que vous obtenez d'étranges erreurs appelant Robocopy avec PS lorsque le fichier journal n'existe pas auparavant.
icnivad
6
Je sais que c'est un vieux post, mais je pense qu'il vaut la peine de dire que les avoir comme variables est un signal pour les futurs lecteurs du script que celles-ci étaient censées être des parties configurables du script. La raison pour laquelle cela fonctionne est qu'il arrive de corriger la faute de frappe dans l'original $ quoi.
Octopus
2
Même si quelque chose ne change pas, l'encapsulation des variables peut aider à rendre un script moins alambiqué. Et s'il s'avère que j'ai besoin de référencer cette variable plus tard, cela rend le script plus dynamique
Kolob Canyon
20

Le remplissage des chaînes dans les paramètres des commandes externes à partir de Powershell nécessite un saut de cerceau si vous voulez pouvoir utiliser l'expansion variable et que la ligne de commande résultante comprenne correctement quels paramètres vous souhaitez séparer et lesquels ne devraient pas. Dans votre exemple, vous envoyez la chaîne entière comme premier paramètre et Robocopy vous dit qu'il ne peut pas trouver cette longue chaîne comme répertoire source. Vous voulez que la chaîne Source entière soit traitée comme un paramètre (y compris les espaces qui peuvent s'y trouver), de même pour Destination, mais vos options $ what et $ échoueront car elles seront toutes deux livrées à Robocopy en tant que paramètre unique qui ne peut pas être analysé.

Il existe plusieurs façons de le faire correctement, mais l'extrait de code suivant est basé sur la façon dont vous semblez vouloir décomposer vos paramètres et fonctionne pour l'exemple de répertoire unique que j'ai utilisé.

$source="C:\temp\source"
$dest="C:\temp\dest"

$what = @("/COPYALL","/B","/SEC","/MIR")
$options = @("/R:0","/W:0","/NFL","/NDL")

$cmdArgs = @("$source","$dest",$what,$options)
robocopy @cmdArgs
Helvick
la source
Merci pour la réponse. J'ai essayé votre code mais j'obtiens toujours l'erreur suivante: ERREUR: Paramètre n ° 3 non valide: "/ COPYALL / B / SEC / MIR"
Mark Allison
Lorsque j'exécute le code de talon ci-dessus (copié ci-dessus), il fonctionne exactement comme prévu - en particulier toutes les options \ quels éléments sont reconnus comme paramètres séparés. Si vous exécutez uniquement le stub ci-dessus (avec un dossier source qui existe), cela fonctionne-t-il? Si c'est le cas, revérifiez le Quotes \ s'échappant dans votre propre code modifié. Le paramètre non valide # 3 indique que votre version de $ ce qui est toujours une chaîne unique et non un tableau de paramètres séparés dans le code que vous exécutez.
Helvick
2
+1. C'est certainement le problème ici. Je trouve drôle la façon dont les gens essaient constamment d'utiliser PowerShell comme s'il s'agissait d'un ancien shell basé uniquement sur du texte ;-)
Joey
@Joey Avez-vous trouvé que le modèle d'objets de PowerShell fonctionnait mieux dans la pratique que ceux basés sur du texte?
Didier A.
1
@DidierA.: Certainement. Surtout lorsque les objets existent pour décrire les choses dont vous interagissez avec, par exemple , fichiers, processus, services, etc. Mais même si vous avez seulement des applications natives et les sorties de chaîne , vous pouvez simplement utiliser les opérateurs de chaînes habituelles, par exemple , -matchet -replaceainsi que le filtrage / cmdlets de projection de travailler avec ça. Il est généralement assez bien conçu à cet égard (la plupart des *-Objectcommandes sont orthogonales et elles peuvent toutes être appliquées à n'importe quel objet).
Joey
3

Supprimer les guillemets de la ligne robocopy?

c'est à dire

param ($configFile)

$config = Import-Csv $configFile
$what = "/COPYALL /B /SEC/ /MIR"
$options = "/R:0 /W:0 /NFL /NDL"
$logDir = "C:\Backup\"

foreach ($line in $config)
{
 $source = $($line.SourceFolder)
 $dest = $($line.DestFolder)
 $logfile =  $logDIr 
 $logfile += Split-Path $dest -Leaf
 $logfile += ".log"

 robocopy $source $dest $what $options /LOG:MyLogfile.txt
}
Bryan
la source
Non, j'ai essayé en premier et cela n'a pas fonctionné (erreurs similaires), alors je l'ai changé pour ce qui est montré ci-dessus. D'autres idées?
Mark Allison
Quelle est la sortie lorsque vous l'exécutez sans guillemets? Votre sortie d'origine montrant la source comme "P: \ C: \ Backup \ Photos \ COPYALL \ B \ SEC \ \ MIR \ R: 0 \ W: 0 \ NFL \ NDL \ LOG: MyLogfile.txt \" a été ce qui a attiré mon attention aux citations.
Bryan
3

J'ai eu le même problème. La ligne de commande entière a été interprétée comme le premier argument.

Rien de mentionné ci-dessus n'a fonctionné, y compris:

invoke-expression "robocopy ""$sourceFolder"" ""$destinationFolder"" /MIR"  
invoke-expression "robocopy \`"$sourceFolder\`" \`"$destinationFolder\`" /MIR"  

Oublier les guillemets ne fonctionne pas lorsqu'il y a un espace sur le chemin:

invoke-expression "robocopy $sourceFolder $destinationFolder /MIR"  

Après avoir essayé les astuces sur http://edgylogic.com/blog/powershell-and-external-commands-done-right/ , je l'ai finalement compris.

robocopy "\`"$sourceFolder"\`" "\`"$destinationFolder"\`" /MIR

Peut-être que j'aurais dû m'en tenir aux fichiers de chauve-souris. :)

Justin
la source
Votre lien vers edgylogic est-il passé à slai.github.io/posts/… ?
Henrik Staun Poulsen
1

J'ai trouvé que la meilleure façon d'exécuter une commande native est d'utiliser la commande & pour exécuter une liste de chaînes. De plus, si vous souhaitez que la sortie de la console soit incluse dans une transcription PowerShell, vous devrez diriger la sortie vers l'hôte externe. Voici un exemple d'appel de 7Zip avec plusieurs paramètres extraits d'un script 7-Zip vers Amazon S3 Powershell que j'ai écrit:

$7ZipPath = "C:\Program Files\7-Zip\7z.exe" #Path to 7Zip executable
$FolderPath = "C:\Backup" #Folder to backup (no trailing slash!)
$FolderName = $FolderPath.Substring($FolderPath.LastIndexOf("\")+1) #grab the name of the backup folder
$TimeStamp = [datetime]::Now.ToString("yyyy-MM-dd_HHmm") #Create unique timestamp string
$strFile = [String]::Format("{0}\{1}_{2}.7z", "c:\",$FolderName,$TimeStamp) #Create filename for the zip
#7z options: add, type 7z, Archive filename, Files to add (with wildcard. Change \* to \prefix* or \*.txt to limit files), compression level 9, password, encrypt filenames, Send output to host so it can be logged.
& $7ZipPath "a" "-t7z" "$strFile" "$FolderPath\*" "-mx9" "-r" "-pPASSWORD" "-mhe" | out-host #create archive file
if($LASTEXITCODE -ne 0){ #Detect errors from 7Zip. Note: 7z will crash sometimes if file already exists
    Write-Error "ERROR: 7Zip terminated with exit code $LASTEXITCODE. Script halted."
    exit $LASTEXITCODE
}

Fondamentalement, pour votre cas, j'essaierais quelque chose comme ça (peut-être besoin de décomposer les paramètres $ what et $ options individuellement):

$robocopypath = "c:\pathtofolder\robocopy.exe"
& $robocopypath "$source" "$dest" "$what" "$options" "/LOG:MyLogfile.txt"
Greg Bray
la source
Le problème avec cela est que lorsque vous dites "$ quoi" et "$ options" seront envoyés à robocopy en tant que paramètres uniques plutôt qu'une liste de paramètres séparés et étant donné son exemple de code, ils devront être décomposés, il n'y a pas de solution il.
Helvick
Vous pouvez utiliser l'opérateur splat @si vous utilisez powershell v3 pour diviser une liste séparée par des virgules en une pile d'arguments
Zasz
0
invoke-expression "robocopy $source $dest $what $options /LOG:MyLogfile.txt"

Cela interpolera toutes les variables, puis appellera la chaîne résultante. De la façon dont vous l'avez maintenant, il semble que robocopy soit invoqué avec les guillemets autour de toutes les options, c'est-à-dire, robocopy "c: \ src c: \ dst blah meh". Ceci est interprété comme un seul paramètre.

M Aguilar
la source
0

Cela a fonctionné pour moi et permet une entrée dynamique de l'emplacement du fichier journal. le symbole & indique simplement à PowerShell que le texte est une ligne de code que je veux exécuter.

    $source = "E:\BackupToRestore\"
    $destination = "E:\RestoreMe\"
    $logfile = "E:\robocopyLogFile.txt"    
    $switches = ("/B", "/MIR", "/XJ", "/FFT", "/R:0", "/LOG:$logfile")
    & robocopy $source $destination $roboCopyString $switches
BriGuy
la source
0

Ajouter mes 2 cents à cela car cela peut aider quelqu'un ... Le code ci-dessous manque la partie "boucle" où je lis un csv pour obtenir les fichiers à copier

# first we setup an array of possible robocopy status
$RobocopyErrors="NO ERRORS",
            "OKCOPY",
            "XTRA",
            "OKCOPY + XTRA",
            "MISMATCHES",
            "OKCOPY + MISMATCHES",
            "MISMATCHES + XTRA",
            "OKCOPY + MISMATCHES + XTRA",
            "FAIL",
            "OKCOPY + FAIL",
            "FAIL + XTRA",
            "OKCOPY + FAIL + XTRA",
            "FAIL + MISMATCHES& goto end",
            "OKCOPY + FAIL + MISMATCHES",
            "FAIL + MISMATCHES + XTRA",
            "OKCOPY + FAIL + MISMATCHES + XTRA",
            "***FATAL ERROR***"
#setting some variables with the date
$DateLogFile=get-date -format "yyyyddMMhh"

#this commands below one usually goes into a loop
$DateLog=get-date -format "yyyyddMMhhmmss"

#adding options and command arg as described in previous post
$options = @("/R:0","/W:0")
$cmdArgs = @("$Source","$Destination",$File,$options)

#executing Robocopy command
robocopy @cmdArgs

# We capture the lastexitcode of the command and use to confirm if all was good or not

$errorlevel=$LASTEXITCODE
# I output the status to a log file
"$DateLog`t::$($RobocopyErrors[$errorlevel])::`"$file`"`t`"$Source`" -> `"$Destination`"" | out-file "$scriptPath\Logs\$DateLogFile.log" -append
 if ($errorlevel -lt 8) {
    #error below 8 = all is good

}
else {
    # something bad happened ..  

}
galadriann
la source