Generative KI liefert Kontext? Ja klar, aber egal welches Modell, welcher Anbieter: Möchte man ein Word-Dokument oder eine Powerpoint-Präsentation, lassen sich diese häufig nicht öffnen oder enthalten nur Teile der Ausgabe. Das das einzige Dateiformat, das LLMs wirklich zuverlässig liefern, ist Markdown (*.md) – eine sehr simple, ursprünglich 2004 von John Gruber entwickelte Auszeichnungssprache, die mit einfachen Sonderzeichen Textformatierungen erlaubt. Doch die Alltagstauglichkeit von Markdown ist eher bescheiden, weil bislang kein Office-Paket, egal von welchem Hersteller, Markdown wirklich unterstützt (Hinweis: Für Februar 2026 ist LibreOffice v26 angekündigt, das als ein neues Main Feature die Markdown-Unterstützung mitbringen wird).
Eine sehr schöne Lösung stellt pandoc dar: Kommandozeilenbasiert wandelt es .md in .docx oder .pptx um:
Jedes beliebige Terminal kann benutzt werden, also Windows Terminal, der gute alte DOS-Prompt oder auch Powershell, im Terminal in VS Code gibt’s sogar Vorschläge von Github Copilot!
Ja, erfordert aber zusätzlich eine LaTEX-Installation, und da die meisten Dokumente ohnehin noch bearbeitet werden, wird hier der Weg über das eigene Office-Paket der elegantere sein.
Digitale Kameras (ja, auch die in Handys!) schreiben beim Speichern einer Aufnahme eine ganze Reihe fotografisch relevanter Daten (Belichtung, ISO-Zahl, Brennweite, Blende, Kameramodell) als sog. EXIF-Daten in die Bilddatei – übrigens nahezu unabhängig vom Zielformat, sowohl RAW als auch JPG enthalten diese Daten. Und auch unabhängig vom Kamerahersteller, denn EXIF ist ein weltweiter Standard.
Allerdings, da das Speicherformat von Bildern immer ein binäres ist, werden die EXIF-Daten ebenfalls dort binär abgelegt, was das Auslesen etwas aufwändiger macht. Jedem EXIF-Tag (Belichtung, ISO-Zahl, Brennweite,..) ist eine hexadezimale Tag-ID zugeordnet, und jeder EXIF-Tag hat einen spezifischen Datentyp, ggf. sogar eine Auswahlliste fest vorgegebener Werte. Wer sich reinnerden will: https://exiftool.org/TagNames/EXIF.html.
Aber egal, ob für eine Bilddatenbank oder RAG für eine lokale KI, man kann den Job mit Powershell erledigen, wir bauen uns ein CmdLet Get-ExifData, das für ein gegebenes Bild die EXIF-Daten ausliest.
Zwei kleine Helper-Funktionen
Ich kann mich an eine Auftragsarbeit in C# vor Jahren erinnern, wo das Auslesen der EXIF-Daten in eine ziemliche Konvertierungsorgie eskalierte. Powershell ist da freundlicher, weil es viele Konvertierungen implizit vornimmt. Trotzdem erstellen wir uns eine kleine Konvertierungsfunktion für die wichtigsten Datentypen, auf die wir treffen werden – Byte, ASCII, short, long, rational (Fließkomma):
Und weil beim rational-Datenformat jemand ganz kreativ war (es stellt eigentlich einen ganzzahligen Bruch dar: Die ersten 4 Bytes sind der Zähler, die zweiten 4 Bytes der Nenner), hier noch eine zusätzliche Konvertierung, damit daraus ein in Powershell verwendbarer double wird:
Diese Liste kann natürlich beliebig erweitert werden, hier wurden nur die wichtigsten der gut 80 EXIF-Tags ausgewählt.
Die eigentliche Skript-Logik
Und dann kann es auch schon losgehen: $Image enthält den vollständigen Pfad zu einer Bilddatei, aus der wir ein System.Drawing.Image machen. Diese .NET-Klasse stellt uns die EXIF-Daten als PropertyItems zur Verfügung, über die wir iterieren und nach „unseren“ TagIDs suchen. Bei einem Treffer kommt unsere Get-ExifValue Funktion zum Einsatz, und wir schreiben den nun für Powershell lesbaren Wert in unsere Ergebnisdaten $exifData.
if (-not (Test-Path$Image)) {Write-Error"File not found: $Image"return }try {$imageObj = [System.Drawing.Image]::FromFile($Image)# Hashtabelle für EXIF-Daten$exifData = [ordered]@{}# Die EXIF-Daten sind PropertyItems des Image-Objektsforeach ($propin$imageObj.PropertyItems) {# Konvertiere die numerische Property-ID in einen 4-stelligen Hexadezimal-String (z.B. 0x829A)$id = '{0:X4}'-f$prop.Id# Verwende einen sprechenden Namen aus $exifTags, falls verfügbar, sonst einen generischen Namen$name = $exifTags[$prop.Id] ?$exifTags[$prop.Id] : "Unknown_0x$id"# Dekodiere den Property-Wert mit der Hilfsfunktion, abhängig vom Typ$value = Get-ExifValue$prop# Speichere den Property-Namen und den dekodierten Wert in der Hashtabelle$exifData[$name] = $value }$imageObj.Dispose()# Gib alle EXIF-Daten als PowerShell-Objekt aus [PSCustomObject]$exifData }catch {Write-Error"Could not read EXIF data from $Image. $_" }
Weil Speicher begrenzt ist, wird am Ende noch das Image entsorgt (Dispose()), und weil Dateizugriffe kleine Zicken sind, liegt das Ganze in einem robusten Try...Catch Block.
Das fertige CmdLet im Ganzen
Und hier nochmal das ganze Skript verpackt in ein CmdLet Get-ExifData:
functionGet-ExifData { [CmdletBinding()]param ( [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias("FullName")] [string]$Image )begin {$exifTags = @{0x0100 = "ImageWidth"0x0101 = "ImageHeight"0x010F = "Make"0x0110 = "Model"0x0112 = "Orientation"0x0132 = "DateTime"0x829A = "ExposureTime"0x829D = "FNumber"0x8833 = "ISOSpeed"0x9003 = "DateTimeOriginal"0x9201 = "ShutterSpeedValue"0x9202 = "ApertureValue"0x9204 = "ExposureBiasValue"0x9209 = "Flash"0x920A = "FocalLength"0xA002 = "PixelXDimension"0xA003 = "PixelYDimension"0xA405 = "FocalLengthIn35mmFilm"0xA432 = "LensSpecification"0xA434 = "LensModel"0xA420 = "ImageUniqueID" }functionConvert-Rational {param ($bytes)$num = [BitConverter]::ToUInt32($bytes, 0)$den = [BitConverter]::ToUInt32($bytes, 4)if ($den -eq 0) { return$num }return [math]::Round($num / $den, 4) }functionGet-ExifValue {param ($prop)switch ($prop.Type) {1 { return$prop.Value[0] } # BYTE2 { return ([System.Text.Encoding]::ASCII.GetString($prop.Value)).Trim([char]0) } # ASCII3 { return [BitConverter]::ToUInt16($prop.Value, 0) } # SHORT4 { return [BitConverter]::ToUInt32($prop.Value, 0) } # LONG5 { returnConvert-Rational$prop.Value } # RATIONAL7 { return$prop.Value } # UNDEFINED9 { return [BitConverter]::ToInt32($prop.Value, 0) } # SLONG10 { returnConvert-Rational$prop.Value } # SRATIONALdefault { return$prop.Value } } } }process {if (-not (Test-Path$Image)) {Write-Error"File not found: $Image"return }try {$imageObj = [System.Drawing.Image]::FromFile($Image)# Hashtabelle für EXIF-Daten$exifData = [ordered]@{}# Die EXIF-Daten sind PropertyItems des Image-Objektsforeach ($propin$imageObj.PropertyItems) {# Konvertiere die numerische Property-ID in einen 4-stelligen Hexadezimal-String (z.B. 0x829A)$id = '{0:X4}'-f$prop.Id# Verwende einen sprechenden Namen aus $exifTags, falls verfügbar, sonst einen generischen Namen$name = $exifTags[$prop.Id] ?$exifTags[$prop.Id] : "Unknown_0x$id"# Dekodiere den Property-Wert mit der Hilfsfunktion, abhängig vom Typ$value = Get-ExifValue$prop# Speichere den Property-Namen und den dekodierten Wert in der Hashtabelle$exifData[$name] = $value }$imageObj.Dispose()# Gib alle EXIF-Daten als PowerShell-Objekt aus [PSCustomObject]$exifData }catch {Write-Error"Could not read EXIF data from $Image. $_" } }}Export-ModuleMember -Function Get-ExifData# Anwendung:# Import-Module ".\Get-ExifData.psm1"# Get-ExifData -Image "C:\Path\To\Your\image.jpg"
Neulich hatte ich das Vergnügen, mich mit dem guten alten Wakeup-On-LAN (WOL) auseinandersetzen zu dürfen. Die Doku schreibt etwas von einem „Magic Paket“, das gesendet wird, um den Rechner aufzuwecken.
Und weil Magie und Powershell sich irgendwie ganz gut vertragen, kurz mal tiefer eingelesen und gelernt, dass es sich dabei um ein simples UDP-Paket handelt, das an die MAC-Adresse des zu weckenden Rechners gesendet wird.
Voraussetzung ist, dass der zu weckende Rechner entsprechend für Wakeup-On-LAN konfiguriert ist (siehe BIOS-Einstellungen). Außerdem funktioniert die Wakeup-On-LAN nicht, wenn der Rechner im Hibernate-Zustand ist. Komplett heruntergefahren oder Standby sind Zustände, aus denen er aufwachen kann.
Cookie-Zustimmung verwalten
Wir Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wenn du diesen Technologien zustimmst, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn du deine Zustimmung nicht erteilst oder zurückziehst, können bestimmte Merkmale und Funktionen beeinträchtigt werden.
Funktional
Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt.Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.