ubuntuusers.de

Brauche Hilfe für Skript

Status: Gelöst | Ubuntu-Version: Ubuntu 12.04 (Precise Pangolin)
Antworten |

kalsan

Anmeldungsdatum:
30. Mai 2010

Beiträge: 198

N'Abend, ich habe wenig Erfahrung mit Shell-Programmation und kann das folgende Programm leider nicht umsetzen. Hier ist das Skript in Pseudo-Code:

1
2
3
4
5
6
7
8
Tue Folgendes für alle JPG-Dateien in allen Unterordnern (rekursive Vorgehensweise) im aktuellen Ordner:
{
  identify -format "%[jpeg:sampling-factor]" DATEINAME.JPG
  WENN DIE AUSGABE DES VORHERIGEN BEFEHLS == "1x1,1x1,1x1"
  {
    mogrify -sampling-factor 2x1 DATEINAME.JPG
  }
}

Wie lautet dieser Code in korrektem Shell? Danke im Voraus und liebe Grüsse, Kalsan

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

Ungetestet

1
2
3
4
5
6
7
8
#!/bin/bash

shopt -s globstar

for f in **/*.{JPG,jpg}; do
  identify -format '%[jpeg:sampling-factor]' "$f" | egrep -q '^1x1,1x1,1x1$' \
  && mogrify -sampling-factor 2x1 "$f"
done

Ciao

robert

kalsan

(Themenstarter)

Anmeldungsdatum:
30. Mai 2010

Beiträge: 198

Herzlichen Dank, das Skript funktioniert! Allerdings erscheint folgende Meldung:

1
identify: unable to open image `**/*.JPG':  @ error/blob.c/OpenBlob/2587.

Ist das normal? lg, Kalsan

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

kalsan schrieb:

Herzlichen Dank, das Skript funktioniert! Allerdings erscheint folgende Meldung:

1
identify: unable to open image `**/*.JPG':  @ error/blob.c/OpenBlob/2587.

Ist das normal? lg, Kalsan

Wenn Du keine Dateien mit groß geschriebener Extension hast, dann ja. Ist unschön, ich weiß. Das könnte man nochmal umbauen mit find.

Ciao

robert

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2510

rklm schrieb:

Das könnte man nochmal umbauen mit find.

Oder:

$ echo **/*.[Jj][Pp][Gg]
tmp/bla.jpg tmp/bla.jpG tmp/bla.JPg tmp/bla.JPG tmp/keineahnung/foo.jpg

Oder mit „shopt -s extglob“ vielleicht auch (falls man die komischen Mischformen nicht haben will):

$ echo **/*.@(jpg|JPG)
tmp/bla.jpg tmp/bla.JPG tmp/keineahnung/foo.jpg

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

Vain schrieb:

rklm schrieb:

Das könnte man nochmal umbauen mit find.

Oder:

Du wirst aber das Problem nicht los, dass alle Shell-Glob-Muster zu sich selbst expandieren, wenn sie nix finden. Es sei denn man setzt "nullglob". In meiner Erfahrung klappt rekursives Globben weit schlechter als find. Man hat beim Globben auch das Problem, dass eine Kommandozeile nur eine begrenzte Länge haben darf. Das reicht zwar für die meisten Fälle aus, aber das bedeutet auch, dass erst alle Dateien gefunden werden müssen, bevor das Kommando ausgeführt werden kann. find kann da prinzipiell anders vorgehen.

Ciao

robert

schmidti411

Avatar von schmidti411

Anmeldungsdatum:
24. Oktober 2007

Beiträge: 152

Wohnort: Berlin

Warum nicht mit file den Dateitypen bestimmen. Ist doch viel genauer als immer auf die Endung zu achten. (Dateiendungen sind doch was für Windowsnutzer ☺ )

# file ./wallpapers/Turtle.jpg
./wallpapers/Turtle.jpg: JPEG image data, JFIF standard 1.01

Könnte mir sowas vorstellen:

1
2
3
4
5
6

for F in `find . -type f`
do
   file "$F" | grep JPEG 1>/dev/null 2>&1
   [ $? -eq 0 ] && identify -format '%[jpeg:sampling-factor]' "$F" | egrep -q '^1x1,1x1,1x1$' && mogrify -sampling-factor 2x1 "$F"
done

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2510

rklm schrieb:

Vain schrieb:

rklm schrieb:

Das könnte man nochmal umbauen mit find.

Oder:

[...]

Stimmt.

Ich hatte gestern abend im Kopf, dass du oben in deinem Schnipsel „nullglob“ gesetzt hast, aber das ist ja ein „globstar“. 😬

Trotzdem ist Brace Expansion nochmal was anderes als Globbing, wobei ein „nullglob“ in diesem Fall natürlich beides erschlägt:

$ echo **/*.{JPG,jpg}
**/*.JPG tmp/bla.jpg tmp/keineahnung/foo.jpg

$ echo **/*.@(JPG|jpg)
tmp/bla.jpg tmp/keineahnung/foo.jpg

$ shopt -s nullglob

$ echo **/*.{JPG,jpg}
tmp/bla.jpg tmp/keineahnung/foo.jpg

Die Beschränkung von „argv[]“ gilt auch nicht für „for“-Schleifen (laut dieser Seite ist „ARG_MAX“ neuerdings von der Stack-Größe abhängig):

$ ulimit -s 256
$ bash
$ /bin/echo **
bash: /bin/echo: Argument list too long
$ for i in **; do echo "$i"; done | wc -l
208904

Aber das ist nur Spielerei. „find“ wäre insgesamt schon die schönere Lösung, da bin ich bei dir.


schmidti411 schrieb:

Warum nicht mit file den Dateitypen bestimmen. Ist doch viel genauer als immer auf die Endung zu achten.

Schon. Aber es ist auch um Größenordnungen langsamer. Würde ich nur machen, wenn ich mir nicht sicher wäre, dass die Bilder „ordentliche“ Endungen haben.

Dass Dateiendungen nicht nur was für Windowsnutzer sind, sieht man an dem Beispiel auch sehr gut. Würden wir die gar nicht verwenden, dann müsstest du fast immer „file“ benutzen, was einfach total lahm ist. Mit Endungen hat man aber eine schöne Vorsortierung.

Die Endungen wegzulassen, ergibt nur in relativ wenig Fällen einen Sinn, finde ich. Bei ausführbaren Dateien zum Beispiel, aber da auch nur in einem „Sonderfall“: Heißt ein Shellskript „create-all-playlists“, dann kann ich das immer so an der Shell schreiben und – bei Bedarf – das Ding irgendwann als Python-Skript oder C-Programm neu implementieren und den Namen beibehalten. Das ist ein echter Mehrwert, finde ich. Aber sonst? Wo wäre der allgemeine Vorteil, wenn ich Bildern kein „.jpg“ mehr verpassen würde?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

schmidti411 schrieb:

Warum nicht mit file den Dateitypen bestimmen. Ist doch viel genauer als immer auf die Endung zu achten. (Dateiendungen sind doch was für Windowsnutzer ☺ )

Dateiendungen funktionieren in der Praxis erstaunlich gut. Außerdem kann find das schneller auswerten, da die Checks auf Namen eingebaut sind und man dafür nicht die Datei teilweise lesen muss.

Könnte mir sowas vorstellen:

1
2
3
4
5
6

for F in `find . -type f`
do
   file "$F" | grep JPEG 1>/dev/null 2>&1
   [ $? -eq 0 ] && identify -format '%[jpeg:sampling-factor]' "$F" | egrep -q '^1x1,1x1,1x1$' && mogrify -sampling-factor 2x1 "$F"
done

Das Bestimmen der Dateien in der Art wie Du das da machst ist nicht sicher in Bezug auf Leerzeichen und andere Späße in Dateinamen. Du kannst außerdem mit "grep -q" die Ausgabe unterdrücken und gleich den Exit-Status von grep auswerten. Ich würde aber lieber file --mime-type nutzen, weil das direkt den MIME Typ ausgibt.

1
2
3
4
5
6
7
find . -type f -exec bash -c '
for f; do
  file -b --mime-type "$f" | egrep -q "^image/jpeg$" \
  && identify -format "%[jpeg:sampling-factor]" "$f" | egrep -q "^1x1,1x1,1x1$" \
  && mogrify -sampling-factor 2x1 "$f"
done
' -- {} +

Ciao

robert

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

Vain schrieb:

Die Beschränkung von „argv[]“ gilt auch nicht für „for“-Schleifen

Gut zu wissen! Der Nachteil bleibt allerdings, dass dann erst alle Dateien gefunden werden müssen, bevor die for-Schleife loslaufen darf (theoretisch könnte man das anders machen, aber dann würde sich die Semantik ändern, da der Schleifenrumpf z.B. auch Dateien löschen könnte, die später noch gefunden würden; dann hätte die parallelisierte Version ein anderes Verhalten). find kann im Prinzip zu beliebigen Zeiten den Prozess mit -exec starten und so viele Dateinamen übergeben, wie die Kommandozeile erlaubt oder find für richtig hält (bei "-execdir ... {} +" sind es i.d.R. alle, die in einem Verzeichnis liegen).

Aber das ist nur Spielerei. „find“ wäre insgesamt schon die schönere Lösung, da bin ich bei dir.

☺ Das einzig unschöne ist die Integration in ein Skript, finde ich. Wenn man das so macht wie ich im letzten Posting, sieht das schnell schlecht aus. Moment, hier gibt es eine schöne Möglichkeit, die aber nur funktioniert, wenn der Schleifenrumpf in einer anderen Shell laufen darf, weil er keine Daten an den Rest des Skriptes zurück liefern muss (hier der Fall):

1
2
3
4
5
find . -type f -print0 | while read -d '' f; do
  file -b --mime-type "$f" | egrep -q '^image/jpeg$' \
  && identify -format '%[jpeg:sampling-factor]' "$f" | egrep -q '^1x1,1x1,1x1$' \
  && mogrify -sampling-factor 2x1 "$f"
done

Der Trick ist, find -print0 mit read -d '' zu kombinieren: durch den leeren String als Delimiter nimmt read das Null-Byte als Delimiter. ☺

Ciao

robert

kalsan

(Themenstarter)

Anmeldungsdatum:
30. Mai 2010

Beiträge: 198

Wow, das ist ja zu einer rechten Fachsimpelei geworden 😲 Ich verstehe das Skript zwar nur ansatzweise, aber ich habe es nach viel Bastelei irgendwie geschafft, einen "echo"-Befehl reinzubauen, der die Dateinamen der modifizierten Dateien wiedergibt (das ist zur Kontrolle praktisch):

1
2
3
4
5
find . -type f -print0 | while read -d '' f; do
  file -b --mime-type "$f" | egrep -q '^image/jpeg$' \
  && identify -format '%[jpeg:sampling-factor]' "$f" | egrep -q '^1x1,1x1,1x1$' \
  && mogrify -sampling-factor 2x1 "$f" && echo "$f"
done

Mit "bash meinSkript.sh" kann ich das Skript ausführen, ohne dass es zu weiteren Fehlermeldungen kommt. Aus meiner Sicht wäre das Skript somit "betriebsbereit", aber bevor ich es auf die riesige Fotosammlung loslasse, möchte ich doch noch eine Bestätigung von euch Experten, dass das Skript keine Daten zerstören wird. Was meint ihr, ist alles in Ordnung?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

kalsan schrieb:

Wow, das ist ja zu einer rechten Fachsimpelei geworden 😲

☺ Das machen wir gerne, weil jeder etwas dabei lernt.

Ich verstehe das Skript zwar nur ansatzweise, aber ich habe es nach viel Bastelei irgendwie geschafft, einen "echo"-Befehl reinzubauen, der die Dateinamen der modifizierten Dateien wiedergibt (das ist zur Kontrolle praktisch):

Was genau verstehst Du nicht? Das müssen wir dann natürlich noch erklären.

Mit "bash meinSkript.sh" kann ich das Skript ausführen, ohne dass es zu weiteren Fehlermeldungen kommt. Aus meiner Sicht wäre das Skript somit "betriebsbereit", aber bevor ich es auf die riesige Fotosammlung loslasse, möchte ich doch noch eine Bestätigung von euch Experten, dass das Skript keine Daten zerstören wird. Was meint ihr, ist alles in Ordnung?

Ich weiß nicht, wie mogrify seinen Exit-Code nutzt. Ggf. ist das hier sicherer, was die Ausgabe angeht:

1
2
3
4
5
find . -type f -print0 | while read -d '' f; do
  file -b --mime-type "$f" | egrep -q '^image/jpeg$' \
  && identify -format '%[jpeg:sampling-factor]' "$f" | egrep -q '^1x1,1x1,1x1$' \
  && { mogrify -sampling-factor 2x1 "$f"; echo "$f"; }
done

Das echo kommt dann immer und nicht nur, wenn mogrify 0 liefert, wie in Deiner Version.

Ciao

robert

kalsan

(Themenstarter)

Anmeldungsdatum:
30. Mai 2010

Beiträge: 198

rklm schrieb:

Was genau verstehst Du nicht? Das müssen wir dann natürlich noch erklären.

Haha, vielen Dank, das ist nicht nötig ☺ Ich sollte sowieso schon längst mal meine Nase in ein Tutorial über Skripting stecken. Zwei, drei Tage Arbeit und ich verstehe dieses und alle anderen Skripte auch.

Das echo kommt dann immer und nicht nur, wenn mogrify 0 liefert, wie in Deiner Version.

Aha, wieder was gelernt: Mit dem Strichpunkt habe ich's auch schon probiert, aber ohne Erfolg. Anscheinend fehlten die Klammern.

Ciao robert

Vielen vielen Dank für all die grossartige Hilfe! Nun kann ich mich endlich an die Datenbank wagen 👍 lg, Kalsan

kalsan

(Themenstarter)

Anmeldungsdatum:
30. Mai 2010

Beiträge: 198

Herzlichen Dank nochmals, das hat wunderbar geklappt ☺ lg, Kalsan

Antworten |