none
Verzeichnisse vergleichen mit PowerShell

    Frage

  • Hallo,

    an folgender Aufgabenstellung bin ich gescheitert und wir alle können an der Lösungsfindung wachsen :-)

    - Ordner A hat mehrere Unterordner und wieder Unterordner und ... - in diesem Verzeichnisbaum liegen verschiedene Dateien (irgendwo)

    - dann hab ich einen Ordner B - in diesem Ordner liegen ebenfalls Dateien (ohne Unterordner)

    - nun will ich 1. aus B eine Datei nehmen 2. mir die Bezeichnung dieser Datei anschauen 3. in Ordner A gehen 4. in A die Datei finden, die die gleiche Bezeichnung hat wie aus 2. 5. wenn in A etwas gefunden wurde, wird Datei aus A durch Datei aus B ersetzt

    $origin = "C:\Pfad\B"
    $target = "C:\Pfad\A"
    $files_origin = Get-ChildItem -File $origin
    $filenames_target = Get-ChildItem -File $target | Split-Path -leaf
    foreach ($file in $files_origin) {
        $filename = Split-Path $file -leaf
        if ($filenames_target.contains($filename)) {
            cp $file $target
        }
    }


    Ich laufe in einen Fehler:

    You cannot call a method on a null-valued expression.
    At line:7 char:9
    +     if ($filenames_target.contains($filsename)) {
    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

    Des weiteren sind Unterordner auch noch nicht berücksichtigt :-(

    Ich bin um Rat sehr dankbar!

    Freitag, 14. September 2018 07:47

Antworten

  • Schulnote ist bisher 2+,

    Nee nee nee neee ...  das ist bisher ne 1 und wird gleich ne 1+. Wenn der Lehrer es nämlich nicht korrekt erklärt und sogar wichtige Details weglässt, darf das nicht zum Nachteil ausgelegt werden!!!!! :-P

    $origin = 'C:\Pfad\B'
    $target = 'C:\Pfad\A'
    
    $files_origin = Get-ChildItem -Path $origin -Recurse -Force -Exclude *.docx
    $filenames_target = Get-ChildItem -Path $target -Recurse -Force -Exclude *.docx
    
    Compare-Object -ReferenceObject $filenames_target -DifferenceObject $files_origin -PassThru -IncludeEqual |
        ForEach-Object {
            $sourceFile = Join-Path -Path $origin -ChildPath $_.Name
            Copy-Item -Path $sourceFile -Destination $_.Directory -Force 
        }

    So, ich muss jetzt los ... wenn das immernoch nicht passt, musst Du Dir bis Dienstag allein weiterhelfen ... oder einer von den anderen Kollegen hier macht weiter.

    Schönes WE!


    Best regards,

    (79,108,97,102|%{[char]$_})-join''


    Freitag, 14. September 2018 12:51

Alle Antworten

  • Anderweitig habe ich folgenden Code in nicht PowerShell gefunden, welcher passen sollte (vielleicht hilft diese Info)

    for f in B/* 
    do 
      file=${f/*\/}
      find A -type f -execdir cp "$f" . ";" 
    done

    Freitag, 14. September 2018 07:59
  • Ich würde es so machen ...
    $origin = 'C:\Pfad\B'
    $target = 'C:\Pfad\A'
    
    $files_origin = Get-ChildItem -Path $origin -File -Recurse -Force
    $filenames_target = Get-ChildItem -Path $target -File -Recurse -Force
    
    Compare-Object -ReferenceObject $files_origin -DifferenceObject $filenames_target -PassThru -IncludeEqual -ExcludeDifferent |
        Copy-Item -Destination $target -Force
      ...  bitte erst mit Testdaten probieren!  ;-)

    Best regards,

    (79,108,97,102|%{[char]$_})-join''


    • Bearbeitet BOfH_666 Freitag, 14. September 2018 08:11
    Freitag, 14. September 2018 08:05
  • twinson_999,

    willkommen im Deutschen Powershell Forum.

    ...   Code in nicht PowerShell gefunden...

    Neeee .... wir sind hier im Powershell Forum!!  ... wenn überhaupt, dann ersetzen wir cmd/batch oder allen anderen Code durch Powershell Code und nicht andersrum.  :-D  Überlicherweise ist Powershell sowieso viel mächtiger und auch einfacher zu lesen als cmd/batch.  ;-)


    Best regards,

    (79,108,97,102|%{[char]$_})-join''

    Freitag, 14. September 2018 08:15
  • Hallo BOfH_666,

    erst einmal Danke für deine Unterstützung.

    Wenn ich es auf ein Test-Verzeichnis anwende, erhalte ich folgende Mitteilung:

    Compare-Object : Cannot bind argument to parameter 'DifferenceObject' because it is null.
    At line:7 char:65
    + Compare-Object -ReferenceObject $files_origin -DifferenceObject $filenames_targe ...
    +                                                                 ~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidData: (:) [Compare-Object], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.CompareObjectCommand
    

    Hast du hierzu eine Idee?

    Ich würde es so machen ...

    $origin = 'C:\Pfad\B'
    $target = 'C:\Pfad\A'
    
    $files_origin = Get-ChildItem -Path $origin -File -Recurse -Force
    $filenames_target = Get-ChildItem -Path $target -File -Recurse -Force
    
    Compare-Object -ReferenceObject $files_origin -DifferenceObject $filenames_target -PassThru -IncludeEqual -ExcludeDifferent |
        Copy-Item -Destination $target -Force

      ...  bitte erst mit Testdaten probieren!  ;-)


    Best regards,

    (79,108,97,102|%{[char]$_})-join''



    Freitag, 14. September 2018 10:23
  • Ich muss noch einmal prüfen, ich hatte nur ein schlechtes Beispiel gewählt (unter A und B war kein identisches Dokument)
    Freitag, 14. September 2018 10:26
  • Ok, es werden Datein von A nach B kopiert, jedoch wird unter B nicht das passende Gegenstück/Dokument, welches sich unter einer Sub-Struktur befindet, gesucht und ersetzt (lediglich auf die selbige Ebene (wie unter A) kopiert)


    • Bearbeitet twinson_999 Freitag, 14. September 2018 10:30
    Freitag, 14. September 2018 10:28
  • Drehe ich target und origin, dann werden aus dem Ordner mit der Ordner-Struktur (und die darunter befindlichen Dokumente) in die flache Struktur (ohne Ordner) kopiert.

    Eigentlich müsste es nur noch Spiegelverkehrt angewendet werden

    Freitag, 14. September 2018 10:39
  • Häh? ... ich bin verwirrt ... von wo nach wo soll kopiert werden?  ... egal ...

    Ich erklär einfach mal die generelle Vorgehensweise und Du kannst Dir dann den Code so zusammenklöppeln, wie Du es brauchst.

    Du erstellst Dir mit den beiden Get-ChildItem Befehlen quasi zwei Tabellen. Darin enthalten sind die Eigenschaften der Dateien. Also Name (mit Endung), BaseName (ohne Endung), FullName (incl. Pfad), Length (Größe), LastWriteTime (Zeitpunkt der letzten Änderung) usw ....

    Mit Compare-Object kannst Du Diese beiden Tabellen vergleichen. Du kannst angeben, welche Eigenschaft dafür benutzt wird - im Standard ist es wohl der Name. Du kannst angeben, ob Du die Unterschiede sehen willst oder die Gemeinsamkeiten oder alles. Und Du kannst noch angeben, dass das Eingangs-Object in die Ausgabe eingeschlossen wird. Damit hast Du dann alle "hintenraus" benötigten Informationen - denke ich.

    Diese Ausgabe benutzt Du dann einfach als Basis für die entsprechenden Kopieraktionen. Lass Dir einfach mal nur alles ausgeben - dann siehst Du, womit Du weiterarbeiten kannst.

    Vielleicht musst Du auch Referenz- und Differenz-Object tauschen - es kann sein, dass ich Deine anfängliche Erklärung "falschrum" verstanden habe.  ;-)  :-D

    Best regards,

    (79,108,97,102|%{[char]$_})-join''


    • Bearbeitet BOfH_666 Freitag, 14. September 2018 10:43
    Freitag, 14. September 2018 10:42
  • Drehe ich target und origin, ........

    Eigentlich müsste es nur noch Spiegelverkehrt angewendet werden

    ... endlich mal wider jemand, der mitdenkt und selbst ein bissl rumprobiert .... großartig. ;-) :-D

    Danke.


    Best regards,

    (79,108,97,102|%{[char]$_})-join''


    • Bearbeitet BOfH_666 Freitag, 14. September 2018 10:47
    Freitag, 14. September 2018 10:46
  • Nochmals lieben Dank für die genommene Zeit.

    Wenn ich deinen Erklärrung richtig folgen konnte, beschreibt das PS das kopieren auf identische Ebene zwischen A und B.

    Leider kann ich noch keine Grafiken oder Links einfügen (als "neuer Nutzer"), sodass ich meine gerade erstellte Grafik nicht einfügen kann.


    Ich probiere es zu umschreiben:

    Unter origin liegt:
    C:\A\file111.pdf
    C:\A\file222.pdf
    C:\A\file333.pdf

    Unter target liegt:
    C:\B\ORDNER1\file111.pdf
    C:\B\ORDNER2\file222.pdf
    C:\B\file333.pdf

    Alle Dokumente unter origin sollen die Dokumente unter target ersetzen, egal ob diese auf der identischen, oder einer sub-struktur liegen.

    • Bearbeitet twinson_999 Freitag, 14. September 2018 11:26
    Freitag, 14. September 2018 11:22
  • OK ... von hinten durch die Brust ins Auge ... ich glaub, ich hab mir grad einen Knoten in meine linke Hemisphäre gemacht.  ;-) :-D ....

    Ja, da müssen wir dann eben ein bissl tricksen ... so sollte es aber flutschen:

    $origin = 'C:\Pfad\B'
    $target = 'C:\Pfad\A'
    
    $files_origin = Get-ChildItem -Path $origin -File -Recurse -Force
    $filenames_target = Get-ChildItem -Path $target -File -Recurse -Force
    
    Compare-Object -ReferenceObject $filenames_target -DifferenceObject $files_origin -PassThru -IncludeEqual -ExcludeDifferent |
        ForEach-Object {
            $sourceFile = Join-Path -Path $origin -ChildPath $_.Name
            Copy-Item -Path $sourceFile -Destination $_.Directory -Force 
        }


    Best regards,

    (79,108,97,102|%{[char]$_})-join''


    • Bearbeitet BOfH_666 Freitag, 14. September 2018 11:42
    Freitag, 14. September 2018 11:42
  • Jeden Tag eine gute Tat.

    Ich möchte mich noch nicht zu sehr freuen, aber der erste Test war erfolgreich :-)

    Ich teste weiter, freue mich aber bereits riesig, sollte es sich bewahrheiten!

    Vielen Dank soweit und nun geht es ans testen...

    Freitag, 14. September 2018 11:48
  • Schulnote ist bisher 2+, weshalb 2+; ich habe noch eine Kleinigkeit.

    Wenn ein Dokument mehrfach unter target verwendet wird, jedoch nur 1x unter origin (geht ja auch nicht anders) genannt ist, werden nicht alle Dokumente (unter target) sondern nur das zuerst gefundene ersetzt.

    Gibt es eine Möglichkeit, eine Datei, welche unter origin genannt ist prinziepell auszuschließen (-Exclude?)

    Hast du noch eine abschlißende Idee, damit ich eine 1+ vergeben kann?




    • Bearbeitet twinson_999 Freitag, 14. September 2018 12:32
    Freitag, 14. September 2018 12:15
  • Schulnote ist bisher 2+,

    Nee nee nee neee ...  das ist bisher ne 1 und wird gleich ne 1+. Wenn der Lehrer es nämlich nicht korrekt erklärt und sogar wichtige Details weglässt, darf das nicht zum Nachteil ausgelegt werden!!!!! :-P

    $origin = 'C:\Pfad\B'
    $target = 'C:\Pfad\A'
    
    $files_origin = Get-ChildItem -Path $origin -Recurse -Force -Exclude *.docx
    $filenames_target = Get-ChildItem -Path $target -Recurse -Force -Exclude *.docx
    
    Compare-Object -ReferenceObject $filenames_target -DifferenceObject $files_origin -PassThru -IncludeEqual |
        ForEach-Object {
            $sourceFile = Join-Path -Path $origin -ChildPath $_.Name
            Copy-Item -Path $sourceFile -Destination $_.Directory -Force 
        }

    So, ich muss jetzt los ... wenn das immernoch nicht passt, musst Du Dir bis Dienstag allein weiterhelfen ... oder einer von den anderen Kollegen hier macht weiter.

    Schönes WE!


    Best regards,

    (79,108,97,102|%{[char]$_})-join''


    Freitag, 14. September 2018 12:51
  • Super und der "Lehrer" (obwohl ich ja der jenige bin der von Dir lernt) gesteht seine Schande.

    Hat funktioniert und die 1+ steht im Notenbuch.

    Einzig die Fragestelltung wie kann ich ein Dokument nun ignorieren steht aus.

    Dir auch ein schönes Wochenende und bis Dienstag
    • Bearbeitet twinson_999 Freitag, 14. September 2018 13:22
    Freitag, 14. September 2018 13:22
  • ... (obwohl ich ja der jenige bin der von Dir lernt) ....

    ... hmmm ... Du hast angefangen mit dem Zensuren-Dingsbums ... ;-)

    Einzig die Fragestelltung wie kann ich ein Dokument nun ignorieren steht aus.

    Was für ein Dokument? ... und was meinst Du mit ignorieren?


    Best regards,

    (79,108,97,102|%{[char]$_})-join''

    Dienstag, 18. September 2018 00:03
  • Hallo noch einmal,

    ich habe leider bisher noch immer keine Möglichkeit gefunden, eine Datei, welche unter Origin genannt ist, auszuschließen.

    Hast du noch eine Idee auf Lager?

    Donnerstag, 20. September 2018 15:15
  • $files_origin = Get-ChildItem -Path $origin -File -Recurse -Force -Exclude

    Get-Help Get-ChildItem -Full.

    Es ist sehr empfehlenswert IMMER die KOMPLETTE Hilfe der cmdlets zu lesen, die Du benutzt - inclusive der Beispiele.

    Das beantwortet üblicherweise bereits alle Anfängerfragen.  ;-)


    Best regards,

    (79,108,97,102|%{[char]$_})-join''


    • Bearbeitet BOfH_666 Donnerstag, 20. September 2018 17:49
    Donnerstag, 20. September 2018 17:48
  • Hallo und Danke für deine Rückmeldung.

    -Exclude hatte ich bereits probiert, jedoch scheint dann das Script nicht richtig zu funktionieren.

    Ich hatte:

    "...-Force -Exclude *.doc"

    sowie

    "...-Force -Exclude DOKUMENTNAME.doc"

    probiert.


    In beiden Varianten, warden diese Dokumente dennoch ersetzt.
    • Bearbeitet twinson_999 Donnerstag, 27. September 2018 11:28
    Donnerstag, 27. September 2018 11:27
  • Ja ... da muss man gelegentlich ein bissl rumprobieren. Get-ChildItem  kann manchmal 'ne ganz schöne Diva sein.  ;-) Lass mal bitte den Parameter -File weg. Ich hab den Code oben in meiner Antwort angepasst. Probier ma so.

    Live long and prosper!

    (79,108,97,102|%{[char]$_})-join''

    Donnerstag, 27. September 2018 12:12
  • Hallo,

    ich habe nach 1 1/2 Stunden probieren, keine Lösung gefunden.

    Immer dann, wenn ich (irgendwo) einen "-Exclude" parameter einbaue, gibt es eine Vielzahl von Fehlermeldungen. Exkludiere ich einen vollständigen Dokument Typen, erhalten ich zwar die Fehlermeldungen, die hauptsächlich besagen, dass der Inhalt unter "origin" nicht durch sich selbst ersetzt werden kann, jedoch wird das Regelwerk angewendet.

    Spezifiziere ich nun den Dokument Typen, sodas bestimmte DATEI.EXT ausgeschlossen sind, funktioniert das ganze Script nicht mehr.

    Ich hatte mich auch daran probiert, weitere Zeilen Code zu ergänzen, sodass in einen zweiten "nachverlagerten Schritt" die Listungen bereinigt werden, jedoch ohne Erfolg. 

    Irgendwie verzweile ich gerade.

    Hast du eine Idee wie man die Logik anderweitig abbilden kann, z.B. mit den nachverlagerten Schritt der Bereinigung?

    Freitag, 28. September 2018 13:57
  • Statt Deinen Code zu beschreiben, könntest Du ihn posten. Ich hatte es mit Testdateien auf meinen System getestet und es lief ohne Fehler.

    Live long and prosper!

    (79,108,97,102|%{[char]$_})-join''

    Freitag, 28. September 2018 17:49
  • $origin = 'C:\A'
    $target = 'C:\B'
    $files_origin = Get-ChildItem -Path $origin -Recurse -Force -Exclude (“*.docx”, “to-be-excluded-file.pdf”)
    $filenames_target = Get-ChildItem -Path $target -Recurse -Force -Exclude (“*.docx”, “to-be-excluded-file.pdf”)
    Compare-Object -ReferenceObject $filenames_target -DifferenceObject $files_origin -PassThru -IncludeEqual |
        ForEach-Object {
            $sourceFile = Join-Path -Path $origin -ChildPath $_.Name
            Copy-Item -Path $sourceFile -Destination $_.Directory -Force 
        }

    Gerne, das hier ist der Code, unterm Strich ist ein spezifisches Dokument (to-be-excluded-file.pdf)ausgeschlossen und alle Dateien mit dem Dateityp .docx.

    Das Script als solches funktioniert nun, jedoch erhalte ich eine Vielzahl von Fehlermeldung zu jeden unter A: nicht verfügbaren Ordner:

    Copy-Item : Cannot find path ‘C:\A\Ordner1’ because it does not exist.
    At line: 4 char:9
    +	Copy-Item –Path $sourceFile –Destination $_.Directory –Force
    +	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    	+ CategoryInfo		: ObjectNotFound: (C:\A\Ordner1:String) [Copy-Item], ItemNotFoundException
    	+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.CopyItemCommand

    Sowie eine Fehlermeldung zu jeder unter A: befindlichen Datei (mit Ausnahme der .docx Dokumente und des to-be-excluded-file.pdf):

    Copy-Item : Cannot copy item C:\A\123456.pdf onto itself.
    At line: 4 char:9
    +	Copy-Item –Path $sourceFile –Destination $_.Directory –Force
    +	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    	+ CategoryInfo		: WriteError: (C\A\123456.pdf:String) [Copy-Item], IOException
    	+ FullyQualifiedErrorId	: CopyError,Microsoft.PowerShell.Commands.CopyItemCommand

    Die erste Reihe von Fehlermeldungen (auf die Ordner bezogen) ist zumindest nachvollziehbar,

    die zweite Reihe von Fehlermeldungen (sich selbst ersetzen zu wollen unter A:!) weniger.

    Aufgrund der vielen Meldungen fällt es mir schwer, wichtige Meldungen zu identifizieren.

    Hast du einen weiteren Ratschlag?

    DANKE



    • Bearbeitet twinson_999 Freitag, 5. Oktober 2018 09:34
    Freitag, 5. Oktober 2018 09:32
  • OK - 'hab wenig Zeit - die Fehlermeldung passt nicht zum Code (Zeile 4 ist kein Copy-Befehl) - schade, das macht es schwerer den Fehler zu bestimmen.

    Du solltest lernen wie man Code "debugged". Man führt den Code zeilenweise aus (oder die kleinste zusammengehörige, nicht trennbare Einheit, wie Code-Blöcke) und schaut sich das Ergebnis an. Wenn Du Variablen benutzt kannst Du Dir den Inhalt der Variablen ansehen. Wenn ich mir ein Test-Verzeichnis erstelle mit ein paar Dateien und Unterordnern und den zu Deinem Exclude passenden Test-Dateien und dann folgende Zeilen ausführe

    $origin = 'D:\sample\test1'
    Get-ChildItem -Path $origin -Exclude *.docx, to-be-excluded-file.pdf -Recurse -Force
    
    ... dann erhalte ich als Ergebnis eine Liste mit meinen Test-Dateien und Unterordnern aber ohne die ausgeschlossenen Dateien - genau wie erwartet

    ...  Wenn ich die Unterordner aus der Liste ausschließen will, hänge ich ein -File an den Befehl an oder ich filtere sie mit einem Where-Object aus.

    Wie kommst Du auf die Idee, die Exclude-Liste der in Klammern zu setzen?

    Wenn Du kontrollieren möchtest, was in Deiner Foreach-Schleife passiert, soltlest Du die enthaltenen Variablen einfach ausgeben - notfalls mit einer Pause zwischen jedem Element.

    So - ich hab jetzt 'n langes Wochenende ohne Computer. ;-) 

    Viel Spaß beim Debuggen!


    Live long and prosper!

    (79,108,97,102|%{[char]$_})-join''

    Freitag, 5. Oktober 2018 10:03
  • Lustiger Fred :-)) So wie ich das verstehe, wäre es mit "Compare-Object -ExcludeDifferent -Property Name" fast schon gegessen, aber wenn mehrfach vorhandene Ziele von einer gemeinsamen Quelle überschrieben werden sollen, würde ich es so machen.

    $Ziele = Get-ChildItem $Target -Recurse -File

    # Create Hash table for source files

    $Quellen = @{}

    Foreach ( $File in Get-ChildItem $Source -Recurse -File) {

    $Quellen.Add( $File.Name, $File.FullName )

    }

    # check for each target if a matching source exists

    Foreach ( $Ziel in $Ziele ){

    If $Quellen.ContainsKey( $Ziel.Name ) {

    Copy-Item ( $Quellen.Get_Item( $Ziel.Name ) ) $Ziel.FullPath -Force

    }

    }

    Wie immer - das A und O ist nicht das Coden ansich, sondern das Finden einer Logik, die zum Problem passt :)

    Und das lustige Compare-Object ist damit komplett obsolet.


    Greetings/Grüße, Martin - https://mvp.microsoft.com/en-us/PublicProfile/5000017 Mal ein gutes Buch über GPOs lesen? - http://www.amazon.de/Windows-Server-2012--8-Gruppenrichtlinien/dp/3866456956 Good or bad GPOs? My blog - http://evilgpo.blogspot.com And if IT bothers me? Coke bottle design refreshment - http://sdrv.ms/14t35cq

    Freitag, 5. Oktober 2018 11:53