2013-07-05 29 views
11

Muszę skopiować wszystkie pliki *.doc (ale nie foldery, których nazwy odpowiadają *.doc) z folderu sieciowego \\server\source (w tym pliki we wszystkich zagnieżdżonych folderach) do folderu lokalnego C:\destination bez zachowania hierarchii zagnieżdżonych folderów (tzn. Wszystkie pliki powinny być bezpośrednio C:\destination i nie powinny być tworzone zagnieżdżone foldery w C:\destination). W przypadku, gdy istnieje kilka plików o tej samej nazwie z różnych podfolderów \\server\source, tylko pierwsza powinna zostać skopiowana i nigdy nie nadpisywana - wszystkie konflikty znalezione później powinny zostać pominięte (może być wiele takich przypadków, a pominięte pliki powinny nie mogą być przekazywane przez sieć, w przeciwnym razie zajmie to zbyt dużo czasu). Oto moja próba wdrożenia go w PowerShell:Jak kopiować określone pliki (bez hierarchii folderów), ale nie zastępuj istniejących plików?

cp \\server\source\* -Recurse -Include *.doc -Container:$false -Destination C:\destination 

Istnieją co najmniej dwa problemy z tym poleceniem:

  • It kopiuje foldery, których nazwy odpowiadają *.doc też.
  • W przypadku konfliktów nazw każdy plik znaleziony później jest przesyłany przez sieć i zastępuje poprzedni.

Czy możesz zasugerować, jak rozwiązać te problemy?
Implementacje przy użyciu copy, xcopy, robocopy, cscript lub *.bat, *.cmd są również mile widziane.
Lokalny system operacyjny to Windows 8, a system plików to NTFS.

+0

Jakie jest oczekiwane zachowanie, jeśli skrypt działa dwukrotnie? Czy powinien jeszcze raz skopiować wszystko? A może nie powinien niczego kopiować? –

+1

@splatteredbits Katalog docelowy może być początkowo pusty. Jeśli ten warunek nie powiedzie się, zachowanie skryptu może być niezdefiniowane. –

Odpowiedz

14

Najpierw powinienem sporządzić listę plików i zatwierdzić je podczas przeglądania listy.

coś takiego:

$srcdir = "\\server\source\"; 
$destdir = "C:\destination\"; 
$files = (Get-ChildItem $SrcDir -recurse -filter *.doc | where-object {-not ($_.PSIsContainer)}); 
$files|foreach($_){ 
    if (!([system.io.file]::Exists($destdir+$_.name))){ 
       cp $_.Fullname ($destdir+$_.name) 
    }; 
} 

więc używać Get-ChildItem do listy plików w folderze źródłowym pasujące filtr, rury przez where-object rozebrać katalogów na zewnątrz.

następnie przejść przez każdego pliku w foreach pętli i sprawdzić, czy nazwa pliku (nie fullname) istnieje w miejscu przeznaczenia za pomocą metody klasy system.io.file .NET Exists.

Jeśli nie, skopiuj, używając tylko oryginalnej nazwy pliku (zrzucając oryginalną ścieżkę).

Użyj opcji -whatif na kopii podczas testowania, więc to tylko pokazuje, co by to zrobić, w wyniku wypadku nie jest to, czego chciał :-)

+1

brakuje Ci nawiasu –

+0

Zamiast '[system.io.file] :: Exists', powinieneś także móc używać' Plik-testu' – jessehouwing

2
# Get all *.doc files under \\server\source 
Get-ChildItem -Path \\server\source *.doc -Recurse | 
    # Filter out directores 
    Where-Object { -not $_.PsIsContainer } | 
    # Add property for destination 
    Add-Member ScriptProperty -Name Destination -Value { Join-Path 'C:\destination' $this.Name } -PassThru | 
    # Filter out files that exist on the destination 
    Where-Object { -not (Test-Path -Path $_.Destination -PathType Leaf } | 
    # Copy. 
    Copy-Item 
0
$docFiles = Get-ChildItem -Path "\\server\source" -Recurse | Where-Object {$_.Attributes.ToString() -notlike "*Directory*" -and ($_.Name -like "*.doc" -or $_.Name -like "*.doc?")} | Sort-Object -Unique; 
$docFiles | ForEach-Object { Copy-Item -Path $_.fullname -Destination "C:\destination" }; 

Pierwsza linia czytać każdy *. plik doc i * .doc? (więc bierze pod uwagę także format .docx pakietu Office 2010), z wyłączeniem katalogów i duplikatów plików.
Druga linia kopiuje każdy element z miejsca docelowego do źródła (folder C: \ miejsce docelowe musi już istnieć).
Ogólnie proponuję podzielić polecenie na wiele linii, ponieważ łatwiej jest utworzyć kod (w tym przypadku pierwsze zadanie: pobierz pliki, drugie zadanie: skopiuj pliki).

6

Poprzednie odpowiedzi wydają mi się zbyt skomplikowane, chyba że coś nie rozumiem. To powinno działać:

Get-ChildItem "\\server\source\" *.doc -Recurse | ?{-not ($_.PSIsContainer -or (Test-Path "C:\Destination\$_"))} | Copy-Item -Destination "C:\Destination" 

Żaden z wbudowanych poleceń - kopiowanie, XCopy lub robocopy - zrobi to, co chcesz na własną rękę, ale tam jest narzędzie o nazwie XXCOPY tej woli, wygodnie dostępne w http://www.xxcopy.com. Ma wiele wbudowanych opcji specjalnie do spłaszczania drzew katalogów w jednym katalogu. Następujące będzie robić to, co opisano:

xxcopy "\\server\source\*.doc" "C:\Destination" /SGFO 

Jednak XXCOPY ma różne inne opcje do obsługi zduplikowanych nazw niż tylko kopiowanie pierwszy napotkał, takie jak dodawanie nazwy katalogu źródłowego do pliku, lub dodając kolejne identyfikuje liczbowych do wszystkich prócz tego pierwszego lub wszystkich oprócz najnowszych lub najstarszych. Zobacz tę stronę, aby uzyskać szczegółowe informacje: http://www.xxcopy.com/xxcopy16.htm

+0

Hmmm, naprawdę nie rozumiem tego zdania, chyba że ktoś zrobił wyjątek frazowanie pierwszej linii. Nie było to zamierzone jako zniewaga, po prostu czułem, że już opublikowane odpowiedzi przechodzą dodatkowe kroki w celu uzyskania czegoś prostszego. Czy prostota i zwięzłość nie są uważane za lepsze w kodowaniu, o ile nie rezygnujesz z jasności? W każdym razie OP wyraźnie powiedział, że chętnie przyjmie rozwiązania za pomocą poleceń kopiowania. Dostarczyłem informacje o narzędziu do kopiowania stron trzecich, które jest dostosowane do tego, co chce osiągnąć za pomocą jednego przełącznika. Jak to "nie jest użyteczne"? –

+0

Wypróbowałem twoją opcję (pierwszą) - jest dobra, ale spłaszcza hierarchię. – Archeg

+2

Spłaszczanie hierarchii było celem pytania: "bez zachowania hierarchii zagnieżdżonych folderów (tzn. Wszystkie pliki powinny przejść bezpośrednio do C: \ miejsce docelowe i nie zagnieżdżone foldery należy utworzyć w C: \ destination) ". –

1

Po co używać foreach, gdy już masz potok? Obliczone właściwości wygranej!

Get-ChildItem -Recurse -Path:\\Server\Path -filter:'*.doc' | 
    Where { -not $_.PSIsContainer } | 
    Group Name | 
    Select @{Name='Path'; Expression={$_.Group[0].FullName}},@{Name='Destination'; Expression={'C:\Destination\{0}' -f $_.Name}} | 
    Copy-Item