PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : bash: Schleifenausstieg bei Fehler in Zuweisung mit Expression



foto-andreas
23-04-2008, 10:06
Hallo vom Neuling hier, gleich mit einem seltsamen Verhalten der bash: Nach einer fehlerhaften Expression in einer Zuweisung gehts beim Script nicht da weiter, wo ich erwarte. Schaut doch mal bitte:


#!/bin/bash

echo "Start"

von=$[8#9]

echo "vor verschachtelter Schleife"

for j in 1 2; do
echo "Start outer $i"
for i in 1 2; do
echo "Start inner $i"
vom=$[8#9]
echo "OK inner $i"
done
echo "RC inner for: $?"
echo "OK outer $j"
done
echo "RC outer for: $?"

echo "vor einfacher Schleife"

for i in 1 2; do
echo "single $i"
von=$[8#9]
echo "OK single $i"
done
echo "RC single for: $?"

echo "Ende"


und hier das Ergebnis:

Start
x.sh: line 5: 8#9: value too great for base (error token is "8#9")
vor verschachtelter Schleife
Start outer
Start inner 1
x.sh: line 13: 8#9: value too great for base (error token is "8#9")
RC outer for: 1
vor einfacher Schleife
single 1
x.sh: line 20: 8#9: value too great for base (error token is "8#9")
RC single for: 1
Ende


Wieso wird nach der fehlerhaften Zuweisung nicht mit dem nächsten Befehl weitergemacht? Das ist beim ersten Fehler nur zufällig so, denn:

Wieso lande ich hinter der äußersten Schleife, nicht wenigstens hinter der innersten?

Wieso wird alternativ das Script nicht ganz beendet?

Kann mir jemand dazu etwas sagen? Und bei welchen Fehlern bzw. Konstruktionen kann mir das wohl sonst noch passieren?

Andreas

undefined
23-04-2008, 11:27
Die Syntax Fehler Meldung ist doch eindeutig, oder? ;)
http://www-user.tu-chemnitz.de/~hot/unix_linux_werkzeugkasten/bash.html#vars

foto-andreas
23-04-2008, 11:44
ja klar, es geht mir ja nicht um den Fehler selbst sondern die Reaktion der bash nach dem Fehler.

Andreas

jan61
30-04-2008, 23:05
Moin,

ich bin nicht ganz sicher, ob meine These wissenschaftlich korrekt ist (dazu müsste man sich in den Parser der Shell vergraben): Es dürfte daran liegen, wie die Shell Kommandos parst und ausführt. Die Shell ist ja ein relativ simpler (das meine ich nicht abwertend!) Interpreter, der das Script quasi zeilenweise einliest, parst und dann ausführt. Tritt beim Parsen ein Fehler auf, dann wird die weitere Bearbeitung des aktuellen Befehls abgebrochen und mit dem nächsten weitergemacht. Das ist das Standardverhalten.

Deine äußere Schleife IST dieser abzuarbeitende Befehl. Egal wie tief drinnen Du einen Syntaxfehler versteckst - für die Shell ist es ein Syntaxfehler der äußersten Schleife, die nur weitere Befehle enthält - und damit packt sie das Ding weg.

HTH
Jan

foto-andreas
02-05-2008, 10:15
Hallo Jan,

ja, Deine Idee dazu kann ich schon nachvollziehen. Es geht aber auch anders, wenn die bash will:


for ((i=0; i<2; i=$[i+1])); do
for ((j=0; j<2; j=$[j+1])); do
echo "($i,$j)"
export N#9
done
done


mit der Ausgabe:


(0,0)
bashtest.sh: line 4: export: `N#9': not a valid identifier
(0,1)
bashtest.sh: line 4: export: `N#9': not a valid identifier
(1,0)
bashtest.sh: line 4: export: `N#9': not a valid identifier
(1,1)
bashtest.sh: line 4: export: `N#9': not a valid identifier


Ist hier der Syntaxfehler nicht so schlimm?

Andreas

PS: Auch in dieser for(())-Konstruktion schmiert $[8#9] oder $[8/0] ab und beendet das ganze Konstrukt.

foto-andreas
02-05-2008, 10:18
ach, ja da hatte ich eben eine Idee, aber das ist der Hammer:


for ((i=0; i<2; i=$[i+1])); do
for ((j=0; j<2; j=$[j+1])); do
echo "($i,$j)"
eval "N=$[8#9]"
done
done


Das funktioniert auch nicht, obwohl eval mindestens so eine "Command-Wertigkeit" wie export hat.

Andreas

WeenerChild
02-05-2008, 13:21
omg...
Vielleicht schreibst du erstmal in Worten, was du eigentlich willst, statt ein Stück syntaktischen Müll nach dem anderen zu Posten, bei dem nicht nur die Bash keinen Anflug einer Ahnung hat, was du eigentlich meinen könntest...

Ich fang mal damit an, wo ich mir deiner Intention recht sicher bin, nämlich die c-Style for-Schleifen, die schreibt man so:

for((i=0; i<10; i++)); do echo $i; done
#oder ohne die "increment-Kurzschreibweise":
for((i=0; i<10; i=i+1)); do echo $i; done
Kann man übrigens auch einfacher so schreiben:

for i in {1..9}; do echo $i; done
# oder mit variabler länge:
limit=25
for i in $(seq 1 $limit); do echo $i; done
Eine Integer-Berechnung kannst du in diesen Doppelklammern machen:

echo $((10 + 5))
foo=((25 * 3))
echo $foo #gibt 75 aus
Ich habe keine Ahnung wo du diesen Unsinn mit den eckiken Klammern her hast.
Und was zur Hölle soll das hier darstellen? 0o

N#9?
Modulo? 0o
Das schreibt man mit "%" (prozent):

echo 5 Modulo 3 ist $((5 % 3))
Und abgesehen davon, dass N#9 irgendwie sinnfrei erscheint, in deinem export-Kommando ist es das gleich doppelt: (also sinnfrei)

export N#90o

help export
export: export [-nf] [name[=value] ...] or export -p
NAMEs are marked for automatic export to the environment of
subsequently executed commands. If the -f option is given,
the NAMEs refer to functions. If no NAMEs are given, or if `-p'
is given, a list of all names that are exported in this shell is
printed. An argument of `-n' says to remove the export property
from subsequent NAMEs. An argument of `--' disables further option
processing.
export variable=wert...
Sprich die bash sieht "N#9" als Variablennamen an (weil man die Wertzuweisung weglassen kann). Und Rauten sind in Variablennamen halt nicht erlaubt. -> Invalid Identifier.
Und wo der Sinn von eval (bei bash im Allgemeinen) liegt, erschließt sich mir auch nicht ganz...

variable="echo ich werde ausgeführt :/"
$variable # gibt 'ich werde ausgeführt :/' aus, ganz ohne "eval"...Vielleicht ist eval ja gut für Sachen mit besonderem Quoting, keine Ahnung.

Was eine "Command-Wertigkeit" ist, weiß ich übrigens auch nicht.

foto-andreas
02-05-2008, 14:19
Hi,

das das kein korrektes Script ist, ist klar. Ich schrieb es bereits oben. Es geht darum, dass die Bash einen Runtime-Fehler bei Exceptions nicht korrekt abfängt. Meine Meinung.

Die reale Anwendung ist, dass ich über diverse Verzeichnisse und Dateien Daten sammle und verarbeite. Das Liefersystem der Daten hat ein Problem, nämlich dass manchmal statt einer Zahl Unsinn geliefert wird.

Die Zahl interpretiere ich mittels $[10#{$zahl}], damit führende Nullen nicht zur Interpretation als Oktalzahl führen, sondern dezimal behandelt werden. Ob da nun $[] steht oder $(()), ist der bash völlig egal.

Nun ist aber $zahl machmal keine Zahl, sondern ".....". Damit gibt es einen Syntaxfehler der bash. Klar. Aber dann soll sie gefälligst wie immer mit dem nächsten Befehl weitermachen und nicht mein gesamtes Schleifenkonstrukt verlassen. Das macht sie nämlich bei anderen Syntaxfehlern auch, nur nicht bei Fehlern in der Expression. Mir wäre egal, wenn das Ergebnis hinterher 0 oder undefiniert wäre. Es geht hier nur um das Verhalten der Bash bei einem Fehler. Und solche Fehler "export N#9" statt "export N=9" usw. hatte ich vorhin gepostet.

War's nun ausführlich genug? Das versuche ich in Foren zu vermeiden, weil kaum jemand Lust hat, Romane zu lesen.

Andreas

jan61
02-05-2008, 14:41
Moin,

nur eine Vermutung: eval führt eine Variablenzuweisung durch, daran könnte sich die Shell stärker verschlucken als bei einem simplen export. Aber wie gesagt: das ist Stochern im Nebel. Um sicherzugehen, müsste man sich den Parser der Shell vornehmen.

Prinzipiell würde ich eh davon ausgehen, dass man Syntaxfehler in Shell-Scripts vermeiden muss und sich nie darauf verlassen darf, nach einem Syntaxfehler kontrolliert im Script weitermachen zu können.

Jan

jan61
02-05-2008, 14:56
Moin,


...Vielleicht ist eval ja gut für Sachen mit besonderem Quoting, keine Ahnung.

hier mal eine nette Möglichkeit, sich per eval einen Haufen Tipparbeit zu schenken:

WORK_DIR=/igend/ein/pfad
TEMP_DIR=/ein/anderer/pfad
INPUT_DIR=/noch/ein/pfad
OUTPUT_DIR=/ein/ganz/anderer/pfad
for dname in WORK_DIR TEMP_DIR INPUT_DIR OUTPUT_DIR NO_DIR; do
dpath=`eval echo $\`echo $dname\``
echo "Pruefe Variable $dname"
if test -z "$dpath"; then
echo "Variable $dname ist nicht gesetzt" >&2
elif test ! -d "$dpath"; then
echo "$dname=$dpath ist kein Verzeichnis" >&2
fi
doneJan

jan61
02-05-2008, 15:07
Moin,


..Es geht darum, dass die Bash einen Runtime-Fehler bei Exceptions nicht korrekt abfängt. Meine Meinung.

Nein, die Bash hat nur eine andere Idee von der Auswirkung syntaktischer Fehler als Du. Und sie ist bei den Expressions vielleicht der Meinung, dass sie mit der gesamten weiteren Befehlsfolge nicht mehr weiterarbeiten kann (überlege mal, was da tatsächlich stehenbleibt, wenn etwas, was keine Zahl ist, in der Variablen steht und ob für die Bash dann da tatsächlich noch was Auswertbares steht).


Die reale Anwendung ist, dass ich über diverse Verzeichnisse und Dateien Daten sammle und verarbeite. Das Liefersystem der Daten hat ein Problem, nämlich dass manchmal statt einer Zahl Unsinn geliefert wird.

Und deshalb der ganze Aufwand?


test -n "`echo $fname | tr -d '[0-9]'`" && echo "$fname ist keine Zahl"Jan

WeenerChild
02-05-2008, 15:54
Ok, meine Schuld, sorry fürs nicht sauber Lesen des Threads. Aber: dein einziges Problem ist doch jetzt deine Verwirrung, richtig? Also in deinem Skript solltest du, wenn du es mit Daten zu tun hast die potentiell nicht das erwartete Format haben (sprich nicht hundertprozentig "vertrauenswürdig" sind) eh eine Überprüfung machen.. (eigentlich immer..)
Geht mit test-[sed|perl|grep|awk] Kombinationen oder sogar mit der bash selbst:

var="23a42"
RE_INT='^[1-9][0-9]*$'
[[ "$var" =~ $RE_INT ]] && echo "$var ist ein Integer." || echo "$var ist kein Integer."

Also ich weiß nicht obs dir hilft, aber kennst du trap? Ich hab mich nie weiter damit beschäftigt. Vielleicht hilft auch das hier (http://fvue.nl/wiki/Bash:_Error_handling)?
Sonst kannst du auch einfach jeden Befehl in einer Subshell ausführen, dann wird die Elternshell nicht beinflusst D:

for j in 1 2; do
echo "Start outer $i"
(
for i in 1 2; do
echo "Start inner $i"
vom=$[8#9]
echo "OK inner $i"
done
)
echo "RC inner for: $?"
echo "OK outer $j"
done
gibt aus:

Start outer
Start inner 1
bash: 8#9: value too great for base (error token is "8#9")
RC inner for: 1
OK outer 1
Start outer
Start inner 1
bash: 8#9: value too great for base (error token is "8#9")
RC inner for: 1
OK outer 2

Und @ jan:

WORK_DIR=/igend/ein/pfad
TEMP_DIR=/ein/anderer/pfad
INPUT_DIR=/noch/ein/pfad
OUTPUT_DIR=/ein/ganz/anderer/pfad
for dname in WORK_DIR TEMP_DIR INPUT_DIR OUTPUT_DIR NO_DIR; do
dpath=`eval echo $\`echo $dname\``
# ...
done
Warum nicht einfach:

WORK_DIR=/igend/ein/pfad
TEMP_DIR=/ein/anderer/pfad
INPUT_DIR=/noch/ein/pfad
OUTPUT_DIR=/ein/ganz/anderer/pfad
for dname in "$WORK_DIR" "$TEMP_DIR" "$INPUT_DIR" .....?

Edit: btw kann es sein, dass diese $[expr] Schreibweise von der ksh übernommen wurde? Die wirkt so kornschellig. (Ich habe auch in "man bash" jetzt darüber nichts finden können, aber da gibts auch das Problem, dass die eckigen Klammern für syntaktische Bausteine verwendet wird, die weggelassen werden können. Von daher kanns auch einfach sein, dass ichs übersehen hab..)

foto-andreas
02-05-2008, 16:23
Hi,

ja das $[] könnte von der korn-Shell kommen, ich hab das hier gerade in man bash gefunden:


The old format $[expression] is deprecated and will be removed in upcoming versions of bash.

Werde mich also an $(()) gewöhnen.

Ich weiß auch genügend Möglichkeiten, wie ich dieses Problem beim nächsten mal umgehen kann. Es ist ja auch längst eine Prüfroutine in dem Script drin.

Bisher war ich jedenfalls von folgenden Grundlagen ausgegangen:

1) ein falscher Befehl liefert einen Exit-Code, den ich abfragen oder abfangen muss, um darauf zu reagieren.

2) wenn ich das nicht tue, gibt es zwei Möglichkeiten:

a) die Shell beendet das Script oder eine eventuelle Subshell
b) die Shell macht beim nächsten Befehl weiter.

Nun habe ich dazugelernt: Manchmal macht die Shell irgendwo später weiter und ich kann nicht nach dem Fehler abfragen, ob es geklappt hat.

Darauf war ich nicht gefasst und dachte, ich frag mal im Forum, ob ich mit weiteren Überraschungen dieser Art rechnen muss, oder ob ich einfach einen logischen Grund für dieses Verhalten übersehen habe.

Danke für Eure Hinweise!

Andreas

jan61
03-05-2008, 02:25
Moin,


...
var="23a42"
RE_INT='^[1-9][0-9]*$'
[[ "$var" =~ $RE_INT ]] && echo "$var ist ein Integer." || echo "$var ist kein Integer."

Naja, 042 ist auch ein Integer. Und warum wild mit regex rumschiessen? Siehe mein anderes Posting - wenn man alle Ziffern löscht und dann noch was übrig bleibt, dann isses keine Zahl.


Also ich weiß nicht obs dir hilft, aber kennst du trap?

trap ist ein Mittel, um Signale innerhalb des Scripts abfangen und darauf reagieren zu können. Wird normalerweise benutzt, um z. B. bei einem <CTRL>-c vor dem Beenden aufräumen zu können.


Sonst kannst du auch einfach jeden Befehl in einer Subshell ausführen, dann wird die Elternshell nicht beinflusst

*würg* - na das ist ja ne tolle Variante - weil ich nicht in der Lage bin, das Script syntaktisch sicher zu machen, verbrate ich mal fix tonnenweise Systemressourcen und lasse so ne Art Fork-Bombe los. Igitt!


Und @ jan:...
Warum nicht einfach:

WORK_DIR=/igend/ein/pfad
TEMP_DIR=/ein/anderer/pfad
INPUT_DIR=/noch/ein/pfad
OUTPUT_DIR=/ein/ganz/anderer/pfad
for dname in "$WORK_DIR" "$TEMP_DIR" "$INPUT_DIR" .....?

Wenn Du Deinen und meinen Code mal vergleichsweise ausführst, dann siehst Du den Unterschied.

Jan

WeenerChild
03-05-2008, 09:52
Naja, 042 ist auch ein Integer. Und warum wild mit regex rumschiessen? Siehe mein anderes Posting - wenn man alle Ziffern löscht und dann noch was übrig bleibt, dann isses keine Zahl.
Joar, nur ist "042" Oktal für die Dezimalzahl "34", viele wissen das nicht und laufen bei sowas in Schwierigkeiten, deswegen hab ich mir angewöhnt explizit keine führenden Nullen zu akzeptieren.. (Wenn ich Oktalzahlen will, dann wird die führende Null vorausgesetzt etc. Kein Grund "00000000000000000000000000000000000000000000000000 000000000002" als Integer durchgehen zu lassen...)
Außerdem war das nur ein Beispiel wie mans in bash machen kann, man kann auch mit bash Zahlen wegschnibblen:

var="123abc456"
echo ${var//[0-9]/} # gibt "abc" aus
# mit test kann man das dann schon benutzen:
[ $(echo ${var//[0-9]/}) ] || echo "$var enthält keine nicht-Zahlen."
Ist wesentlich prozesssparender, funktioniert allerdings auch nur mit "Integern"..
Die Frage: Warum also wild mit Prozessen rumschießen, wenns auch (in verschiedensten Variationen) ohne geht?
Und die doppelte Verneinung ist übrigens nur explizit was der Test (ursprünglich ja auch deiner..) macht: testen, ob wenn alle Zahlen gelöscht sind noch Zeichen übrig sind. Damit sind dann auch leere Strings "Integer"... (Im ggs zu meiner Lösung btw...)



*würg* - na das ist ja ne tolle Variante - weil ich nicht in der Lage bin, das Script syntaktisch sicher zu machen, verbrate ich mal fix tonnenweise Systemressourcen und lasse so ne Art Fork-Bombe los. Igitt!
Genau, fork-Bombe, 1 (in Worten: eine) Subshell öffnen ist wie eine Fork-Bombe. omfg..
Vielleicht solltest du dir noch einmal durchlesen was eigentlich eine Fork-Bombe (http://en.wikipedia.org/wiki/Fork_bomb) ist, damit es nicht wieder zu solch unqualifizierten Aussagen kommt. Das mitunter wichtigste Merkmal einer Fork-Bombe ist das exponentielle Wachstum. Wenn du mir zeigst wie das mit meinem geposteteten Abschitt möglich ist, dann nehm ich dich sogar für voll. Und bitte nicht sowas:

for j in 1 2; do
echo "Start outer $i"
(
for i in 1 2; do
echo "Start inner $i"
:(){ :|:& };:
vom=$[8#9]
echo "OK inner $i"
done
)
echo "RC inner for: $?"
echo "OK outer $j"
doneAnsonsten muss ich deinen Kommentar leider als polemischen Ausrutscher interpretieren, vor allem im Schatten des (von dir im Übrigen gekonnt im quote weggelassenen) "D:" Smilies, der in besagtem Kontext soviel ausdrücken sollte wie: "Das ist nicht wirklich ernst gemeint, weil suboptimal und hirnverbrand. (Schreib lieber syntaktisch einwandfreie Skripte!)".
Außerdem erscheint er (der Kommentar, der untere anderem Highlights wie "tonnenweise Systemressourcen" für die Ausführung 1 Subshell enthält) in einem interessanten Licht, wenn man bedenkt, dass pipes sowie command-substitutions in subshells ausgeführt werden und du dies hier als "Lösung" gepostet hast:

test -n "`echo $fname | tr -d '[0-9]'`" && echo "$fname ist keine Zahl"Da werden 2 Subshells (backticks und die pipe) abgefeuert sowie ein externes Programm aufgerufen...

Und bei deinem eval Beispiel ist mir nicht aufgefallen, dass das verschiedene Dateinamen sind. Irgendeine Berechtigung muss eval ja auch haben, sonst wärs wahrscheinlich nicht da.. (Ich wusste nur wie gesagt nicht, welche Berechtigung genau..)


Und last but not least: $[expr] funktioniert hier nicht in ksh (grade mal installiert) also scheinbar kommts nicht von da.

jan61
04-05-2008, 14:30
Moin,


Joar, nur ist "042" Oktal für die Dezimalzahl "34"...

nein, "042" ist in der Interpretation der Bash eine Oktalzahl - und das muss nicht mit der Intention des liefernden Prozesses übereinstimmen. Gerade in Dateinamen wird oft ein laufender Zähler in konstanter Breite mit Vorlaufnullen benutzt - z. B. um die Sortierung zu erleichtern. Ein Teil des Codes von foto-andreas drehte sich ja gerade darum, diese (für den Zweck falsche) Interpretation durch die Bash zu verhindern.


Die Frage: Warum also wild mit Prozessen rumschießen, wenns auch (in verschiedensten Variationen) ohne geht?

Natürlich kann man sowas auch mit bash-internen Mechanismen machen - ist dann allerdings auch auf die Bash festgenagelt. Da ich ziemlich viel mit ksh (oder sogar dem Urahn Bourne-Shell) zu tun habe, ist bei mir der Griff zuerst in die portable Ecke sozusagen erblich bedingt.


Und die doppelte Verneinung ist übrigens nur explizit was der Test (ursprünglich ja auch deiner..) macht: testen, ob wenn alle Zahlen gelöscht sind noch Zeichen übrig sind. Damit sind dann auch leere Strings "Integer"... (Im ggs zu meiner Lösung btw...)

Da müsste man doch tatsächlich in meiner Variante noch einen test -z "$fname" hinzufügen - sorry, dass ich keine wasserdichte Komplettlösung gepostet habe, wobei ein komplett leerer Dateiname schon ein wenig unwahrscheinlich ist %-/ Im Zweifelsfall reicht es aus, statt echo $fname sicherer echo "$fname" zu schreiben, damit erwischt man auch Dateinamen, die nur aus Whitspaces bestehen.


Genau, fork-Bombe, 1 (in Worten: eine) Subshell öffnen ist wie eine Fork-Bombe. omfg..

<Sarkasmus>Da hab ich doch tatsächlich die "" um das Wort weggelassen :-( Im Übrigen weiß ich, was ne richtige Fork-Bombe ist, danke für die Belehrung. BTW: Deine Subshell startet in einer Schleife mit 2 Durchläufen. Also 2 (in Worten: zwei)</Sarkasmus>


Ansonsten muss ich deinen Kommentar leider als polemischen Ausrutscher interpretieren, vor allem im Schatten des (von dir im Übrigen gekonnt im quote weggelassenen) "D:" Smilies, der in besagtem Kontext soviel ausdrücken sollte wie: "Das ist nicht wirklich ernst gemeint, weil suboptimal und hirnverbrand. (Schreib lieber syntaktisch einwandfreie Skripte!)".

Hm, wer wird jetzt ein wenig polemisch? Ich habe das "D:" nicht "gekonnt im quote weggelassen", sondern schlicht übersehen. Sorry. In Zukunft werde ich Deine Postings mit der Lupe lesen - so wie Du das ja auch immer machst (http://www.mrunix.de/forums/showthread.php?p=263500).


Außerdem erscheint er (der Kommentar, der untere anderem Highlights wie "tonnenweise Systemressourcen" für die Ausführung 1 Subshell enthält) in einem interessanten Licht, wenn man bedenkt, dass pipes sowie command-substitutions in subshells ausgeführt werden und du dies hier als "Lösung" gepostet hast:

Der Kommentar von mir enthielt einen Kontext, den Du "gekonnt im quote weggelassen hast" - nämlich die Frage nach dem Warum. Ich mache einen Unterschied zwischen Prozessen, die man zum Funktionieren des Scripts startet und solchen, die man nur deshalb aufbaut, um Syntaxfehlern auszuweichen. Meine "Lösung" (wie Du so schön und völlig unpolemisch anmerkst) funktioniert mit dem o. g. Zusatz und ist portabel. Und nebenbei ist es schon ein Unterschied, ob ich je Dateinamen 1x diese Prozesse starte oder für jeden Dateinamen innerhalb einer Schleife (hier zwar nur mit 2 Werten, aber mit steigender Schleifenlänge linear anwachsend).

Übrigens noch was: Deine Subshells liefern für den Fehlerfall eine korrekte Reaktion, allerdings muss man sich bewusst sein, dass kein innerhalb dieser Subshell erzeugter Wert für irgendwelche Variablen (hier $vom) außerhalb der Subshell im Zugriff ist. Die müsste man (z. B. per "echo" und entspr. Kommandosubstitution der Subshell) nach außen durchreichen. Aber das nur am Rande.


Und bei deinem eval Beispiel ist mir nicht aufgefallen, dass das verschiedene Dateinamen sind. Irgendeine Berechtigung muss eval ja auch haben, sonst wärs wahrscheinlich nicht da.. (Ich wusste nur wie gesagt nicht, welche Berechtigung genau..)

Du hast es immer noch nicht ausprobiert, ne? Der wesentliche Unterschied ist, dass ich mit der eval-Syntax Zugriff auf den Variablen-Namen habe. Damit kann ich z. B. (für einen solchen Zweck ist es in Scripts von mir oft im Einsatz) dem Anwender gezielt mitteilen, welche notwendige Variable nicht gesetzt ist. Die Aussage "Verzeichnis '' existiert nicht" ist nämlich nicht besonders hilfreich. Eine andere interessante Anwendungsmöglichkeit ist das Zuweisen von Defaults bei nicht gesetzten Variablen, da ich den Variablennamen manipulieren kann (und das Ganze ist auch noch portabel zur ksh):
test -z "$dpath" && dpath=`eval echo $\`echo ${dname}_DEFAULT\``Meine Scripts werden oft durch Konfig-Dateien der Systemumgebung entspr. angepasst, die in den Scripts gesourced werden. Da ist es dann natürlich notwendig, die Vollständigkeit der Konfiguration zu prüfen, es können sich ja Syntaxfehler (überflüssige Blanks, auskommentierte Zeilen usw.) einschleichen. Mit der eval-Schleife erspare ich es mir, jede Variable einzeln zu prüfen und dann in einer Fehlermeldung auszugeben. Man kann sowas auch über eine Funktion machen (2 Parameter: Variablenname, Variableninhalt) oder per set -x den Shell-Ablauf protokollieren ... - wie immer führen verschiedene Wege nach Rom.

EOT
Jan

EDIT: Die Zuweisung eines Defaults mit der eval-Syntax macht natürlich erst in folgender Form richtig Sinn:

test -z "$dpath" && eval $dname=`eval echo $\`echo ${dname}_DEFAULT\``Damit ist z. B. die in meiner ursprünglichen Schleife nicht gesetzte Variable NO_DIR nach diesem Lauf mit dem Wert von NO_DIR_DEFAULT gefüllt.

WeenerChild
04-05-2008, 17:30
nein, "042" ist in der Interpretation der Bash eine Oktalzahl
Stimmt, und
in der Interpretation von sh und
in der Interpretation von ksh und
in der Interpretation von awk und
in der Interpretation von python und
in der Interpretation von ruby und
in der Interpretation von perl und
in der Interpretation von php und
in der Interpretation von Java und
in der Interpretation von Javascript und
in der Interpretation von c++ und
in der Interpretation von c und
(Aufzählung nicht abschließend...)
Da fühlt man sich schon ziemlich wichtig wenn man so viel unterstreichen kann..


Natürlich kann man sowas auch mit bash-internen Mechanismen machen - ist dann allerdings auch auf die Bash festgenagelt. Da ich ziemlich viel mit ksh (oder sogar dem Urahn Bourne-Shell) zu tun habe, ist bei mir der Griff zuerst in die portable Ecke sozusagen erblich bedingt.
Ok, dann schau nochmal auf mein "Urpost" zurück und stelle fest, dass ich als erstes auch eine portable Version vorgeschlagen habe, allerdings da auch mit regexes, denn IMHO gibts nichts Besseres um Userinput (lies: Fremddaten, die der Programmierer nicht selber "eingeschleust" hat; muss also nicht unbedingt von "Menschen" eingegeben worden sein...) zu Validieren.

Geht mit test-[sed|perl|grep|awk] Kombinationen oder sogar mit der bash selbst: (...)



<Sarkasmus>Da hab ich doch tatsächlich die "" um das Wort weggelassen :-( Im Übrigen weiß ich, was ne richtige Fork-Bombe ist, danke für die Belehrung. BTW: Deine Subshell startet in einer Schleife mit 2 Durchläufen. Also 2 (in Worten: zwei)</Sarkasmus>Wow, bist du Student? Du kannst ja zählen...
Aber trotz deiner Behauptung zu wissen was eine Fork-Bomb sei, behauptest du hier, dass 2 Subshells gestartet werden. Was soweit erstmal richtig ist, allerdings werden diese Subshells zeitlich versetzt instanziert und es ist strukturell unmöglich, dass sie gleichzeitig "gelaunched" werden. Denn darum gehts ja bei ner Fork-Bomb. Wenn ich 5 Milliarden Kopien eines Prozesses über 2 Milliarden Jahre verteilt starte und niemals mehr als eine Kopie gleichzeitig im Speicher "lurkt", würde man (/würdest du) es dann auch als "Fork-Bomb" bezeichnen?

Also ich antizipiere hier mal deine Reaktion um das ein wenig abzukürzen: Nein, würde Niemand. Nicht einmal du. Denn du weißt natürlich was eine Fork-Bomb ist. Deswegen muss ich dich korrigieren: im "Fork-Bomb-Vorwurfs"-Kontext wird exakt eine Subshell gestartet. (Im ggs zu deinem geposteten Lösungsansatz. Dort werden in der Tat zwei Subshells gleichzeitig gestartet. Da werden quasi "tonnenweise Systemressourcen" aus dem Fenster geschmissen...)
Und ja, ich weiß um das Scoping Problem bei Subshells. Wie bereits durch den initial hinzugefügten Smilie "D:" und durch die Ausschweifungen in meinem letzten Post deutlich geworden sein sollte: Ich meinte das nicht als ernstzunehmenden Lösungsansatz..


Ich mache einen Unterschied zwischen Prozessen, die man zum Funktionieren des Scripts startet und solchen, die man nur deshalb aufbaut, um Syntaxfehlern auszuweichen. Meine "Lösung" (wie Du so schön und völlig unpolemisch anmerkst) funktioniert mit dem o. g. Zusatz und ist portabel. (...)
Übrigens noch was: Deine Subshells liefern für den Fehlerfall eine korrekte Reaktion, allerdings muss man sich bewusst sein, dass kein innerhalb dieser Subshell erzeugter Wert für irgendwelche Variablen (hier $vom) außerhalb der Subshell im Zugriff ist.
Portable Lösung hab ich wie bereits gesagt auch schon vorgeschlagen und das du immernoch der Meinung bist ich meinte das ernst mit der Subshell finde ich ehrlich gesagt ein wenig traurig. Wieviel deutlicher als:

Das ist nicht wirklich ernst gemeint, weil suboptimal und hirnverbrand. (Schreib lieber syntaktisch einwandfreie Skripte!)solls eigentlich noch werden?
Ich habs mal zum (diesmal hoffentlich) besseren Verständnis typografisch ein wenig "gepimpt".


Du hast es immer noch nicht ausprobiert, ne?
Doch hab ich. Ich hatte mich allerdings vertippt.
s/Dateinamen/Variablennamen/;
Denn der optische Unterschied zwischen
"dname" und
"dpath" ist, das musst du zugeben, nicht sonderlich groß. (Grade wenn alles in Monospace-Font(s) dargestellt wird..) Also ich hatte das Problem bei meinem letzten Post bereits "durchblickt", keine Angst..

jan61
16-05-2008, 22:52
Moin,

aus aktuellem Anlass (http://www.mrunix.de/forums/showthread.php?p=265084) gibts doch noch mal einen Kommentar von mir.


Stimmt, und
in der Interpretation von sh und
...
Da fühlt man sich schon ziemlich wichtig wenn man so viel unterstreichen kann..Wenn Du Dich dadurch besser fühlst - bitte. Aber meine (von Dir wieder mal gekonnt unterschlagene) Hauptaussage lautete: Diese Interpretation ist für diesen Fall schlicht und einfach falsch - egal was für eine Sprache oder welches Programm Du nutzt. Das liefernde Programm baut die Dateinamen dezimal in konstanter Breite mit Vorlaufnullen auf (so ist zumindest meine Interpretation des Ursprungs-Postings), und exakt so muss jedes auswertende Programm die Dateinamen auch interpretieren können, ansonsten ist es fehlerhaft!

Nebenbei: Deine Aussage mag für die Sprachen zutreffen, die nicht typisiert sind (wobei man bei perl, php usw. streiten kann - diese Sprachen bieten durchaus Mittel, Variablen nach Erfordernis als String oder numerisch zu behandeln), für C, C++ und Java ist das einfach nur Quatsch. Wir reden hier nämlich von Zeichenketten (es sind Dateinamen, schon vergessen?) und da liegt es allein am Programmierer, wie er die auswertet, nicht an der Sprache.


Ok, dann schau nochmal auf mein "Urpost" zurück und stelle fest, dass ich als erstes auch eine portable Version vorgeschlagen habe, allerdings da auch mit regexes, denn IMHO gibts nichts Besseres um Userinput (lies: Fremddaten, die der Programmierer nicht selber "eingeschleust" hat; muss also nicht unbedingt von "Menschen" eingegeben worden sein...) zu Validieren.

1. Habe ich es abgestritten, dass Du eine solche Lösung gebracht hast?
2. Nimmst Du einen Vorschlaghammer, um eine Reißzwecke in einen Korken zu drücken? Regex sind sehr mächtig, aber auch deutlich rechenintensiver als einfache Bereichsprüfungen. Für einen einfachen Test, ob noch was anderes außer Ziffern in einer Zeichenkette enthalten ist, ziehe ich auch möglichst einfache Verfahren vor (ich weiß, in der bash geht das auch ganz ohne fork - Du brauchst es nicht nochmal hinzuschreiben). Wenn da ein Float in allen seinen möglichen Ausprägungen stehen könnte, würde ich allerdings wahrscheinlich auch auf Regex zugreifen.


Wow, bist du Student? Du kannst ja zählen..."Thema verfehlt, Setzen, 6". So würde Deine Sachkundelehrerin reagieren.


Aber trotz deiner Behauptung zu wissen was eine Fork-Bomb sei, behauptest du hier, dass 2 Subshells gestartet werden. (bla bla bla)...Sag mal, liest Du überhaupt die Postings, auf die Du antwortest? Ich dachte eigentlich, dass spätestens seit meiner 2. Antwort klar geworden sei, dass ich die Begriffe "Fork-Bombe" und "tonnenweise Ressourcen" bildlich, im übertragenen Sinn, grotesk übersteigert, satirisch überspitzt (frag Deine Deutschlehrerin, wenn Du die Wörter nicht verstehst) gemeint habe, also in einem Kontext, der klar machen sollte, dass ich diese Art von Forks als unnötig missbillige (ich weiß es schon seit Deinem vorigen Beitrag: das tust Du auch, Du brauchst es nicht nochmal auszuführen). Und mit meiner Korrektur des Prozesszählers von 1 auf 2 wollte ich auch nur klarstellen (im Zusammenhang mit dem Schleifen-Effekt, dass mit steigender Schleifenlänge auch die Anzahl der Forks linear ansteigt): Ob parallel oder sequentiell - jeder unnötig gestartete Child ist meiner Meinung nach einer zuviel, weil er Systemressourcen belegt, die andere Prozesse vielleicht gerne haben würden. Ich weiß, wir sind da einer Meinung, Du brauchst es mir nicht nochmal zu erklären.


Denn der optische Unterschied zwischen
"dname" und
"dpath" ist, das musst du zugeben, nicht sonderlich groß.

Hm, was soll ich dazu sagen? Du programmierst ja offenbar zumindest gelegentlich. Benutzt Du da Variablen-Namen wie "gruesse_an_meine_magengeschwuere", damit Du sie auseinanderhalten kannst? Schwache Ausrede. Kleiner Tipp: Hättest Du die relevanten Teile meines Code-Schnipsels einfach kopiert und im vim eingefügt, wäre Dir diese "Verwechslung" erspart geblieben.

nun aber endgültig: für mich EOT <WeenerChild_description>End Of Thread</WeenerChild_description>

Jan