Papiertiger
Anmeldungsdatum: 7. April 2010
Beiträge: 17
Wohnort: Neuburg an der Donau
|
Hallo zusammen, meine Ausgangslage ist folgende:
Ich habe ein Script, welches eine Datei.ext1 einliest, umwandelt und in eine Datei.ext1.ext2 im selben Verzeichnis speichert. Jetzt möchte ich, dass ein zweites Script diesen Vorgang automatisiert.
Hierbei sollen solche Dateien nicht mehr bearbeitet werden sollen, bei welchen schon eine Datei.ext1 UND Datei.ext1.ext2 im Verzeichnis vorliegen. Beispiel:
Verzeichnis mit Inhalt:
Ich würde jetzt gerne ein Kommando finden, welches nur die Datei b.gmn an das Script übergibt. Mit meinem bisherigen Versuchen, z.B.:
| find -type f -name \*.gmn -not \( -name \*.gmn -name \*.gmn.tcx \)
|
war ich nicht erfolgreich, da ich im besten Fall beide .gmn-Dateien (also a.gmn und b.gmn) bekommen habe... Vielen Dank für eure Hilfe!
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12832
|
Du kannst das mit der Shell lösen. Auf meiner Benutzerseite ist ein solches Skript.
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
Das ginge z.B. so (statt echo könntest du dann dein Skript einsetzen, dann bekommt das die Dateien als Argumente übergeben):
| sort -z <(find -name "*.gnm" -print0) <(find -name "*.gnm.tcx" -print0 | sed -z 's/\.gnm\.tcx$/.gnm/') | uniq -uz | xargs -0 echo
|
Edit: das cat braucht es nicht.
Edit2: falsche Ersetzung des Suffix korrigiert.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12832
|
seahawk1986 schrieb: Das ginge z.B. so (statt echo könntest du dann dein Skript einsetzen, dann bekommt das die Dateien als Argumente übergeben):
| sort -z <(find -name "*.gnm" -print0) <(find -name "*.gnm.tcx" -print0 | sed -z 's/\.gnm\.tcx$/.gnm/') | uniq -uz | xargs -0 echo
|
Das ist mir zu umständlich. Ich würde das ganz einfach mit der Shell machen: | for f in *.gmn; do
target="$f.tcx"
test "$target" -nt "$f" || convert "$f" >|"$target"
done
|
|
Papiertiger
(Themenstarter)
Anmeldungsdatum: 7. April 2010
Beiträge: 17
Wohnort: Neuburg an der Donau
|
Hallo zusammen, Danke für die schnellen Antworten. Ich muss das später mal ausprobieren. Das praktische an "find" war für mich, dass der Befehl rekursiv arbeitet.
Das von mir erwähnte erste Script legt die .gmn-Dateien nämlich in einer Verzeichnisstruktur sortiert nach Jahr und Monat an. Da kam mir das rekursive "find" ganz gelegen, da ich beide Scripte im Ursprungsverzeichnis starten könnte. Oder lassen sich eure Vorschläge auch rekursiv gestalten? Vielen Dank!
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
Papiertiger schrieb: Oder lassen sich eure Vorschläge auch rekursiv gestalten?
Mein Vorschlag arbeitet bereits rekursiv, wenn man die I/O Operationen noch weiter reduzieren wollte, könnte man das noch so umformen:
| find -type f -name "*.gmn" -o -name "*.gmn.tcx" -print0 | sed -z 's/\.tcx$//' | sort -z | uniq -uz | xargs -0 echo
|
Die Bash kennt die globstar Option, also könnte man für den Ansatz von rklm sowas machen:
| shopt -s globstar
shopt -s nullglob
for f in **/*.gmn; do
target="$f.tcx"
test "$target" -nt "$f" || convert "$f" >|"$target"
done
|
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12832
|
seahawk1986 schrieb: Papiertiger schrieb: Oder lassen sich eure Vorschläge auch rekursiv gestalten?
Mein Vorschlag arbeitet bereits rekursiv, wenn man die I/O Operationen noch weiter reduzieren wollte, könnte man das noch so umformen:
| find -type f -name "*.gmn" -o -name "*.gmn.tcx" -print0 | sed -z 's/\.tcx$//' | sort -z | uniq -uz | xargs -0 echo
|
Mit find würde ich das so machen: | find -type f -name '*.gmn' -exec sh -c '
for f; do
target="$f.tcx"
test "$target" -nt "$f" || convert "$f" >|"$target"
done
' -- {} +
|
Vorteil hier ist, dass die Zieldatei auch überschrieben wird, wenn die Eingabe neuer ist. Das leistet der find von seahawk1986 nicht.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Papiertiger schrieb:
Mit meinem bisherigen Versuchen, z.B.:
| find -type f -name \*.gmn -not \( -name \*.gmn -name \*.gmn.tcx \)
|
war ich nicht erfolgreich, da ich im besten Fall beide .gmn-Dateien (also a.gmn und b.gmn) bekommen habe...
Das klappt nicht, weil find sich nacheinander alle Dateien anschaut.
Jetzt findet es beispielsweise a.gmn.
Du willst nur .gmn-Dateien betrachten ... | find -type f -name "*.gmn" -exec wennKeinePassendeTcxDateiDaDannTCXeSie.sh {} ";"
|
Und wennKeinePassendeTcxDateiDaDannTCXeSie.sh sieht so aus:
| #/bin/bash
#
datei="$1"
test -e ${datei}.tcx || tcxconvert "${datei}" "${datei}.tcx"
|
Statt das in ein Skript zu schreiben müsste es sich auch als Shellbefehl von find ausführen lassen.
Ungetestet und daher, wahrscheinlich, noch fehlerbehaftet: | find -type f -name "*.gmn" -exec bash -c 'test -e "{}.tcx" || ./tcxconvert.sh "{}" "{}.tcx"' ";"
|
tcxconvert.sh wäre natürlich Dein Programm, und die Annahme ist, dass es die Syntax hat:
| tcxconvert.sh INPUTFILENAME OUTPUTFILENAME
|
An rklms Lösung stört mich, dass da noch eine for-Schleife ist, deren Aufgabe ich nicht begreife. Was seahawk86 mit -print0/sed/sort/uniq/xargs veranstaltet muss ich auch erraten. Ich schätze die Vereinigungsmenge aller gmn- und gmn.tcx-Dateien bilden, die extrahieren, deren Vorname nur 1x auftaucht (auch gmn.tcx-Dateien ohne passende gmn-Datei?) und dann diese bearbeiten.
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
user_unknown schrieb: Was seahawk86 mit -print0/sed/sort/uniq/xargs veranstaltet muss ich auch erraten. Ich schätze die Vereinigungsmenge aller gmn- und gmn.tcx-Dateien bilden, die extrahieren, deren Vorname nur 1x auftaucht (auch gmn.tcx-Dateien ohne passende gmn-Datei?) und dann diese bearbeiten.
Ja, wobei das mit Sets zugegeben besser funktioniert, weil dann gmn.tcx-Dateien ohne dazugehörige gmn-Datei kein Problem sind:
| #!/usr/bin/env python3
from pathlib import Path
gmn_files = set(str(f) for f in Path('.').rglob('*.gmn') if f.is_file())
gmn_tcx_files = set(f.stem for f in Path('.').rglob('*.gmn.tcx') if f.is_file())
for file in gmn_files.difference(gmn_tcx_files):
print(file)
|
Die Lösung für jede gmn-Datei auf eine gmn.tcx-Datei zu prüfen ist vergleichsweise langsam:
$ ls -1 *.gmn | wc -w
10000
$ ls -1 *.gmn.tcx | wc -w
4980
$ time find -type f -name "*.gmn" -exec bash -c 'test -e "{}.tcx" || echo "{}"' ";" > ../find_test_result.txt
real 1m19,794s
user 1m5,204s
sys 0m11,149s
$ time ../test.py > ../py_result.txt
real 0m0,734s
user 0m0,617s
sys 0m0,085s
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12832
|
user_unknown schrieb:
An rklms Lösung stört mich, dass da noch eine for-Schleife ist, deren Aufgabe ich nicht begreife.
Die Shell muss halt alle Argumente bearbeiten, wenn man nicht für jede gefundene Datei eine Shell starten will. Ich vermeide das üblicherweise, weil die Schleife in der Shell effizienter ist als ständig neue Prozesse zu starten.
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
rklm schrieb: Die Shell muss halt alle Argumente bearbeiten, wenn man nicht für jede gefundene Datei eine Shell starten will. Ich vermeide das üblicherweise, weil die Schleife in der Shell effizienter ist als ständig neue Prozesse zu starten.
Oh ja stimmt, der Geschwindigkeitsnachteil stammt vom Spawnen neuer Shells, nicht von den Tests, ob die Dateien existieren. Der Ansatz aus deinem Post ist für 10000 *.gmn Dateien ähnlich schnell wie das Python-Skript.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Ja, und jetzt bitte nochmal testen mit dem richtigen Konverter, 5.000 Dateien in 80s sind ja - öhhhm - 500 Dateien in 8s oder rund 50 Dateien pro Sekunde.
Wenn der Konverter eine Sekunde pro Datei braucht, dann ist der Overhead etwa 2%. Braucht er 10s ist er nur noch 0,2%. Premature optimization is the root of all evil. Sind in der Realität nur 50 Dateien zu bearbeiten, nicht 5000, dann ist der Job in 0,8s erledigt. Premature optimization is the root of all evil.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12832
|
user_unknown schrieb:
Sind in der Realität nur 50 Dateien zu bearbeiten, nicht 5000, dann ist der Job in 0,8s erledigt. Premature optimization is the root of all evil.
Ja, das schreibst Du ja gerne. Nur, dass es halt nicht passt: ich habe nix optimiert, ich baue solche Skripte einfach immer so. Das kostet mich gar keinen extra Aufwand. Das Zitat bezieht sich auf Fälle, in denen jemand, ggf. sogar erheblichen, Aufwand spendiert hat, um eine (vermeintliche) Optimierung zu implementieren. Das ist hier nicht der Fall. Ich mache das lediglich anders als Du. Es lohnt auch, sich einmal das volle Zitat von Knuth zu lesen.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
rklm schrieb: user_unknown schrieb:
Sind in der Realität nur 50 Dateien zu bearbeiten, nicht 5000, dann ist der Job in 0,8s erledigt. Premature optimization is the root of all evil.
Ja, das schreibst Du ja gerne.
Nein, ich bin es leid das immer wieder zu schreiben.
Nur, dass es halt nicht passt: ich habe nix optimiert, ich baue solche Skripte einfach immer so. Das kostet mich gar keinen extra Aufwand.
Ja, das ist auch schön, wenn Dir das inzwischen in Fleisch und Blut übergegangen ist. Nur der Fragesteller machte auf mich nicht den Eindruck in der Materie drin zu sein, und dann ist oft die einfachste Lösung die beste.
Das Zitat bezieht sich auf Fälle, in denen jemand, ggf. sogar erheblichen, Aufwand spendiert hat, um eine (vermeintliche) Optimierung zu implementieren. Das ist hier nicht der Fall.
Vermeintliche Optimierung ist m.E. schon der Fall, weil keine Performanceanforderungen benannt worden sind.
Ich mache das lediglich anders als Du.
Manches sogar besser. Dieser Lösungsweg ist auch unter Umständen besser. Ich würde hier die einfachste Lösung wählen und mich nur bei Bedarf zu weiteren Verbesserungen vorarbeiten.
Es lohnt auch, sich einmal das volle Zitat von Knuth zu lesen.
Oder hier, in Structured Programming with go to Statements von 1974.
|
Papiertiger
(Themenstarter)
Anmeldungsdatum: 7. April 2010
Beiträge: 17
Wohnort: Neuburg an der Donau
|
Hallo zusammen, Auch wenn es schon lange her ist, ist bin erst jetzt zum Testen gekommen. Vielen Dank für Eure Antworten. Ich habe mich für die folgende Variante entschieden:
| find -type f -name "*.gmn" -exec bash -c 'test -e "{}.tcx" || gmn2tcx "{}" ' ";"
|
Von Python habe ich keine Ahnung.
|