Erster Skript-Versuch: Massenhaft XML-Files ändern

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
JOberst
Beiträge: 1
Registriert: 31.10.2015 21:34:11

Erster Skript-Versuch: Massenhaft XML-Files ändern

Beitrag von JOberst » 31.10.2015 21:48:39

Hallo!

Dies ist mein erster Beitrag hier. Ich habe mich angemeldet, weil ich gerade anfange, mein erstes Skript zu schreiben. Ich bin absoluter Anfänger und habe alles bisher irgendwo zusammen geklaut. Aber so kommt man natürlich nicht weit, daher bin ich jetzt hier.

Das Problem: Ich habe eine Fotosammlung, die aus *.RAW-Dateien besteht, sowie aus mittels UFRAW entwickelten *.ufraw-Dateien. Letztere sind xml-Dateien und sehen folgendermaßen aus:

Code: Alles auswählen

<?xml version="1.0" encoding="utf-8"?>
<UFRaw Version='7'>
<InputFilename>H:\Fotos\user\201408_Mallorca\P1070267.RAW</InputFilename>
<OutputFilename>H:\Fotos\user\201408_Mallorca\P10702672.jpg</OutputFilename>
  <WB>Manual WB</WB>
  <WBFineTuning>0</WBFineTuning>
  <Temperature>9295</Temperature>
  <Green>1.120</Green>
  <ChannelMultipliers>2.8544 1.0000 1.1334 1.0000</ChannelMultipliers>
  <LensfunAuto>no</LensfunAuto>
<Exposure>0.747826</Exposure>
<Saturation>0.000000</Saturation>
<OutputType>4</OutputType>
<CreateID>1</CreateID>
<ProgressiveJPEG>1</ProgressiveJPEG>
<Compression>78</Compression>
<BaseManualCurve Current='yes'>
	<AnchorXY>0.000000 0.000000</AnchorXY>
	<AnchorXY>0.254902 0.183246</AnchorXY>
	<AnchorXY>1.000000 1.000000</AnchorXY>
</BaseManualCurve>
<ManualCurve Current='yes'>
	<AnchorXY>0.113725 0.000000</AnchorXY>
	<AnchorXY>0.403922 0.544503</AnchorXY>
	<AnchorXY>0.435294 1.000000</AnchorXY>
</ManualCurve>
<MatrixInputProfile Current='yes'>Color matrix
</MatrixInputProfile>
<sRGBOutputProfile Current='yes'>sRGB
</sRGBOutputProfile>
<Make>Panasonic</Make>
<Model>DMC-FZ50</Model>
<Timestamp>Wed Aug 20 13:15:22 2014</Timestamp>
<Orientation>0</Orientation>
<ISOSpeed></ISOSpeed>
<Shutter>1/100 s</Shutter>
<Aperture>F4</Aperture>
<FocalLength>33.8 mm</FocalLength>
<FocalLength35></FocalLength35>
<EXIFSource>exiv2 0.23</EXIFSource>
<Crop>0 0 3672 2748</Crop>
<AspectRatio>1.336245</AspectRatio>
<Rotation>0.000000</Rotation>
<Log>
ufraw_open: w:3672 h:2748 curvesize:0
EXIF data read using exiv2, buflen 256

Scaling with darkness 0, saturation 4095, and
multipliers 1,000000 0,486137 0,711645 0,486137
Loading Panasonic DMC-FZ50 image from H:\Fotos\user\201408_Mallorca\P1070267.RAW ...
Black: 0, Maximum: 4095
AHD interpolation...

</Log>
</UFRaw>
Die Datei beinhaltet also in Zeile 3 die Verknüpfung zum Original, und zwar leider als absoluten Pfad. Nach einer Neuordnung meines Archivs bekomme ich also bei sämtlichen *.ufraw-Dateien eine Fehlermeldung (die Beispieldatei stammt sogar noch von meinem alten Windows-System). Das Ziel des Skriptes soll also sein, sämtliche Unterverzeichnisse nach *.ufraw's zu durchsuchen, und als dritte Zeile

Code: Alles auswählen

<InputFilename>../Originale/P*******.RAW</InputFilename>
einzufügen, wobei die wildcards der fortlaufenden Nummerierung aus der Kamera entsprechen, also beibehalten werden sollen.
Ich habe es zuerst mit sed versucht, komme da aber immer mit der Maskierung durcheinander. Außerdem habe ich die Nummerierung teilweise scheinbar falsch übernommen, so dass ein Zeichen fehlte, und die Schleife rekursiv durch alle Unterordner bekomme ich auch noch nicht zum Laufen ... Mir ist aber bei meiner Suche xmlstarlet begegnet, was viele als ideal für solcherlei Aufgaben bezeichnen. Leider habe ich es damit noch weniger geschafft.

Hier trotzdem mal mein erster Versuch:

Code: Alles auswählen

#!/bin/bash

#Variablendefinition
WORKDIR=Abzuege

cd $WORKDIR

for i in *.ufraw

do
  ZEILE3=$(head -n3 "$i" | tail -1l)
  echo "  alter Pfad:" $ZEILE3
  LAENGE=$(head -n3 "$i" | tail -1l | wc -m)
  NAME=$(echo $ZEILE3 | cut -c $(( ($LAENGE-28)))-$(( ($LAENGE-21) )) )
  echo "  RAW-File:   " $NAME
  sed -i '/^<InputFilename/d' "$i"
  sed -i "3i\ <InputFilename>../Originale/$NAME.RAW</InputFilename>" "$i"  # vor der neuen dritten Zeile wird eine neue Zeile eingefügt
  ZEILE3=$(head -n3 "$i" | tail -1l)
  echo "  neuer Pfad:" $ZEILE3
  echo

done
Falls mir hier jemand behilflich sein könnte wäre es großartig!

Liffi
Beiträge: 2306
Registriert: 02.10.2004 01:33:05

Re: Erster Skript-Versuch: Massenhaft XML-Files ändern

Beitrag von Liffi » 31.10.2015 22:26:28

Zunächst mal danke für den großartigen ersten Post. Super Frage, schonmal Grundlagen vorbereitet, offensichtlich auch schon gut gesucht und den ersten Ansatz vorbereitet.

Ich habe mal ein bisschen damit rumgespielt und einfach mal folgende Zeile geschrieben. test.xml müsstest du natürlich noch durch $i ersetzen :-).

Code: Alles auswählen

xmlstarlet edit --update "//InputFilename" --value $(xmlstarlet sel -t -v "//InputFilename" test.xml | rev | cut -d '\' -f 1|rev) test.xml
Geht sicher auch deutlich effizienter, in 3 Minuten hat auch bestimmt jemand mein rev| cut| rev verbessert :-).

EDIT::
Okay, ich versuche es mal selbst innerhalb der 3 Minuten:

Code: Alles auswählen

xmlstarlet edit --update "//InputFilename" --value $(xmlstarlet sel -t -v "//InputFilename" test.xml | awk -F '\' '{print $NF}') test.xml

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: Erster Skript-Versuch: Massenhaft XML-Files ändern

Beitrag von eggy » 31.10.2015 23:51:47

Hallo JOberst, willkommen im Forum.
Ich werfe mal awk für "solche" Aufgaben in den Raum. Das ist in komplexen Fällen "netter" als sed (finde ich zumindest, hier gibts mindestens ne handvoll Leute, die das anders sehn ;) )

Als Einzeiler in etwa (bitte so nicht benutzen, das ist nicht fehlerfrei)

Code: Alles auswählen

awk -F '[<,>]' 'NR==3{ n=split($3, a, "\\"); file=a[n]; sub("RAW", "jpg", file);  printf "<InputFilename>../Originale/P"; printf file ; print "</InputFilename>"; next} {print}'  datensatz 
oder übersichtlicher geschrieben (awk kann den Code auch aus Dateien entgegennehmen):

Code: Alles auswählen

NR==3{ 
         n=split($3, a, "\\"); 
         file=a[n]; 
         sub("RAW", "jpg", file);  
         printf "<InputFilename>../Originale/P"; 
         printf file; 
         print "</InputFilename>
         next
}

{
        print
}
awk liest Zeilenweise, NR==3 führt den Code in den folgenden {} nur für Zeile 3 aus, der Code in {} wird für alle Zeilen ausgeführt. Dann hat awk noch die schöne Eigenschaft die Eingabezeilen in Felder zu zerlegen, die Feldtrenner kann man selbst definieren, hier < und >. Die Felder werden über $1, $2 etc angesprochen, $0 ist die ganze Zeile. In den ersten Klammern steht dann die Stringzerlegung und was davon ausgegeben werden soll. next liest die nächste Zeile, und sorgt dafür, dass die 3. Zeile nicht doppelt ausgegeben wird.


Ps: vielleicht hast Du Spaß an dem Thread da viewtopic.php?f=34&t=156724 (der könnt mal ne neue Aufgabenstellung vertragen)

Benutzeravatar
catdog2
Beiträge: 5352
Registriert: 24.06.2006 16:50:03
Lizenz eigener Beiträge: MIT Lizenz

Re: Erster Skript-Versuch: Massenhaft XML-Files ändern

Beitrag von catdog2 » 31.10.2015 23:59:57

Hier mal was in python (überschreibt die *.ufraw Dateien mit der Änderung):

Code: Alles auswählen

#!/usr/bin/env python3

from pathlib import Path
from xml.dom.minidom import parse, parseString
import re

pwd = Path('.')
for p in pwd.iterdir():
    if p.is_file() and p.match("*.ufraw"):
        with p.open() as f:
            dom = parse(f)
        
        ifn = dom.getElementsByTagName("InputFilename")[0]
        rawfn = re.search(r"P[0-9]+\.RAW", ifn.firstChild.data, flags=re.I).group()
        ifn.firstChild.data = "../Originale/" + rawfn

        with p.open(mode="w") as f:
            f.write(dom.toxml())
Unix is user-friendly; it's just picky about who its friends are.

Antworten