Wie bekomme ich Robocopy in PowerShell zum Laufen?

24

Ich versuche, Robocopy in Powershell zu verwenden, um einige Verzeichnisse auf meinen Heimcomputern zu spiegeln. Hier ist mein Drehbuch:

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

Das Skript nimmt eine CSV-Datei mit einer Liste von Quell- und Zielverzeichnissen auf. Wenn ich das Skript ausführe, erhalte ich folgende Fehlermeldungen:

-------------------------------------------------------------------------------
   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 !

Irgendeine Idee, was ich tun muss, um das Problem zu beheben?

Danke, Mark.

Mark Allison
quelle

Antworten:

10

Wenn sich die Variablen für $whatund $optionsnicht ändern, warum sind sie dann Variablen?

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

Das funktioniert gut für mich:

robocopy   $source $dest /COPYALL /B /SEC /MIR /R:0 /W:0 /NFL /NDL
Tatiana Racheva
quelle
Guter Punkt - halte es einfach. :)
Helvick
3
auch / sec nicht / sec / ist hier kritisch
1
Also hast du einfach das / Log übersprungen und es hat funktioniert :-) Gotcha ist, dass du seltsame Fehler beim Aufruf von Robocopy mit PS bekommst, wenn das Logfile vorher nicht existiert.
Icnivad
6
Ich weiß, dass dies ein alter Beitrag ist, aber ich denke, es lohnt sich zu erwähnen, dass diese als Variablen ein Signal für zukünftige Leser des Skripts sind, dass diese konfigurierbare Teile des Skripts sein sollten. Der Grund dafür ist, dass es passiert, dass der Tippfehler im ursprünglichen $ what behoben wird.
Octopus
2
Auch wenn sich etwas nicht ändert, kann die variable Kapselung dazu beitragen, ein Skript weniger kompliziert zu machen. Und wenn sich herausstellt, dass ich später noch einmal auf diese Variable verweisen muss, wird das Skript dynamischer
Kolob Canyon,
20

Das Auffüllen von Zeichenfolgen in Parameter für externe Befehle aus Powershell erfordert einen gewissen Rahmensprung, wenn Sie die Variablenerweiterung verwenden möchten und die resultierende Befehlszeile genau verstehen soll, welche Parameter Sie trennen möchten und welche nicht. In Ihrem Beispiel senden Sie die gesamte Zeichenfolge als ersten Parameter und Robocopy teilt Ihnen mit, dass die lange Zeichenfolge nicht als Quellverzeichnis gefunden werden kann. Sie möchten, dass die gesamte Quellzeichenfolge als ein Parameter behandelt wird (einschließlich der Leerzeichen, die sich möglicherweise darin befinden), ebenso für Destination, aber Ihre Optionen $ what und $ schlagen fehl, da beide als ein einziger Parameter an Robocopy übergeben werden, der nicht vorhanden sein kann analysiert.

Es gibt einige Möglichkeiten, dies richtig zu machen, aber das folgende Snippet basiert auf der Art und Weise, wie Sie Ihre Parameter aufschlüsseln möchten, und funktioniert für das einzelne Verzeichnis, das ich verwendet habe.

$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
quelle
Danke für die Antwort. Ich habe Ihren Code ausprobiert, erhalte aber immer noch die folgende Fehlermeldung: FEHLER: Ungültiger Parameter Nr. 3: "/ COPYALL / B / SEC / MIR"
Mark Allison
Wenn ich den obigen Stub-Code (von oben kopiert) ausführe, funktioniert er genau wie erwartet - insbesondere alle Optionen, \ welche Elemente als separate Parameter erkannt werden. Wenn Sie nur den obigen Stub ausführen (mit einem vorhandenen Quellordner), funktioniert das? Wenn ja, überprüfen Sie die Anführungszeichen \ escaping in Ihrem eigenen geänderten Code. Der ungültige Parameter 3 gibt an, dass Ihre Version von $ what immer noch eine einzelne Zeichenfolge und kein Array separater Parameter in dem von Ihnen ausgeführten Code ist.
Helvick
2
+1. Das ist definitiv das Problem hier. Ich finde es witzig, wie die Leute ständig versuchen, PowerShell so zu benutzen, als wäre es eine alte Shell, die nur auf Text basiert ;-)
Joey,
@Joey Haben Sie festgestellt, dass das Objektmodell von Powershell in der Praxis besser funktioniert als die textbasierten?
Didier A.
1
@ DidierA .: Auf jeden Fall. Insbesondere dort, wo Objekte vorhanden sind, um die Dinge zu beschreiben, mit denen Sie interagieren, z. B. Dateien, Prozesse, Dienste usw. Aber auch wenn Sie nur native Anwendungen und String-Ausgaben haben, können Sie nur die üblichen String-Operatoren verwenden, z. B. -matchund -replacesowie Filter- / Projektions-Cmdlets damit zu arbeiten. In dieser Hinsicht ist es im Allgemeinen recht gut gestaltet (die meisten *-ObjectBefehle sind orthogonal und können auf jedes Objekt angewendet werden).
Joey
3

Entfernen Sie die Anführungszeichen aus der Robocopy-Zeile?

dh

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
quelle
Nein, das habe ich zuerst versucht und es hat nicht funktioniert (ähnliche Fehler), also habe ich es in das geändert, was oben gezeigt wird. Irgendwelche anderen Ideen?
Mark Allison
Was ist die Ausgabe, wenn Sie es ohne Anführungszeichen ausführen? Ihre ursprüngliche Ausgabe, in der die Quelle als "P: \ C: \ Backup \ Photos \ COPYALL \ B \ SEC \ \ MIR \ R: 0 \ W: 0 \ NFL \ NDL \ LOG: MyLogfile.txt \" angezeigt wurde, war das, was meine Aufmerksamkeit auf die Zitate.
Bryan
3

Ich hatte das gleiche problem Die gesamte Befehlszeile wurde als erstes Argument interpretiert.

Nichts oben Erwähntes hat funktioniert, einschließlich:

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

Das Weglassen der Anführungszeichen funktioniert nicht, wenn der Pfad ein Leerzeichen enthält:

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

Nachdem ich die Tricks in http://edgylogic.com/blog/powershell-and-external-commands-done-right/ ausprobiert hatte , bekam ich es endlich.

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

Vielleicht hätte ich mich an Fledermausfeilen festhalten sollen. :)

Justin
quelle
Ist Ihr Link zu edgylogic auf slai.github.io/posts/… umgezogen ?
Henrik Staun Poulsen
1

Ich habe festgestellt, dass die beste Möglichkeit, einen nativen Befehl auszuführen, darin besteht, mit dem Befehl & eine Liste von Zeichenfolgen auszuführen. Auch wenn Sie möchten, dass die Konsolenausgabe in ein Powershell-Transkript aufgenommen wird, müssen Sie die Ausgabe an den Out-Host weiterleiten. Hier ist ein Beispiel für den Aufruf von 7Zip mit mehreren Parametern, die aus einem von mir geschriebenen 7-Zip-Powershell-Skript für Amazon S3 stammen :

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

Grundsätzlich würde ich für Ihren Fall so etwas versuchen (muss möglicherweise die Parameter $ what und $ options einzeln aufschlüsseln):

$robocopypath = "c:\pathtofolder\robocopy.exe"
& $robocopypath "$source" "$dest" "$what" "$options" "/LOG:MyLogfile.txt"
Greg Bray
quelle
Das Problem dabei ist, dass, wenn Sie "$ what" und "$ options" sagen, diese als einzelne Parameter an robocopy gesendet werden und nicht als Liste von separaten Parametern, und angesichts seines Beispielcodes müssen sie herausgebrochen werden es.
Helvick
Sie können den splat-Operator verwenden, @wenn Sie Powershell v3 verwenden, um eine durch Kommas getrennte Liste in einen Argumentenstapel
aufzuteilen
0
invoke-expression "robocopy $source $dest $what $options /LOG:MyLogfile.txt"

Dadurch werden alle Variablen interpoliert und die resultierende Zeichenfolge aufgerufen. So wie Sie es jetzt haben, sieht es so aus, als würde Robocopy mit den Anführungszeichen um alle Optionen aufgerufen, dh Robocopy "c: \ src c: \ dst blah meh". Dies wird als einzelner Parameter interpretiert.

M Aguilar
quelle
0

Dies hat bei mir funktioniert und ermöglicht eine dynamische Eingabe des Speicherorts der Protokolldatei. Das Symbol & weist PowerShell lediglich darauf hin, dass der Text eine Codezeile ist, die ich ausführen möchte.

    $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
quelle
0

Das Hinzufügen meiner 2 Cent dazu, da es jemandem helfen könnte ... Im folgenden Code fehlt der "Schleifen" -Teil, in dem ich eine CSV-Datei lese, um die zu kopierenden Dateien zu erhalten

# 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
quelle