warum hängt script?



  • Hallo zusammen,

    ich habe hier einen auszug aus einem script. leider "hängt" das kommando, und wartet auf einen eingabe (ENTER). laut doku, wo ich dies her hab, sollte dies nicht so sein und ist auch ziemlich doof. jemand eine idee, wie ich dies abstelle?

    # !/bin/bash
    
    LOG_FILE="startTestApp.log"
    START_COMMAND="hostname" 
    
    ${START_COMMAND} > >(tee ${LOG_FILE}) 2> >(tee ${LOG_FILE} >&2 )
    

    Quelle: http://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe



  • sven_ schrieb:

    # !/bin/bash
    
    LOG_FILE="startTestApp.log"
    START_COMMAND="hostname" 
    
    ${START_COMMAND} > >(tee ${LOG_FILE}) 2> >(tee ${LOG_FILE} >&2 )
    

    Quelle: http://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe

    Die stackoverflow-Antwort verwendet zwei separate Log-Dateien für stdout und stderr. Das würde ich auch empfehlen, denn sonst sieht das nach einer grauenvollen race condition aus, weil zwei Prozesse in dieselbe Datei schreiben wollen.

    Generell würde ich auch eher die zweite Antwort von stackoverflow nehmen, wo einfach 2>&1 steht. Das mit process substitution ist zwar sehr clever, aber es bringt halt auch nichts, wenn man sich damit selbst austrickst.

    Dein aktuelles Problem könnte aber daher kommen, dass du ein Leerzeichen in der allerersten Zeile hast zwischen # und !, wodurch diese Zeile kein shebang mehr ist, sondern nur noch ein überflüssiger Kommentar.



  • Dieser Thread wurde von Moderator/in rüdiger aus dem Forum Rund um die Programmierung in das Forum Linux/Unix verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • hallo Christoph,

    leider kann ich dir erst jetzt antworten, da ich die letzten tage nicht dazu gekommen bin. vielen vielen dank für deine hilfe! es hat mich wirklich weitergebracht! danke. ich habe dann auf die andere variante umgestellt und die tut wie sau 😃



  • hallo zusammen,

    also ich bins nochmal. einfache programm meit tee starten hat ja dank eurer hilfe schön funktioniert. ich mach dies im moment so:

    PSSH_COMMAND="pssh -O StrictHostKeyChecking=no -v -A -h ${HOST_FILE} -t ${TIME_OUT} -l ${USER_NAME} $1"
    
    #run the pssh
    ${PSSH_COMMAND} 2>&1 | tee -a ${LOG_FILE}
    

    für so etwas wie zum beispiel hostname tut dies wunderbar. wenn ich aber das skript so aufrufe:

    startSkript "java -jar /irgend/ein/langer/pfad/programm_name.jar"
    

    bekomme ich als fehler:

    tee: invalid option -- 'j'
    

    starte ich es als:

    "java /irgend/ein/langer/pfad/programm_name.jar"
    

    bekomme ich als fehler:

    tee: /irgend/ein/langer/pfad/programm_name.jar No such file or directory
    

    [/code]



  • ups ... vorschau mit absenden verwechselt 😞

    im skript bastle ich das log file so zusammen:

    LOG=$(basename $1 .bash)_$1.log
    

    ich nehme an, dass es zwei verschiedene fehler sind, die hier zum tragen kommen, oder???

    gruß

    sven



  • sven_ schrieb:

    ${PSSH_COMMAND} 2>&1 | tee -a ${LOG_FILE}
    

    Das Problem ist diese Zeile. Zuerstmal: Wenn $irgendwas zu einem Dateinamen expandiert, musst du immer "" drumherum setzen, sonst hast du automatisch einen Bug in deinem Skript. Dateien mit Leerzeichen im Pfad werden dann nicht korrekt behandelt.

    Das ist nicht dein aktuelles Problem, aber es wird früher oder später zu Bugs führen, deswegen würde ich mir "" gleich angewöhnen.

    Also nehme ich mal

    ${PSSH_COMMAND} 2>&1 | tee -a "${LOG_FILE}"
    

    Das ist besser, hat aber immer noch ein Problem: der Inhalt der Variable PSSH_COMMAND wird an jedem Leerzeichen gesplittet. Das möchtest du haben, weil PSSH_COMMAND sowohl den Namen eines Programms als auch Parameter enthält.

    Ich mach mal ein einfacheres Beispiel:

    C="c $1"
    # run C
    $C
    

    Angenommen dieses Skript heißt "x" und wird aufgerufen mit

    $ ./x "a"
    

    In dem Fall hat die Variable C den Wert "c a" und Zeile 3 für den Befehl "c" mit dem Parameter "a" aus. Wichtig ist hier: Die "" sind in dem Bash-Skript an keiner Stelle noch vorhanden. Die benutze ich nur im Fließtext, um klar zu machen, wenn ich von Strings rede.

    Angenommen, x wird so aufgerufen:

    $ ./x "a b"
    

    Dann hat C den Wert "c a b", weil $1 den Wert "a b" hat. Zeile 3 führt also den Befehl "c" aus mit den zwei Parametern "a" und "b". Das ist genau das, was du nicht möchtest: Du möchtest, dass der Befehl "c" mit dem einen Parameter "a b" aufgerufen wird.

    Also ein neuer Versuch

    $ ./x "\"a b\""
    

    Jetzt hat C den Wert "c \"a b\"", die Anführungszeichen sind also noch dringeblieben. Das hilft aber leider auch nichts: In Zeile 3 wird "c" mit den zwei Parameter "\"a" und "b\"" aufgerufen. Anführungszeichen in Strings haben keine Sonderbedeutung mehr für Bash.

    Man kann sich nun mit quoting etwas zurechtbasteln. Das sieht dann so aus.

    C="c '$1'"
    # run C
    $C
    

    Der Aufruf

    $ ./x "a b"
    

    führt dann tatsächlich dazu, dass "c" mit dem einen Parameter "a b" aufgerufen wird.

    Das ist aber keine gute Lösung. Angenommen, du möchtest "c" mit zwei Parameter aufrufen. Der erste Parameter soll "a a" sein und der zweite "b".

    $ ./x "a a b"
    

    ist natürlich falsch, das ruft "c" mit 3 Parametern auf.

    $ ./x "\"a a\" b"
    

    Ist auch falsch, weil " keine Sonderbedeutung mehr hat innerhalb von Strings. Das hatten wir oben schon.

    Das ist nun nicht so einfach.

    Ein Aufruf wie

    $ ./x "a a" b
    

    wär ungefähr das, was man gerne hätte. Dann bekommt das Skript x aber zwei Parameter, $1 reicht also nicht mehr. Es gibt zwei Wege, alle Parameter auf einmal anzusprechen in einem Bash-Skript: $* und $@. Das erste, $*, ist effektiv immer der falsche Weg, bleibt also nur $@.

    Das führt dann zu bash-arrays und sieht so aus:

    C=( c "$@" )
    # run C
    "${C[@]}"
    

    Das Quoting mit "" ist hier an beiden Stellen absolut notwendig, damit die Worttrennung korrekt funktioniert. Die Faustregel ist, dass Variablen mit @ immer in "" stehen müssen, sonst ist garantiert irgendwas falsch.

    Der Aufruf

    $ ./x "a a" b
    

    führt dann tatsächlich dazu, dass "c" mit den beiden Parametern "a a" und "b" aufgerufen wird.

    Wie immer beim Programmieren gilt natürlich: Du kannst meine Lösung copy&pasten, aber langfristig besser wäre es, selber nachzulesen, wie bash-arrays funktionieren.



  • hallo christoph,

    vielen dank. das war sehr ausführlich und vorallem lehrreich. ich gebe zu, dass ich immer verwirrt bin, wann ich welche "" bzw '' und welche () {} etc. setzen muß. da komm ich mit python besser klar. ich habe mir ein tutorial über bash arrays durchgelesen und das glaub ich auch verstanden. aber allein wäre ich da wohl nicht drauf gekommen 🙂

    dafür schon mal vielen dank!

    dann allerdings noch eine letzte frage: das kommando, welches ich absetze, blockiert doofer weise. ich dachte eigentlich, dass ich es mit einem & am ende gelöst bekomme aber dies war ein irrglaube. muß ich nun noch screen dazwischen schalten? schon, oder? wenn ich einlogen will, starten und mich wieder verabschieden möchte, ohne dass das programm beendet wird. oder gibt es da was cooleres als screen??

    vielen dank auch hier schonmal im vorraus!

    sven_



  • Wenn du dich einloggen, das Programm starten, und dann ausloggen möchtest, ist screen wahrscheinlich eine der besten Lösungen.

    Die allersimpelste Lösung wäre, nohup vor den Aufruf zu schreiben. Das führt dazu, dass dein Programm auch nach dem Logout weiterläuft, aber du hast keine Möglichkeit mehr, den Output zu sehen, was du bei screen hast.



  • hallo christoph,

    deine Hilfe ist super! danke auf jeden fall schon mal. ein paar probleme hab ich aber noch. ich habe mit nohup und screen rumgespielt und bei beiden wohl noch einen gedankenfehler. das programm, welches ich auf dem anderen rechner starten möchte ist eine testanwendung, welche startet, sockets bindet und dann verkehr generiert bzw. einsammelt. informationen darüber gibt es auf dem monitor aus und schreibt sie in ein log. die log datei reicht mir eigentlich, daher würde nohup reichen. screen wäre eleganter, da ich irgendwann das prog ja auch wieder beenden will ;).

    wenn ich allerdings:

    ssh -l user HOST_NAME "nohup java /pfad_zum_test_prog/testprog"
    

    aufrufe, blockiert ssh ja dennoch auf dem programm. eigentlich will ich sowas wie &, also starten und vergessen (bzw. später nochmal vorbei schauen). aber so tut dies nicht. ich weiß, dass ich einen simplen anfängerfehler mache, sehe ihn aber leider nicht. hast du einen tip? das wäre cool.

    sven_



  • ach man, wie doof!

    ssh -l user HOST_NAME "nohup 'java /pfad_zum_test_prog/testprog' &"
    

    ist die lösung. komisch nur, dass ich ohne & am ende einen fehler bekomme im sinne datei nicht vorhanden? mh. 😕



  • und eins noch, wenn ich das ganz über pssh aufrufe, bekomme ich zwar ein success, aber top auf dem zielrechner zeigt keinen laufenden java prozess. mh 😞 gibts da nen trick zum debuggen? wie man sieht, woran es happert?



  • sven_ schrieb:

    und eins noch, wenn ich das ganz über pssh aufrufe, bekomme ich zwar ein success, aber top auf dem zielrechner zeigt keinen laufenden java prozess. mh 😞 gibts da nen trick zum debuggen? wie man sieht, woran es happert?

    top zeigt nur so viele Prozesse an, wie "auf den Bildschirm passen". Mit ps -a kannst du alle anzeigen lassen - vielleicht liegt es einfach nur daran?



  • Hallo zusammen,

    also ein wenig zweifle ich an mir selbst:

    So:

    ssh -l jgroups JGroups_1 -A -P "nohup java -jar jgroups/Text/dist/Test.jar"
    

    wird die Test-Anwenduung gestartet, ssh kommt aber nicht aus dem Skript zurück. Als ob also nohup nicht funktionieren würde.

    Das selbe Ergebnis ergibt:

    ssh -l jgroups JGroups_1 -A -P "nohup java -jar jgroups/Text/dist/Test.jar &"
    

    Irgdwas mach ich definitiv falsch. Aber was? Ich sehe es leider nicht. Ich habe es mit top und ps überprüft. Endweder rennt das Programm, blockiert aber ssh, oder wird nicht ausgeführt. ??? 😕


  • Mod

    http://en.wikipedia.org/wiki/Nohup#Overcoming_hanging

    P.S.: Die Ausgabedatei darf natürlich auch /dev/null sein.



  • mein held!

    ich hatte es gelesen, aber dachte, da mein prog eh schon nen log schreibt, ist dies nicht nötig! in kombination mit einem & am ende tut es dann auch.

    man, schwere geburt! vielen dank. dann probiere ich dass mal in mein skript einzubauen.

    gruß

    sven_



  • ok, ich brauch doch nochmal hilfe:

    wenn ich pssh so starte, klappt es:

    COMMAND='nohup java -jar jgroups/Test/dist/Test.jar > nohup.log 2>&1 < /dev/null &'
    

    aber so nicht:

    COMMAND='nohup "$@" > nohup.log 2>&1 < /dev/null &'
    

    Im log steht nohup missing operand. Ich vermute, dass ich beim Bash-Array was nicht verstanden habe und deswegen wohl das Kommando falsch übergebe, oder? Noch nen Tip? Wäre sehr cool.



  • aso,

    das command setz ich dann ins pssh_command ein, per ${COMMAND[@]} und das pss_command führe ich wie besprochen aus. offensichtlich löst er das array nicht auf, aber wie ist es richtig?



  • ach blind ist der mensch! ich habs. so ist richtig:

    COMMAND="nohup "$@" > nohup.log 2>&1 < /dev/null &"
    

    was so ein hochkommata so aus machen kann. 😉 vielen dank für eure hilfe!


Log in to reply