¿Cómo hacer que se ejecute robocopy en powershell?

24

Estoy tratando de usar robocopy dentro de powershell para reflejar algunos directorios en mis máquinas domésticas. Aquí está mi guión:

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

El script toma un archivo csv con una lista de directorios de origen y destino. Cuando ejecuto el script obtengo estos errores:

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

¿Alguna idea de lo que necesito hacer para arreglar?

Gracias Mark.

Mark Allison
fuente

Respuestas:

10

Si las variables para $whaty $optionsno cambian, ¿por qué son variables?

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

Esto funciona bien para mi:

robocopy   $source $dest /COPYALL /B /SEC /MIR /R:0 /W:0 /NFL /NDL
Tatiana Racheva
fuente
Buen punto: manténgalo simple. :)
Helvick
3
también / sec not / sec / es crítico aquí
1
Así que simplemente omitió el / Log y funcionó :-) Lo que ocurre es que obtiene errores extraños al llamar a Robocopy con PS cuando el Logfile no existe de antemano.
icnivad
66
Sé que esta es una publicación antigua, pero creo que vale la pena decir que tenerlas como variables es una señal para los futuros lectores del guión de que estas deben ser partes configurables del guión. La razón por la que esto funciona es que sucede para corregir el error tipográfico en el $ what original.
Octopus
2
Incluso si algo no está cambiando, la encapsulación variable puede ayudar a que un script sea menos complicado. Y si resulta que necesito hacer referencia a esa variable nuevamente más adelante, hace que el guión sea más dinámico
Kolob Canyon,
20

Completar cadenas en parámetros para comandos externos desde Powershell requiere un salto de aro si desea poder utilizar la expansión variable y también hacer que la línea de comandos resultante entienda correctamente qué parámetros desea separar y cuáles no. En su ejemplo, está enviando la cadena completa como primer parámetro y Robocopy le dice que no puede encontrar esa cadena larga como directorio de origen. Desea que toda la cadena de origen se trate como un parámetro (incluidos los espacios que pueden estar allí), del mismo modo para el destino, pero sus opciones $ what y $ fallarán porque ambas se entregarán a Robocopy como un parámetro único que no puede ser analizado

Hay algunas maneras de hacer esto bien, pero el siguiente fragmento se basa en la forma en que parece querer desglosar sus parámetros y funciona para el ejemplo de directorio único que he usado.

$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
fuente
Gracias por la respuesta. He intentado su código pero sigo teniendo el siguiente error: ERROR: Parámetro no válido 3: "/ COPYALL / B / SEC / MIR"
Mark Allison el
Cuando ejecuto el código auxiliar anterior (copiado desde arriba) funciona exactamente como se esperaba, específicamente todas las opciones \ qué elementos se reconocen como parámetros separados. Si ejecuta solo el código auxiliar anterior (con una carpeta fuente que existe) ¿funciona? Si es así, verifique las Citas \ escapándose en su propio código modificado. El parámetro no válido n. ° 3 indica que su versión de $, que sigue siendo una sola cadena, no una matriz de parámetros separados en el código que está ejecutando.
Helvick
2
+1. Ese es definitivamente el problema aquí. Me parece curioso cómo la gente trata constantemente de usar PowerShell como si fuera un antiguo shell basado en texto ;-)
Joey
@Joey ¿Has encontrado que el modelo de objetos de powershell funciona mejor en la práctica que los basados ​​en texto?
Didier A.
1
@DidierA .: Definitivamente. Especialmente donde existen objetos para describir las cosas con las que interactúa, por ejemplo, archivos, procesos, servicios, etc. Pero incluso cuando solo tiene aplicaciones nativas y salidas de cadena, solo puede usar los operadores de cadena habituales, por ejemplo, -matchy -replacetambién cmdlets de filtrado / proyección para trabajar con eso. En general, está bastante bien diseñado en ese sentido (la mayoría de los *-Objectcomandos son ortogonales y todos se pueden aplicar a cualquier objeto).
Joey
3

¿Eliminar las comillas de la línea robocopy?

es decir

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
fuente
No, intenté eso primero y no funcionó (errores similares), así que lo cambié a lo que se muestra arriba. ¿Alguna otra idea?
Mark Allison el
¿Cuál es el resultado cuando lo ejecutas sin comillas? Su salida original que muestra la Fuente como "P: \ C: \ Backup \ Photos \ COPYALL \ B \ SEC \ \ MIR \ R: 0 \ W: 0 \ NFL \ NDL \ LOG: MyLogfile.txt \" fue lo que atrajo a mi Atención a las citas.
Bryan
3

Yo tuve el mismo problema. Toda la línea de comando fue interpretada como el primer argumento.

Nada de lo mencionado anteriormente funcionó, incluyendo:

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

Dejar las comillas no funciona cuando hay un espacio en el camino:

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

Después de probar los trucos en http://edgylogic.com/blog/powershell-and-external-commands-done-right/ , finalmente lo entendí.

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

Tal vez debería haberme quedado con los archivos bat. :)

Justin
fuente
¿Su enlace a edgylogic se ha movido a slai.github.io/posts/… ?
Henrik Staun Poulsen
1

He descubierto que la mejor manera de ejecutar un comando nativo es usar el comando & para ejecutar una lista de cadenas. Además, si desea que la salida de la consola se incluya en una transcripción de PowerShell, deberá canalizar la salida a un host externo. Aquí hay un ejemplo de cómo llamar a 7Zip con múltiples parámetros tomados de un 7-Zip a Amazon S3 Powershell Script que escribí:

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

Básicamente para su caso, intentaría algo como esto (puede que necesite separar los parámetros $ what y $ options individualmente):

$robocopypath = "c:\pathtofolder\robocopy.exe"
& $robocopypath "$source" "$dest" "$what" "$options" "/LOG:MyLogfile.txt"
Greg Bray
fuente
El problema con esto es que cuando dices "$ what" y "$ options" se enviarán a robocopy como parámetros únicos en lugar de una lista de valores separados y, dado su código de muestra, tendrán que desglosarse, no hay ninguna posibilidad eso.
Helvick
Puede usar el operador splat @si está usando powershell v3 para dividir una lista separada por comas en una pila de argumentos
Zasz
0
invoke-expression "robocopy $source $dest $what $options /LOG:MyLogfile.txt"

Esto interpolará todas las variables, luego llamará a la cadena resultante. De la forma en que lo tiene ahora, parece que se está invocando robocopy con las comillas alrededor de todas las opciones, es decir, robocopy "c: \ src c: \ dst blah meh". Esto se interpreta como un solo parámetro.

M Aguilar
fuente
0

Esto funcionó para mí y permite una entrada dinámica de la ubicación del archivo de registro. el símbolo & solo le dice a PowerShell que el texto es una línea de código que quiero ejecutar.

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

Agregar mis 2 centavos a esto, ya que puede ayudar a alguien ... Al código siguiente le falta la parte del "bucle" donde leo un csv para que los archivos se copien

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