Jak uruchomić robocopy w PowerShell?

24

Próbuję użyć robocopy wewnątrz programu PowerShell, aby utworzyć kopię lustrzaną niektórych katalogów na moich komputerach domowych. Oto mój skrypt:

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

Skrypt pobiera plik csv z listą katalogów źródłowych i docelowych. Po uruchomieniu skryptu pojawiają się następujące błędy:

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

Masz pomysł, co muszę zrobić, aby to naprawić?

Dzięki, Mark.

Mark Allison
źródło

Odpowiedzi:

10

Jeśli zmienne $whati $optionsnie zmieniają się, to dlaczego są zmienne?

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

To działa dobrze dla mnie:

robocopy   $source $dest /COPYALL /B /SEC /MIR /R:0 /W:0 /NFL /NDL
Tatiana Racheva
źródło
Dobra uwaga - nie krępuj się. :)
Helvick
3
również / sec not / sec / ma tutaj krytyczne znaczenie
1
Więc właśnie ominąłeś / Log i zadziałało :-) Mam problem z dziwnymi błędami wywołującymi Robocopy z PS, gdy plik dziennika nie istnieje wcześniej.
icnivad
6
Wiem, że to stary post, ale myślę, że warto powiedzieć, że posiadanie ich jako zmiennych jest sygnałem dla przyszłych czytelników skryptu, że miały one być konfigurowalnymi częściami skryptu. Powodem tego jest to, że zdarza się, że naprawia literówkę w oryginalnym $ what.
Octopus
2
Nawet jeśli coś się nie zmienia, zmienne enkapsulacja może pomóc w zmniejszeniu złożoności skryptu. A jeśli okaże się, że muszę później odwoływać się do tej zmiennej, powoduje to, że skrypt jest bardziej dynamiczny
Kolob Canyon
20

Wypełnianie ciągów do parametrów poleceń zewnętrznych z poziomu programu PowerShell wymaga przeskakiwania tamborka, jeśli chcesz mieć możliwość używania rozszerzania zmiennych, a także aby wynikowa linia poleceń poprawnie zrozumiała, które parametry chcesz oddzielić, a które nie. W twoim przykładzie wysyłasz cały łańcuch jako pierwszy parametr, a Robocopy mówi ci, że nie może znaleźć tego długiego łańcucha jako katalogu źródłowego. Chcesz, aby cały ciąg źródłowy był traktowany jako jeden parametr (w tym spacje, które mogą tam być), podobnie jak w przypadku miejsca docelowego, ale opcje $ what i $ zawiodą, ponieważ oba zostaną dostarczone do Robocopy jako pojedynczy parametr, którego nie można przeanalizowane.

Istnieje kilka sposobów na zrobienie tego dobrze, ale poniższy fragment kodu jest oparty na sposobie, w jaki wydaje się, że chcesz rozbić parametry i działa dla przykładu z jednym katalogiem, którego użyłem.

$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
źródło
Dziękuję za odpowiedź. Próbowałem kodu, ale nadal pojawia się następujący błąd: BŁĄD: Nieprawidłowy parametr # 3: „/ COPYALL / B / SEC / MIR”
Mark Allison
Po uruchomieniu powyższego kodu pośredniczącego (skopiowanego z góry) działa on dokładnie zgodnie z oczekiwaniami - w szczególności wszystkie opcje \ jakie elementy są rozpoznawane jako osobne parametry. Jeśli uruchomisz tylko powyższy kod pośredniczący (z istniejącym folderem źródłowym), czy to działa? Jeśli tak, to sprawdź dwukrotnie cudzysłowy \ w swoim zmodyfikowanym kodzie. Nieprawidłowy parametr nr 3 wskazuje, że twoja wersja $, która jest nadal pojedynczym ciągiem, a nie tablicą oddzielnych parametrów w uruchomionym kodzie.
Helvick
2
+1. To zdecydowanie problem tutaj. Zabawne jest dla mnie to, że ludzie nieustannie próbują używać programu PowerShell, jakby to była stara powłoka oparta tylko na tekście ;-)
Joey
@Joey Czy znalazłeś model obiektów PowerShell, który sprawdza się lepiej w praktyce, niż tekstowy?
Didier A.
1
@DidierA .: Zdecydowanie. Zwłaszcza tam, gdzie istnieją obiekty opisać rzeczy, które oddziałują z, na przykład pliki, procesy, usługi, itd. Ale nawet jeśli masz tylko natywnych aplikacji i wyjść ciąg można po prostu użyć zwykłych operatorów ciągów, np -matchi -replacejak filtrowanie / cmdlet projekcyjne pracować z tym. Jest ogólnie dość dobrze zaprojektowany pod tym względem (większość *-Objectpoleceń jest ortogonalna i wszystkie można zastosować do dowolnego obiektu).
Joey,
3

Usunąć cytaty z linii robocopy?

to znaczy

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
źródło
Nie, próbowałem najpierw i nie zadziałało (podobne błędy), więc zmieniłem to na powyższe. Jakieś inne pomysły?
Mark Allison,
Jaka jest wydajność, jeśli uruchomisz ją bez cudzysłowów? Oryginalne wyjście pokazujące Źródło jako „P: \ C: \ Backup \ Photos \ COPYALL \ B \ SEC \ \ MIR \ R: 0 \ W: 0 \ NFL \ NDL \ LOG: MyLogfile.txt \” było tym, co narysowało mój uwaga na cytaty.
Bryan,
3

Miałem ten sam problem. Cały wiersz poleceń został zinterpretowany jako pierwszy argument.

Nic wyżej wspomnianego nie działało, w tym:

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

Pomijanie cudzysłowów nie działa, gdy na ścieżce jest miejsce:

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

Po wypróbowaniu sztuczek w http://edgylogic.com/blog/powershell-and-external-commands-done-right/ , w końcu go dostałem.

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

Może powinienem był trzymać pliki nietoperzy. :)

Justin
źródło
Czy Twój link do edgylogic został przeniesiony na slai.github.io/posts/… ?
Henrik Staun Poulsen
1

Odkryłem, że najlepszym sposobem na wykonanie natywnego polecenia jest użycie polecenia & do wykonania listy ciągów. Również jeśli chcesz, aby wyjście konsoli znalazło się w transkrypcie PowerShell, będziesz musiał przesłać wyjście do hosta zewnętrznego. Oto przykład wywołania 7Zip z wieloma parametrami pobranymi z 7-Zip do skryptu Powershell Amazon S3, który napisałem:

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

Zasadniczo w twoim przypadku spróbowałbym czegoś takiego (może być konieczne indywidualne wybranie parametrów $ what i $ options):

$robocopypath = "c:\pathtofolder\robocopy.exe"
& $robocopypath "$source" "$dest" "$what" "$options" "/LOG:MyLogfile.txt"
Greg Bray
źródło
Problem polega na tym, że kiedy powiesz „$ what” i „$ options” zostaną wysłane do robocopy jako pojedyncze parametry zamiast listy osobnych, a biorąc pod uwagę jego przykładowy kod, które będą musiały zostać wykreślone, nie ma mowy o to.
Helvick
Możesz użyć operatora splat, @jeśli używasz programu PowerShell v3, aby podzielić listę rozdzielaną przecinkami na stos argumentów
Zasz
0
invoke-expression "robocopy $source $dest $what $options /LOG:MyLogfile.txt"

Spowoduje to interpolację wszystkich zmiennych, a następnie wywoła powstały ciąg. Tak jak teraz, wygląda na to, że robocopy jest wywoływane z cudzysłowami wokół wszystkich opcji, tj. Robocopy "c: \ src c: \ dst bla meh". Jest to interpretowane jako pojedynczy parametr.

M. Aguilar
źródło
0

To działało dla mnie i pozwala na dynamiczne wprowadzanie lokalizacji pliku dziennika. Symbol & mówi po prostu PowerShellowi, że tekst jest wierszem kodu, który chcę uruchomić.

    $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
źródło
0

Dodanie do tego moich 2 centów, ponieważ może to komuś pomóc ... W poniższym kodzie brakuje części „pętli”, w której czytam plik csv, aby pliki zostały skopiowane

# 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
źródło