Come far funzionare robocopy in PowerShell?


24

Sto cercando di usare Robocopy all'interno di PowerShell per eseguire il mirroring di alcune directory sui miei computer di casa. Ecco la mia sceneggiatura:

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

Lo script contiene un file CSV con un elenco di directory di origine e di destinazione. Quando eseguo lo script ottengo questi errori:

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

Qualche idea su cosa devo fare per risolvere?

Grazie Mark.

Risposte:


10

Se le variabili per $whate $optionsnon cambiano, perché sono variabili?

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

Questo funziona bene per me:

robocopy   $source $dest /COPYALL /B /SEC /MIR /R:0 /W:0 /NFL /NDL

Un buon punto: mantienilo semplice. :)
Helvick

3
anche / sec not / sec / è fondamentale qui

1
Quindi hai semplicemente saltato il / Log e ha funzionato :-) Gotcha è che ricevi strani errori che chiamano Robocopy con PS quando il Logfile non esiste prima.
icnivad,

6
So che questo è un vecchio post, ma penso che valga la pena dire che avere questi come variabili è un segnale per i futuri lettori della sceneggiatura che questi dovevano essere parti configurabili della sceneggiatura. Il motivo per cui funziona è che capita di correggere l'errore di battitura nell'originale $ what.
Polpo,

2
Anche se qualcosa non cambia, l'incapsulamento variabile può aiutare a rendere uno script meno contorto. E se risulta che dovrò fare nuovamente riferimento a quella variabile più tardi, rende la sceneggiatura più dinamica
Kolob Canyon

20

Il popolamento di stringhe in parametri per comandi esterni dall'interno di Powershell richiede alcuni salti del telaio se si desidera essere in grado di utilizzare l'espansione variabile e anche la riga di comando risultante comprende correttamente quali parametri si desidera separare e quali no. Nel tuo esempio stai inviando l'intera stringa come primo parametro e Robocopy ti sta dicendo che non riesce a trovare quella lunga stringa come directory di origine. Vuoi che l'intera stringa di Source sia trattata come un parametro (inclusi gli spazi che potrebbero essere lì), allo stesso modo per Destination ma le tue opzioni $ what e $ falliranno perché saranno entrambe consegnate a Robocopy come un singolo parametro che non può essere analizzato.

Esistono alcuni modi per farlo nel modo giusto, ma il frammento seguente si basa sul modo in cui sembri voler rompere i parametri e funziona per il singolo esempio di directory che ho usato.

$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

Grazie per la risposta. Ho provato il tuo codice ma ho ancora il seguente errore: ERRORE: parametro 3 non valido: "/ COPYALL / B / SEC / MIR"
Mark Allison,

Quando eseguo il codice stub sopra (copiato dall'alto) funziona esattamente come previsto, in particolare tutte le opzioni \ quali elementi sono riconosciuti come parametri separati. Se si esegue solo lo stub sopra (con una cartella di origine esistente) funziona? In tal caso, ricontrolla le virgolette \ escape nel tuo codice modificato. Il parametro 3 non valido indica che la tua versione di $, che è ancora una singola stringa, non una matrice di parametri separati nel codice che stai eseguendo.
Helvick,

2
+1. Questo è sicuramente il problema qui. Trovo divertente il modo in cui le persone cercano costantemente di usare PowerShell come se fosse una vecchia shell basata solo su testo ;-)
Joey

@Joey Hai trovato il modello a oggetti di PowerShell per funzionare in pratica meglio di quelli basati su testo?
Didier A.

1
@DidierA .: Sicuramente. Soprattutto laddove esistono oggetti per descrivere le cose con cui interagisci, ad esempio file, processi, servizi, ecc. Ma anche quando hai solo applicazioni native e output di stringhe puoi semplicemente usare i soliti operatori di stringhe, ad es. -matchE -replaceanche i cmdlet di filtro / proiezione lavorare con quello. È generalmente abbastanza ben progettato in tal senso (la maggior parte dei *-Objectcomandi sono ortogonali e possono essere applicati a qualsiasi oggetto).
Joey,

3

Rimuovere le virgolette dalla linea di robocopy?

vale a 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
}

No, l'ho provato per primo e non ha funzionato (errori simili), quindi l'ho cambiato in quanto mostrato sopra. Altre idee?
Mark Allison,

Qual è l'output quando lo esegui senza virgolette? L'output originale che mostrava l'origine come "P: \ C: \ Backup \ Photos \ COPYALL \ B \ SEC \ \ MIR \ R: 0 \ W: 0 \ NFL \ NDL \ LOG: MyLogfile.txt \" era ciò che ha disegnato il mio attenzione alle citazioni.
Bryan,

3

Ho avuto lo stesso problema. L'intera riga di comando è stata interpretata come il primo argomento.

Nulla di cui sopra ha funzionato tra cui:

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

Lasciare le virgolette non funziona quando c'è uno spazio nel percorso:

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

Dopo aver provato i trucchi su http://edgylogic.com/blog/powershell-and-external-commands-done-right/ , ho finalmente capito.

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

Forse avrei dovuto rimanere bloccato con i file bat. :)


Il tuo link a edgylogic è stato spostato su slai.github.io/posts/… ?
Henrik Staun Poulsen il

1

Ho scoperto che il modo migliore per eseguire un comando nativo è usare il comando & per eseguire un elenco di stringhe. Inoltre, se si desidera che l'output della console sia incluso in una trascrizione PowerShell, sarà necessario reindirizzare l'output all'host esterno. Ecco un esempio di come chiamare 7Zip con più parametri presi da un 7-Zip ad Amazon S3 Powershell Script che ho scritto:

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

Fondamentalmente per il tuo caso proverei qualcosa di simile (potrebbe essere necessario suddividere i parametri $ $ e $ singolarmente):

$robocopypath = "c:\pathtofolder\robocopy.exe"
& $robocopypath "$source" "$dest" "$what" "$options" "/LOG:MyLogfile.txt"

Il problema con questo è che come dici "$ what" e "$ options" verranno inviati a robocopy come parametri singoli piuttosto che un elenco di quelli separati e dato il suo codice di esempio dovranno essere scomposti, non ci sono dubbi esso.
Helvick,

Puoi usare l'operatore splat @se stai usando powershell v3 per dividere un elenco separato da virgole in uno stack di argomenti
Zasz

0
invoke-expression "robocopy $source $dest $what $options /LOG:MyLogfile.txt"

Ciò interpolerà tutte le variabili, quindi chiamerà la stringa risultante. Il modo in cui lo hai ora, sembra che robocopy sia invocato con le virgolette intorno a tutte le opzioni, vale a dire, robocopy "c: \ src c: \ dst blah meh". Questo viene interpretato come un singolo parametro.


0

Questo ha funzionato per me e consente un input dinamico della posizione del file di registro. il simbolo & dice a PowerShell che il testo è una riga di codice che voglio eseguire.

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

0

Aggiungendo i miei 2 centesimi a questo in quanto potrebbe aiutare qualcuno ... Nel codice sottostante manca la parte "loop" in cui ho letto un CSV per ottenere i file da copiare

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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.