ich grüsse euch Alle!
Einleitung
ich mache seit Jahrzehnte nebenbei auch shell-Scripts auf gutem Niveau
(aber noch nicht Guru-Level...) und habe folgenden Evolutionsschritt
schon lange hinter mir:
1
#NAIV (1)
2
for f in $(find . -iname '*.wma') # verschluckt sich an Leerzeichen etc. in Datei~ u. Verzeichnisnamen
3
do
4
:
5
done
6
7
#DEFENSIV (2)
8
find . -iname '*.wma' | while read f # robust. Dachte ich zumindest bis heute...
9
do
10
:
11
done
PROBLEM
TL;DR:
* <liste> | while read <var> ; do job ; done laesst bereits im 2.
Schleifendurchlauf <var> leer also ""
* for <var> in "${<liste>[@]}" ; do job ; done funktioniert
heute wollte ich viele, in mehreren Unterverzeichnisse verstreute, WMA
Dateien nach OGG konvertieren und erlebte erstmals die Böse
Überrasschung dass o.g. DEFENSIV (2) Variante versagte.
im Anhang ist wma.pflist eine stark verkürzte liste von Dateien und
wma2ogg.bash die zusammengefasste Analyse.
Die im script mit ##WTF## markierten Zeilen verursachen das Problem.
Der script ist nur der Problemanalyse wegen so überstrukturiert
(strikt prozedural), denn eigentlich ist sowas fuer mich in 99.9% der
Fälle eine einfache ad-hoc Fingerübung am bash-Prompt, gerne auch vor
dem ersten Kaffee...
Es sind halt in mehreren Aufruftiefen solche ##WTF## Möglichkeiten in
der Datei: da ist mit Verstand nat. nur an jeweils 1 Stelle aufs Mal
solcheine Zeile umzustellen um den Fehler zu reproduzieren.
Die Kiste hier ist ein Ubuntu 16.04.5 LTS (Linux 4.4.0-135-generic)
auf einem aelteren Notebook (HP6730b, C2D P8800, 4GB RAM), täglich
genutzt.
bash: 4.3.48(1)-release
ffmpeg: 2.8.15-0ubuntu0.16.04.1 (gcc + libs a.A.)
FRAGE
kann mir Jemand helfen, eine Erklaerung zu finden WARUM das Problem
auftritt um das Problem gründlich zu verstehen?
(DIE Bitte an Forumschaoten: ein Workaround habe ich bereits gefunden,
s. script-Datei.
Also keine andere Wege aufzeigen - es geht mir hier darum den Fehlerfall
gründlich zu verstehen - Danke!)
HYPOTHESEN
* Fehler in bash: Variablen anfaellig ? (DAS wäre ein
Glueckstreffer...)
* Fehler in ffmpeg: verwurstelt Umgebungsvariablen ?
* ...?
a) dein Problem ($pfn leer) kommt bei mir mit den Eingabedaten nicht,
mußte allerdings etwas rumändern um es Testen zu können. Sehe auch
nicht, wo was verkehrt sein sollte - einzig Newlines (\n) im Dateinamen
gehen schief. Versuch mal nen minimal test case zu basteln.
b) "[ [ -v xxx ] ]" gibt es bei älteren bash-Versionen nicht.
c) "Fehler in ffmpeg: verwurstelt Umgebungsvariablen?" - sowas ist
prinzipiell nicht möglich. Subshells (auch Funktionen in nem
Subshell-Kontext) oder externe Programme können niemals Variablen im
Parent ändern.
d) Variablen kreuz und quer zu definieren und zu benutzen ist nicht die
feine Englische - gibt immer mal Probleme. Besser ist es, Parameter
explizit an Unterprogramme zu übergeben, z.B. <do_shasum "$pfn"> und
dort dann $1 etc benutzen.
foobar schrieb:> c) "Fehler in ffmpeg: verwurstelt Umgebungsvariablen?" - sowas ist> prinzipiell nicht möglich. Subshells (auch Funktionen in nem> Subshell-Kontext) oder externe Programme können niemals Variablen im> Parent ändern.
Definitiv falsche Formulierung. Tatsächlich ist es natürlich so:
c) "Fehler in ffmpeg: verwurstelt Umgebungsvariablen?" - sowas sollte
prinzipiell nicht möglich sein. Geht aber doch, wenn Sicherheitslücken
greifen. Dann können Subshells (auch Funktionen in nem Subshell-Kontext)
oder externe Programme NATÜRLICH auch Variablen im Parent ändern.
Neben vielen anderen Sachen, die dann möglich werden...
Das ist eigentlich eine Trivialität. Besonders interessant wird es erst
durch die vielen bekannten (und vermutlich noch sehr viel zahlreicheren
unbekannten) Sicherheitslücken in ffmpeg, die sich schlicht daraus
ergeben, das ffmpeg ein saukomplexes Stück Software ist, was sehr
regelmäßig mit allen Schlechtigkeiten externer Datenquellen klarkommen
muss.
Ich würde mal davon ausgehen, dass eine genauere Untersuchung hier
letztlich eine weitere Sicherheitslücke vom Status "bisher unbekannt"
zum Status "bekannt" bringt und dann relativ zeitnah zum Status "fixed".
Wenn jemand sich erbarmt, diese Untersuchung durchzuführen und die
Ergebnisse als kompetente Fehlerbeschreibung an der richtigen Stelle im
Netz einzuspeisen...
Der erste Schritt wäre aber natürlich, ffmpeg als Fehlerursache
auszuschließen, indem man an der entsprechenden Stelle im Script einfach
mal was ganz anderes, viel einfacheres macht, die betroffenen Dateien
z.B. einfach nur stumpf umbenennt. Läuft dann das Script erwartungsgemäß
durch, lohnt die Suche nach der ffmpeg-Lücke.
> Definitiv falsche Formulierung.
Quatsch. Umgebungsvariablen können von Kind-Prozessen nicht verändert
werden. Punkt. Nicht gezielt über irgendwelche APIs oder zufällig über
wildgewordene Programme.
Dazu kommt, dass es hier um lokale Variablen der Shell geht - davon weiß
der Kindprozess nicht mal was.
Wenn du nun mit irgendwelchen Sicherheitslücken kommst, ist natürlich
alles möglich. Müßig, darüber zu diskutieren ...
foobar schrieb:> d) Variablen kreuz und quer zu definieren und zu benutzen ist nicht die> feine Englische - gibt immer mal Probleme. Besser ist es, Parameter> explizit an Unterprogramme zu übergeben, z.B. <do_shasum "$pfn"> und> dort dann $1 etc benutzen.
Ja klar! Da ich wie oben beschrieben solche Schlaufen häufig oft ad-hoc
interaktiv direkt am shell-Prompt eingebe, sind normalerweise gar keine
shell-Funktionen dazwischen.
Nun hat es mich eben kalt erwischt und ich musste irgendwie
"Teile-und-Herrsche(-light)" anwenden: eben 'n paar simple Funktionen
und nur mit den globalen Variablen.
Ich wollte da eben gerade nichts vergolden mit den normalen
good practices .
Die 3 Variablen (pfn/out/log) sind ja nicht kreuz und quer definiert:
sie werden genau nur in den 2 Funktionen mit den 2 Schlaufenvarianten
(welche nur zur EXOR-Verwendung gedacht sind) angelegt und pro
Schlaufenduchlauf exakt nur 1x mit Werte besetzt, tiefer in der
Aufrufverschachtelung werden sie ja nur noch gelesen.
c-hater schrieb:> Der erste Schritt wäre aber natürlich, ffmpeg als Fehlerursache> auszuschließen, indem man an der entsprechenden Stelle im Script einfach> mal was ganz anderes, viel einfacheres macht, die betroffenen Dateien> z.B. einfach nur stumpf umbenennt. Läuft dann das Script erwartungsgemäß> durch, lohnt die Suche nach der ffmpeg-Lücke.
Zu genau diesem Zwecke ist die (Blendwerks-)Funktion do_shasum(): wird
nur eine (beliebige) Prüfsumme berechnet anstelle von Audioformat
umrechnen u. neue Datei anlegen so klappt es auch mit allen
aufgefuehrten Schleifenvarianten.
Selbst das sammeln der stdout/stderr-Ausgaben in Logdateien ist nicht
primaeres Ziel, das dient nur dazu die Ausgaben zu minimieren.
ffmpeg steht also schon im Aufmerksamkeitsfokus - ausser ich uebersehe
eine Stein nicht, der so gross ist dass ich gar nicht darüber straucheln
kann.
Sicherheitsluecke in ffmpeg? Ich will ja noch nicht wirklich an sowas
glauben, stammt dies doch aus den ordinären package-repos von (L)ubuntu.
Naja, auch diese kann's ja mal treffen...
Deswegen erst mal hier im uc.net-Forum abchecken.
Hier schon mal ein Fingerprint meines ffmpeg-Executables, das linkt aber
noch gegen 9 libs welche dann auch noch zu untersuchen sind wenn dies
die richtige Spur ist...
In meinem Q'n'D-script fehlt auf Zeile 1 die shebang magic ,
wird also auch der richtige Interpreter angewendet?
Ich meine "JA": fuer mein Account steht /bin/bash als shell und Anlass
der ganzen Untersuchung war ja dass der Fehler bereits beim eingeben des
ganzen Schlaufenbefehls am Prompt passierte.
Etwas aus der Kat. simple sh o.Ä. wuerde ab den bash-ismen ( '[[',
'-v', ...) spucken.
Das "Verpacken" in eine script-Datei ist nur ein Transporthilfsmittel
für hierher ins Forum und um die Sache reproduzierbar zu persistieren.
Die Datenquelle wma.pflist habe ich vorgängig schon fluechtig mit 'cat
-A' untersucht: damit fand ich bisher "immer" /offendingly encoded
characters/ und hier fällt nichts auf.
Immerhin: das Problem ist portabel und ich kann es exakt reproduzieren
auf einer anderen, kleineren Maschine mit neuerer SW: Lubuntu 18.04.1
LTS (Linux 4.15.0-34-generic, i686) [1]
bash: 4.4.19(1)-release
ffmpeg: 3.4.4-0ubuntu0.18.04.1
foobar schrieb:> Ich vermute das Problem eher vor der Tastatur ...
Das will ich überhaupt nicht ausschliessen, genau deshalb meine Anfrage
hier :-)
foobar schrieb:> Pack in den ffmpeg-Aufruf mal nen "-nostdin" ...
Danke fuer diesen Hinweis: soeben ausprobiert, hilft leider nicht.
BOFH-i18n schrieb:> foobar schrieb:>> Pack in den ffmpeg-Aufruf mal nen "-nostdin" ...>> Danke fuer diesen Hinweis: soeben ausprobiert, hilft leider nicht.
Kommando zurück! -nostdin hilft schon, so ich es richtig
einsetzte...
> foobar schrieb:>> Ich vermute das Problem eher vor der Tastatur ...>> Das will ich überhaupt nicht ausschliessen, genau deshalb meine Anfrage> hier :-)
Q.E.D. :-D
LÖSUNG
bei ffmpeg in scripts -nostdin verwenden.
Klingt komisch, ist aber so.
Danke an foobar fuer den entscheidenden Tipp: hat beim weitergoogeln
sehr geholfen
https://www.google.com/search?q=ffmpeg++shell+loop+variables
NB: FUBAR=FuckedUpBeyondAllRepair --> "foobar" vllt. nicht der beste
Nick...
> NB: FUBAR=FuckedUpBeyondAllRepair --> "foobar" vllt. nicht der> beste Nick...
Doch, passt schon ;-)
https://en.wikipedia.org/wiki/Foobar
"Not to be confused with FUBAR."
Übrigens: Dateinamen können auch "\n" enthalten, mit Leerzeichen
beginnen oder enden. Alles Sachen an denen sich deine
"Defensiv"-Variante verschlucken könnte.
> touch $'Hallo\nWelt.wma'
Sicherer ist z.B.
> find ... -exec irgendwas '{}' \;
oder
> find ... -print0 | xargs -0r -n1 irgendwas
ersteres setzt die bösen Dateinamen direkt in's ARGV von irgendwas
ein, die zweite Zeile schreibt sie NULL-Terminiert auf STDOUT, '\0x00'
ist kein gültiger Dateinamensbestandteil, xargs -0 liest 0-terminierte
Strings und kriegt so die original-Dateinamen.
ich persönlich mag die xargs-Variante am liebsten, da kann man mit
"-P 16" auch ganz einfach ein paar CPU-Kerne mehr auslasten...
Was du versuchen könntest, ohne viel umzustellen:
> find ... -print0 | while read -d $'\000' f
bin mir aber nicht sicher wie portabel das ist, und ob es wirklich alle
Fälle abdeckt.
Interessanter Link. Das mit dem IFS haben wir schon im Studium Ende der
90er gelernt, ich wende schon immer
IFS="
"
an, wenn ich for Schleifen über Dateinamen machen muss, damit ging es
dann meistens. Dachte das ist was ganz olles (ist es) und bekanntes,
deswegen hab ich mich gar nicht getraut das hier zu erwähnen, aber im
Artikel steht das wäre recht unbekannt. Komisch.