2009-03-26 3 views
17

Ein Skript ist falsch. Ich muss wissen, wer dieses Skript aufruft und wer das aufrufende Skript aufruft, und so weiter, nur indem ich das fehlerhafte Skript ändere.Trace der ausgeführten Programme von Bash-Skript

Dies ist ähnlich wie ein Stack-Trace, aber ich bin nicht interessiert an einer Aufrufliste von Funktionsaufrufen innerhalb eines einzigen Bash-Skript. Stattdessen brauche ich die Kette der ausgeführten Programme/Skripte, die von meinem Skript initiiert wird.

Antwort

6

Da Sie sagen, dass Sie das Skript bearbeiten können sich einfach ein setzen:

ps -ef >/tmp/bash_stack_trace.$$ 

darin, wo das Problem auftritt.

Dies erstellt eine Reihe von Dateien in Ihrem Verzeichnis tmp, die die gesamte Prozessliste zu dem Zeitpunkt zeigen, als es passiert ist.

Sie können dann herausfinden, welcher Prozess welchen anderen Prozess aufgerufen hat, indem Sie diese Ausgabe untersuchen. Dies kann entweder manuell erfolgen oder automatisiert mit so etwas wie awk, da die Ausgabe regulär ist -. Sie gerade jene PID und PPID Spalten verwenden, um die Beziehungen zwischen allen Prozessen zu arbeiten, der Sie interessiert

Sie über Sie müssen die Dateien im Auge behalten, da Sie pro Prozess einen erhalten, so dass sie möglicherweise verwaltet werden müssen. Da dies nur während des Debuggens durchgeführt werden sollte, wird die Zeile meistens auskommentiert (vorangestellt von), sodass die Dateien nicht erstellt werden.

zu reinigen, können Sie einfach tun:

rm /tmp/bash_stack_trace.* 
+0

dass, obwohl kein Stack-Trace ist. im besten Fall wäre es eine Exec-Spur. Aber dafür wären pstree -pal oder ps -ef --forest besser geeignet.
Es zeigt nicht den Funktionsaufruf * stack *, noch zeigt es die aktuelle Code-Datei und Zeile. Was normalerweise der springende Punkt einer Stack-Spur ist. – Evi1M4chine

+0

Ja, aber OP erklärte, dass sie nur wissen wollten, welche Skripte welche Skripte aufrufen, so dass die Details, welche Zeilen innerhalb der Skripte notwendig sind, nicht notwendig sind. Sobald Sie den exec-stack kennen, können Sie Debug-Anweisungen wie 'set -x' zu einzelnen Skripten hinzufügen, um eine feinkörnigere Ablaufverfolgung zu erhalten. – paxdiablo

+0

Ich halte dies nicht für eine Antwort auf die Frage, zumindest nicht im üblichen Sinne des Stack-Trace. – akostadinov

0

Hinzufügen pstree -p -u ` whoami ` >> Ausgabe in Ihrem Skript werden Sie wahrscheinlich die Informationen, die Sie benötigen.

6
~$ help caller 
caller: caller [EXPR] 
    Returns the context of the current subroutine call. 

    Without EXPR, returns "$line $filename". With EXPR, 
    returns "$line $subroutine $filename"; this extra information 
    can be used to provide a stack trace. 

    The value of EXPR indicates how many call frames to go back before the 
    current one; the top frame is frame 0. 
+1

Während dies und '-x' sind nützlich, es klingt, als würde es OP nicht helfen, da dies nur bei Funktionsaufrufen innerhalb desselben Skripts funktioniert. – ephemient

+0

Siehe Mircea Vutcovici Antwort oben. Dieser ist richtig, was ich brauchte: i = 0; während caller $ i; do ((i ++)); done –

-1

Sie könnten versuchen, so etwas wie

strace -f -e execve script.sh 
14

Ein einfaches Skript, das ich vor einigen Tagen schrieb ...

# FILE  : sctrace.sh 
# LICENSE : GPL v2.0 (only) 
# PURPOSE : print the recursive callers' list for a script 
#    (sort of a process backtrace) 
# USAGE  : [in a script] source sctrace.sh 
# 
# TESTED ON : 
# - Linux, x86 32-bit, Bash 3.2.39(1)-release 

# REFERENCES: 
# [1]: http://tldp.org/LDP/abs/html/internalvariables.html#PROCCID 
# [2]: http://linux.die.net/man/5/proc 
# [3]: http://linux.about.com/library/cmd/blcmdl1_tac.htm 

#! /bin/bash 

TRACE="" 
CP=$$ # PID of the script itself [1] 

while true # safe because "all starts with init..." 
do 
     CMDLINE=$(cat /proc/$CP/cmdline) 
     PP=$(grep PPid /proc/$CP/status | awk '{ print $2; }') # [2] 
     TRACE="$TRACE [$CP]:$CMDLINE\n" 
     if [ "$CP" == "1" ]; then # we reach 'init' [PID 1] => backtrace end 
       break 
     fi 
     CP=$PP 
done 
echo "Backtrace of '$0'" 
echo -en "$TRACE" | tac | grep -n ":" # using tac to "print in reverse" [3] 

... und einen einfachen Test.

test

Ich hoffe, dass es Ihnen gefällt.

+0

Dies funktioniert nicht auf Mac OS X. :(Nicht, dass Sie behauptet haben, dass es getan hat, aber nur für diejenigen, die eine Mac-Lösung suchen, das ist es nicht. –

+2

Ja, es Linux Einrichtungen verwendet (die Frage nach Linux war, nachdem alle), sorry ... –

+2

Dies endete als was für mich auf Mac OS X gearbeitet (und Linux): während Anrufer $ i ich tun = $ ((i + 1)) erledigt –

2

UPDATE: Der folgende Code sollte funktionieren. Jetzt habe ich eine newer answer mit einer neueren Code-Version gefunden, die eine Nachricht in den Stacktrace eingefügt erlaubt.

IIRC Ich konnte diese Antwort zu diesem Zeitpunkt nicht finden. Aber jetzt entschieden Code ist besser in Git gehalten, so dass neueste Version der oben genannten sollte in diesem gist sein.

Original-Code korrigierte Antwort unten:

Es gab eine andere Antwort zu diesem irgendwo, aber hier ist eine Funktion für das Erhalten Stack-Trace in dem Sinne, zum Beispiel in der Programmiersprache Java verwendet zu verwenden. Sie rufen die Funktion auf und setzen den Stack-Trace in die Variable $ STACK. Es zeigt die Codepunkte, die dazu führten, dass get_stack aufgerufen wird. Dies ist vor allem bei komplizierten Ausführungen nützlich, bei denen einzelne Shells mehrere Script-Snippets und Schachtelungen erzeugen.

function get_stack() { 
    STACK="" 
    # to avoid noise we start with 1 to skip get_stack caller 
    local i 
    local stack_size=${#FUNCNAME[@]} 
    for ((i=1; i<$stack_size ; i++)); do 
     local func="${FUNCNAME[$i]}" 
     [ x$func = x ] && func=MAIN 
     local linen="${BASH_LINENO[((i - 1))]}" 
     local src="${BASH_SOURCE[$i]}" 
     [ x"$src" = x ] && src=non_file_source 

     STACK+=$'\n'" "$func" "$src" "$linen 
    done 
} 
+0

Nützliches Bit für den Anfang, aber es hat Bugs. z.B. $ {# FUNCNAME [1]} ist die Länge des ersten Funktionsnamens, nicht die Anzahl der Einträge im FUNCNAME-Array, und das "i - 1" für lineno scheint falsch zu sein. – Eric

+0

@Eric, danke, ich erinnere mich, dass ich in meiner Produktionsversion einige Tune-ups gemacht habe, die ich nicht mehr finden kann, weil ich das Ding vor langer Zeit aufgehört habe. Und verpasste leider Code hier zu aktualisieren. Ich habe 'stack_size' festgelegt. LINENO sieht für mich von einem einfachen lokalen Test korrekt aus, lassen Sie mich wissen, wenn es für Sie nicht funktioniert. Ich erinnere mich auch an einen Unterschied mit 'stack_size', wenn ich ein Skript von einer Datei starte oder eine Funktion von einer interaktiven Shell aufruft. Ich denke, es kann automatisch gemacht werden, aber es ist jetzt keine Zeit, damit zu spielen. d. h. in interaktiven Fällen benötigen Sie 'stack_size + 1'. – akostadinov

+0

@Eric, danke an dich und ich könnte meine zwei Antworten zu diesem Thema verknüpfen. Siehe Update oben. Fühlen Sie sich willkommen bei der Zusammenarbeit am Kernpunkt. Ich denke, dass eine Verbesserung wäre, automatisch zu erkennen, wenn wir bei "stack_size" oder "stack_size + 1" aufhören müssen. – akostadinov

6

können Sie Bash verwenden Debugger http://bashdb.sourceforge.net/

Oder, wie in den vorherigen Kommentaren erwähnt, die caller bash eingebaut. Siehe: http://wiki.bash-hackers.org/commands/builtin/caller

i=0; while caller $i ;do ((i++)) ;done 

Ein anderer Weg, es zu tun ist, PS4 zu ändern und ermöglichen xtrace:

PS4='+$(date "+%F %T") ${FUNCNAME[0]}() $BASH_SOURCE:${BASH_LINENO[0]}+ ' 
set -o xtrace # Comment this line to disable tracing. 
+2

Ich mag besonders diesen: i = 0; während caller $ i; do ((i ++)); done –

+0

Die beste Antwort! –