Hey, Scripting Guy!Wo Sind die Becherhalter?

Die Scripting Guys von Microsoft

Dieser Artikel basiert auf einer Vorabversion von Windows PowerShell. Die in diesem Artikel enthaltenen Informationen können jederzeit geändert werden.

Wenn eine neue Technologie eingeführt wird, dauert es in der Regel eine Weile, bis die Leute die neue Technologie von dem unterscheiden können, was vorher da war. Denken Sie beispielsweise an die ersten Automobile. Es gibt einen Grund, warum diese Maschinen als pferdelose Kutschen bezeichnet wurden: Dieser Begriff beschreibt diese frühen Fahrzeuge recht gut. Wenn Sie sich die ersten Automobile genauer ansehen, werden Sie bemerken, dass der einzige Unterschied zwischen den ersten Automobilen und den Pferdekutschen nur die Tatsache war, dass die Kutschen von Pferden gezogen wurden und die Automobile nicht. Davon abgesehen ließen sie sich nur schwer auseinander halten.

Heutzutage würden natürlich nur sehr wenige Leute ein Automobil mit einer Pferdekutsche verwechseln. Das liegt daran, dass im Lauf der Zeit die Automobilhersteller Dinge getan haben, die ihre Autos deutlich von Kutschen unterscheiden. Beispielsweise werden die Autos von heute bereits in der Fabrik mit CD-Playern, Klimaanlagen und Satellitennavigationssystemen ausgestattet, was nur von wenigen Pferdekutschen gesagt werden kann. Nehmen Sie einmal an, Sie fahren im Jahr 1880 in einer Pferdekutsche und möchten Ihren Pappbecher mit Frappuccino kurz absetzen: Nun, da haben Sie leider Pech. Viele Automodelle von heute dagegen sind standardmäßig mit bis zu 17 Becherhaltern ausgestattet, und es gibt mindestens ein Automodell, das alleine für den Fahrer 3 Becherhalter besitzt. Einige der neueren Modelle haben sogar Becherhalter, die Ihr Getränk je nach Bedarf erhitzen oder abkühlen können.

Hinweis Ja, es ist wirklich kaum zu glauben, dass die Leute einmal ohne die Möglichkeit auskommen mussten, beim Fahren drei verschiedene Getränke zu sich zu nehmen. Wie soll man denn fahren können, wenn einem nur ein oder zwei Getränke zur Verfügung stehen?

Bevor sich eine neue Technologie wirklich erfolgreich durchsetzen kann, müssen die Leute wissen, warum sie das Neue und Unbekannte dem Alten und Bewährten vorziehen sollen. Als DVDs auf den Markt kamen, machten sie es möglich, sich genau wie mit Videorekordern Kinofilme zu Hause anzusehen. Allerdings waren DVDs wesentlich teurer als Videokassetten, und man konnte mit einem DVD-Player Filme nicht aufnehmen. DVDs haben also die Welt nicht gerade im Sturm erobert. Erst als die DVD-Hersteller auf ihren DVDs auch gelöschte Szenen, Kommentare von Schauspielern, die man im Film überhaupt nicht bemerkt hatte, und andere Dinge unterbrachten, die eine Videokassette nicht bieten kann, begannen DVDs, ihre Vorläufer zu verdrängen. Sich zu Hause einen Film ansehen zu können, war noch nicht genug. Die Hersteller von DVDs mussten mehr Möglichkeiten bereitstellen, als den Leuten bereits durch Videokassetten geboten wurden.

Hinweis Ob dieses Bonusmaterial überhaupt jemals von den Leuten genutzt wird, ist dabei nebensächlich: Es liegt nun einmal in der Natur des Menschen, etwas, das verfügbar ist, unbedingt haben zu wollen. Übrigens spielen die Scripting Guys mit dem Gedanken, eine DVD herauszubringen, die hauptsächlich gelöschten Text aus Artikeln des TechNet Magazins und alternative Versionen von Skripts für die Systemverwaltung enthält.

Verwenden von Windows PowerShell

Windows PowerShell™, die von Microsoft veröffentlichte neue Befehlsshell- und Skriptingtechnologie, ist ein weiteres Beispiel für dieses Phänomen. Wenn die Leute zum ersten Mal von Windows PowerShell hören, erfahren sie in der Regel folgende Dinge:

  • "Mit Windows PowerShell kann man Prozesse und Dienste verwalten."
  • "Mit Windows PowerShell kann man aus Ereignisprotokollen Ereignisse abrufen."
  • "Mit Windows PowerShell kann man WMI zur Verwaltung von Computern nutzen."

Nun, daran ist ja nichts verkehrt. Das sind alles nützliche und wichtige Aufgaben. Das Problem ist nur, dass man alle diese Aufgaben bereits mit VBScript und Windows Script Host erledigen kann. Abwärtskompatibilität ist wichtig, und Windows PowerShell würde keine besonders gute Innovation darstellen, wenn es nicht dieselben Dinge wie VBScript beherrschen würde. (Stellen Sie sich einen DVD-Player vor, mit dem man sich keine Filme ansehen kann. Aber eigentlich brauchen Sie sich das gar nicht vorzustellen: Einer der Scripting Guys hat einen dieser DVD-Player zu Hause stehen.)

Andererseits denkt man natürlich, warum sollte man sich mit dieser neuen Technologie überhaupt befassen, wenn Windows PowerShell lediglich dieselben Dinge tun kann, die sich bereits mit VBScript bewerkstelligen lassen? Zugegeben: VBScript bietet keine Becherhalter, aber das tut Windows PowerShell auch nicht. Welchen Unterschied macht es also, welche Skriptingtechnologie wir verwenden?

Um ehrlich zu sein, macht es unter Umständen nicht den geringsten Unterschied. Andererseits – vom Fehlen der Becherhalter einmal abgesehen – bietet Windows PowerShell einige Möglichkeiten, mit denen VBScript nicht aufwarten kann. Wenn Sie lediglich Prozesse und Dienste verwalten müssen, besteht für Sie kein zwingender Grund, sich Windows PowerShell genauer anzusehen, denn wie heißt es so schön: Wenn etwas nicht kaputt ist, dann repariere es auch nicht. (Was die Scripting Guys anbelangt, sollte man diese Redewendung ein wenig abwandeln und stattdessen sagen: „Jungs, versucht ja nicht, das Ding zu reparieren, sonst ist es hinterher kaputt.“) Wirklich interessant wird Windows PowerShell dann, wenn Sie damit etwas machen können, zu dem VBScript nicht in der Lage ist. Nehmen wir zum Beispiel die Möglichkeit, den Zeitpunkt des letzten Schreibzugriffs zu ändern.

Ändern des Zeitpunkts des letzten Schreibzugriffs

Sie haben richtig gehört: Die Änderung des Zeitpunkts des letzten Schreibzugriffs für eine Datei. Aus verschiedenen Gründen (z. B. als schnelle, einfache Methode der Versionskontrolle) haben Skriptentwickler immer von dem Tag geträumt, an dem sie einmal in der Lage sein werden, den Zeitpunkt des letzten Schreibzugriffs (also die Erstellungszeit) einer Datei oder einer Gruppe von Dateien zu ändern. Leider muss gesagt werden, dass diese Möglichkeit weder von WMI (der Windows-Verwaltungsinstrumentation) noch von FileSystemObject geboten wird. Bei beiden Technologien sind die Datums- und Uhrzeitangaben von Dateien schreibgeschützt.

Bei Windows PowerShell dagegen ist das Ersetzen des Zeitpunkts des letzten Schreibzugriffs auf eine Datei durch das aktuelle Datum und die aktuelle Uhrzeit so leicht wie das Ausführen des folgenden Befehls:

$a = Get-Item c:\scripts\test.txt; $a.LastWriteTime = Date

Bevor wir erklären, wie dieser Befehl funktioniert, wollen wir eine Frage beantworten, die wahrscheinlich jedem auf der Zunge brennt: Wie kann Windows PowerShell den Zeitpunkt des letzten Schreibzugriffs auf eine Datei ändern, wenn WMI das nicht kann? Wir haben uns gedacht, dass wir zuerst diese Frage behandeln sollten, bevor wir etwas anderes tun, denn hier liegt ein häufiges Missverständnis in Bezug auf Windows PowerShell begraben. Die Leute neigen dazu anzunehmen, dass Windows PowerShell nichts weiter als eine neue Methode darstellt, WMI zu verwenden. Natürlich können Sie Windows PowerShell auch dazu verwenden, auf WMI-Daten zuzugreifen. Wahr ist auch, dass, während wir auf die Bereitstellung weiterer Cmdlets (systemeigener Windows PowerShell-Befehle) warten, die meisten Skripts sich für die Systemverwaltung weiterhin auf WMI verlassen. Der springende Punkt ist jedoch, dass Windows PowerShell auf .NET Framework basiert und dass die Cmdlets von Windows PowerShell mit .NET-Objekten statt mit WMI-Objekten arbeiten. Dieser Punkt ist wichtig, denn die Eigenschaften, die über .NET Framework verfügbar sind, sind nicht unbedingt dieselben Eigenschaften, die über WMI verfügbar sind.

Wenn wir beispielsweise das Cmdlet „Get-Item“ verwenden, um eine Datei zu binden, erhalten wir als Ergebnis keine Instanz der WMI-Klasse „CIM_Datafile“, sondern ein .NET-Objekt. Macht das wirklich einen so großen Unterschied? Aber natürlich, denn das .NET-Objekt bietet eine etwas andere Gruppe von Eigenschaften und Methoden für die Dateiverwaltung als WMI. Wie wir gerade gesehen haben, können diese geringfügigen Unterschiede sehr wichtig sein, wie zum Beispiel der Unterschied zwischen einer Schreibschutz-Eigenschaft und einer Schreiben/Lesen-Eigenschaft.

Eine kleine Zwischenfrage: Woher wissen wir eigentlich, dass das Cmdlet „Get-Item“ ein .NET-Objekt zurückgibt? Wie in den meisten Fällen lässt sich auch in diesem Fall am leichtesten herausfinden, mit welcher Art von Objekt wir es zu tun haben (und welche Eigenschaften und Methoden uns dadurch zur Verfügung stehen), indem wir es an die Datei binden und dieses Objekt anschließend dem Cmdlet „Get-Member“ übergeben:

Get-Item c:\scripts\test.txt | Get-Member

Die Ergebnisse dieses Befehls sind in Abbildung 1 zu sehen. Wie Sie sehen können, handelt es sich tatsächlich um ein .NET-Objekt. Dachten Sie denn, die Scripting Guys hätten in diesem Punkt etwas falsch gemacht?

Figure 1 Mitglieder des von Get-Item zurückgegebenen Objekts

   
TypeName: System.IO.FileInfo

Name                      MemberType     Definition
----                      ----------     ----------
AppendText                Method         System.IO.StreamWriter AppendText()
CopyTo                    Method         System.IO.FileInfo CopyTo(String destFileName),
                                         System.IO.FileInfo CopyTo(S...
Create                    Method         System.IO.FileStream Create()
CreateObjRef              Method         System.Runtime.Remoting.ObjRef CreateObjRef(
                                         Type requestedType)
CreateText                Method         System.IO.StreamWriter CreateText()

Übrigens veranschaulicht dieser kleine Befehl zwei der interessantesten (und nützlichsten) Fähigkeiten von Windows PowerShell: Pipelining und Metadaten. Wir werden heute zwar nicht auf technische Einzelheiten eingehen, aber das Grundkonzept von Pipelining besteht darin, dass Sie mit einem Cmdlet auf ein Objekt zugreifen und dieses Objekt anschließend einem anderen Cmdlet übergeben. In diesem Befehl haben wir das Cmdlet „Get-Item“ dazu verwendet, ein Objekt abzurufen, das die Datei C:\Scripts\Test.txt repräsentiert, und anschließend dieses Objekt (als Objekt) über die Pipeline dem Cmdlet „Get-Member“ übergeben. (Die Pipeline wird in einem Windows PowerShell-Befehl durch das Symbol | repräsentiert.)

Danach macht „Get-Member“ seine Arbeit, die daraus besteht, die Eigenschaften und Methoden für das abgerufene Objekt abzurufen. Dies ist eines der anderen tollen Features von Windows PowerShell: Es ist sehr leicht, auf Metadaten zuzugreifen, also auf Informationen über Informationen. Mit Cmdlets wie „Get-Member“ und „Get-Command“ können Sie Windows PowerShell so intensiv ins Kreuzverhör nehmen, wie Sie wollen, ungefähr so, wie Sie ein WMI-Skript schreiben können, um Informationen über die Ihnen zur Verfügung stehenden WMI-Klassen, Eigenschaften und Methoden abzurufen.

Beachten Sie dabei jedoch, dass „Get-Member“ noch mehr leistet, als mit WMI möglich ist. Sie können mit WMI zwar Informationen über WMI-Objekte abrufen, aber Sie können WMI nicht dazu verwenden, auf Informationen über andere COM-Objekte (z. B. FileSystemObject) zuzugreifen. Dies ist bei Windows PowerShell nicht der Fall. „Get-Member“ funktioniert mit jedem Objekt, das Sie binden oder erstellen können. Können Sie sich nicht mehr an die Methoden erinnern, die Ihnen zur Verfügung stehen, wenn Sie FileSystemObject verwenden? Dann führen Sie den folgenden Befehl aus:

New-Object -comobject "Scripting.     FileSystemObject" | Get-Member

Dies ist eine unglaublich nützliche und daher bei weitem noch nicht voll ausgenutzte Fähigkeit.

Nett, nicht wahr? Wenn wir durch die Liste der Eigenschaften und Methoden scrollen, die uns bei der Arbeit mit der Datei C:\Scripts\Test.txt zur Verfügung stehen, stoßen wir auf folgende Zeile:

LastWriteTime             Property       System.DateTime LastWriteTime {get;set;}

Interessant. Offenbar ist „LastWriteTime“ eine Eigenschaft, die sowohl „Get“ (Lesen) als auch „Set“ (Schreiben) unterstützt. Bedeutet dies, dass wir Windows PowerShell dazu verwenden können, den Wert für den Zeitpunkt des letzten Schreibzugriffs auf eine Datei zu ändern? Hmmm ...

Glossar zu Windows PowerShell

Alias Ein individuell anpassbarer Name, der ein vorhandenes Cmdlet repräsentiert. Windows PowerShell stellt mehrere Standardaliase bereit, mit denen allgemeine Shellbefehle ihren Cmdlet-Entsprechungen zugeordnet werden.

Cmdlet Die kleinste funktionale Einheit in Windows PowerShell, die den integrierten Befehlen anderer Shells entspricht.

Befehl Eine Ausführungseinheit in Windows PowerShell. Befehle können mehrere Pipeliningvorgänge beinhalten und sich mithilfe des Semikolons (;) über mehrere Zeilen erstrecken.

Frappuccino Ein Kaffee-Mixgetränk mit Eis, das von Starbucks und anderen Cafés verkauft wird.

Monad/MSH/Microsoft-Befehlsshell Ehemalige Codenamen für Windows PowerShell.

Objekt Vom System oder von einem Befehl abgerufene strukturierte Daten. Windows PowerShell verarbeitet Daten als .NET-Objekte und ermöglicht Ihnen, auf Objekteigenschaften nach Namen zuzugreifen, statt textorientierte Befehlausgaben analysieren zu müssen.

Parameter Optionen, die für ein Cmdlet in der Befehlszeile eingestellt werden können. Windows PowerShell verwendet einen einleitenden Bindestrich (-), um einen Parameter als solchen zu kennzeichnen.

Pipeline Pipelines ermöglichen, die Ausgabe eines Befehls als Eingabe an einen anderen Befehl weiterzugeben. Eine Pipeline wird auch kurz als „Pipe“ bezeichnet und in Befehlen durch das Zeichen | dargestellt.

Profil Einstellungen, die beim Starten von Windows PowerShell geladen werden und Ihnen ermöglichen, die Arbeitsweise der Shell zu konfigurieren.

Skript Einer oder mehrere Befehle, die als Datei gespeichert und über eine Datei ausgeführt werden. Windows PowerShell verwendet für Skriptdateien die Namenserweiterung „.ps1“.

Shell Eine Befehlszeilenumgebung, in der sowohl Befehle als auch Cmdlets ausgeführt werden können.

Variable Ein Objekt oder Parameter, dessen Wert bei der Ausführung eines Befehls festgelegt, geändert oder abgerufen werden kann.

Kehren wir jetzt zum Ausgangspunkt zurück, und reden wir über den Befehl, der das aktuelle Datum und die aktuelle Uhrzeit als den Zeitpunkt des letzten Schreibzugriffs auf die Datei Test.txt einstellt. Er sieht folgendermaßen aus:

$a = Get-Item c:\scripts\test.txt; $a.LastWriteTime = Date

Wie Sie sehen können, ist dieser Befehl wirklich nichts Besonderes. Zunächst deklarieren wir eine Variable namens „$a“. Danach verwenden wir das Cmdlet „Get-Item“, um eine Verbindung zur Datei C:\Scripts\Test.txt herzustellen. Anschließend speichern wir dieses Objekt in der Variable $a. Nach der Eingabe eines Semikolons, das uns ermöglicht, in einer einzigen Zeile mehrere Befehle auszuführen, stellen wir den Wert der Eigenschaft „LastWriteTime“ auf das aktuelle Datum und die aktuelle Uhrzeit ein. Das macht das Fehlen von Becherhaltern fast wieder wett, nicht wahr?

Zu den Feinheiten

Selbstverständlich sind Sie nicht darauf beschränkt, den Zeitpunkt des letzten Schreibzugriffs auf das aktuelle Datum und die aktuelle Uhrzeit einzustellen. Betrachten Sie beispielsweise diesen Befehl:

$b = Get-Date "5/22/2006 8:15 PM";
$a = Get-Item c:\scripts\test.txt; $a.LastWriteTime = $b

Diesmal haben wir drei Befehle miteinander verkettet. Zunächst erstellen wir mit dem Cmdlet „Get-Date“ ein Datum/Uhrzeit-Objekt für den Zeitpunkt 22.05.2006, 20:15 Uhr. Wieso mussten wir in unserem ersten Skript, in dem der Zeitpunkt des letzten Schreibzugriffs auf das aktuelle Datum und die aktuelle Uhrzeit eingestellt wurde, nicht das Cmdlet „Get-Date“ verwenden, um ein Datum/Uhrzeit-Objekt zu erstellen? Die Antwort ist ganz einfach: Der Befehl „Date“ gibt automatisch ein Datum/Uhrzeit-Objekt zurück. (Um sich selbst davon zu überzeugen, geben Sie in der Windows PowerShell-Konsole den folgenden Befehl ein: Date | Get-Member.)

Nachdem wir ein Datum/Uhrzeit-Objekt für das gewünschte Datum und die gewünschte Uhrzeit erstellt haben, verwenden wir nun den uns mittlerweile vertrauten Befehl „Get-Item“, um ein Objekt abzurufen, das die Datei „Test.txt“ repräsentiert (ein Objekt, das wir in der Variable $a speichern). Zum Schluss stellen wir die Eigenschaft „LastWriteTime“ auf den Wert ein, der durch die Variable $b repräsentiert wird:

$a.LastWriteTime = $b

Ob Sie es glauben oder nicht: Sie haben bisher noch nicht einmal die Hälfte gesehen. Werfen Sie einen Blick auf diesen harmlos aussehenden Befehl:

$b = Get-Date "5/22/2006 8:15 PM";
Get-ChildItem c:\scripts | ForEach-Object {$_.LastWriteTime = $b}

Was an diesem Befehl so interessant ist? Nun, schauen wir uns einmal an, was er macht. Der erste Teil ist leicht: Wir erstellen wieder ein Datum/Uhrzeit-Objekt, das dem Zeitpunkt 22.05.2006, 20:15 Uhr entspricht. Beachten Sie jedoch, dass wir im zweiten Teil nicht das Cmdlet „Get-Item“, sondern folgenden Befehl verwenden:

Get-ChildItem c:\scripts

Was das soll? Nun, bei jeder Anwendung des Cmdlets „Get-ChildItem“ auf einen Ordner wird eine Sammlung aller in diesem Ordner enthaltenen Dateien zurückgegeben. Wir werden diese Sammlung nehmen und anschließend alle darin enthaltenen Elemente über die Pipeline (da ist dieses Symbol | wieder) dem Cmdlet „ForEach-Object“ übergeben.

Und was macht das Cmdlet „ForEach-Object“ mit all diesen Dateien? Na, es wird natürlich für jede einzelne Datei den Wert der Eigenschaft „LastWriteTime“ ändern:

ForEach-Object {$_.LastWriteTime = $b}

Hinweis Obwohl der Artikel dieses Monats kein Einsteigerlehrgang für Windows PowerShell sein soll, sollten wir darauf hinweisen, dass es sich bei „$_“ um eine Spezialvariable handelt, die das aktuelle Element in einer „For Each“-Schleife repräsentiert. Dies entspricht „objItem“ in der folgenden VBScript-Schleife:

For Each objItem in colItems
    Wscript.Echo objItem.Name
Next

Wenn die Ausführung unseres Windows PowerShell-Befehls abgeschlossen ist, haben alle Dateien im Ordner C:\Scripts denselben Zeitpunkt des letzten Schreibzugriffs. Das soll uns eine dieser neuartigen pferdelosen Kutschen erst einmal nachmachen!

Wir könnten die ganze Nacht lang eine Variante dieses Befehls nach der anderen ausführen. Möchten Sie beispielsweise den Zeitpunkt des letzten Schreibzugriffs nur bei den TXT-Dateien des Ordners C:\Scripts ändern? Ganz wie Sie wünschen:

$b = Get-Date "5/22/2006 8:15 PM"; Get-ChildItem c:\scripts\*.txt | ForEach-Object {$_.LastWriteTime = $b}

Kleiner Appetitanreger

Das ist wirklich toll. Wir sollten jedoch anmerken, dass dieser Artikel nicht dem Zweck dient, Ihnen Windows PowerShell zu „verkaufen“, und wir möchten Sie auch nicht dazu ermutigen, Ihre gesamten VBScript-Skripts wegzuwerfen und sie alle durch Windows PowerShell zu ersetzen. Wenn Ihre Skripts das tun, wofür sie geschrieben wurden, dann sollten Sie sie in Ruhe lassen. Das gleiche gilt für Batchdateien, Befehlszeilenprogramme, Zaubersprüche und alle sonstigen Hilfsmittel, die Sie einsetzen, um Ihre Computer zu verwalten. Denken Sie daran: Wenn etwas nicht kaputt ist ...

Der springende Punkt ist nicht, dass Sie mit Windows PowerShell Dinge tun können, die Sie sowieso schon tun konnten, sondern dass Windows PowerShell – zum Teil deswegen, weil es einen Zugriff auf .NET Framework bereitstellt – Ihnen die Möglichkeit bietet, Dinge zu tun, die Sie vorher nicht tun konnten. Ein Beispiel dafür ist das Ändern des Zeitpunkts des letzten Zugriffs für eine Datei oder eine Gruppe von Dateien. Ob es noch weitere Beispiele dafür gibt, hängt davon ab, was Sie vorhaben. Es sind nämlich genau diese Möglichkeiten, die es die Mühe wert machen, sich Windows PowerShell genauer anzusehen.

Für viele von Ihnen war dies wahrscheinlich eine recht ungewöhnliche Einführung in Windows PowerShell: Schließlich haben wir ja nicht mit dem üblichen einfachen Beispiel der Bauart „Hallo Welt“ angefangen, sondern sind sofort mit einem aus mehreren Teilen bestehenden Windows PowerShell-Befehl eingestiegen und haben mit Begriffen wie „Pipeline“ und „Cmdlet“ um uns geworfen, als ob jeder wissen müsste, was wir damit meinen. Das haben wir absichtlich getan. Schließlich wissen wir ja, dass viele von Ihnen Windows PowerShell erst noch ausprobieren müssen. Wir befürchteten, wenn wir Ihnen zeigen, wie man eine Liste der Dienste auf einem Computer abrufen kann, würden Sie nur mit den Achseln zucken und sich wieder dem zuwenden, was Sie gerade getan haben.

Wir hoffen ja, dass Sie nicht gerade dabei waren, Auto zu fahren. Für viele von uns ist es schwer genug, beim Fahren drei Getränke zu trinken, sich eine DVD anzusehen und sich am Handy zu unterhalten, ohne gleichzeitig auch noch das TechNet Magazin zu lesen.

Na, regen Sie sich doch nicht gleich auf: Ich sagte doch „für viele von uns“. Nicht für alle von uns.

Die Scripting Guys von Microsoft arbeiten für (und sind Angestellte von) Microsoft. Wenn sie nicht gerade ihrem Hobby, dem Baseball (oder verschiedenen anderen Aktivitäten) nachgehen, betreiben sie das TechNet Script Center. Besuchen Sie es unter www.scriptingguys.com.

© 2008 Microsoft Corporation und CMP Media, LLC. Alle Rechte vorbehalten. Die nicht genehmigte teilweise oder vollständige Vervielfältigung ist nicht zulässig.