2009-03-26 10 views
17

Uno script non funziona correttamente. Ho bisogno di sapere chi chiama quello script e chi chiama lo script chiamante, e così via, solo modificando lo script non valido.Traccia di programmi eseguiti chiamati da script bash

Questo è simile a uno stack-trace, ma io sono non interessato a uno stack di chiamate di chiamate di funzione all'interno di un singolo script bash. Invece, ho bisogno della catena di programmi/script eseguiti che è stata avviata dal mio script.

risposta

6

Dal momento che dici è possibile modificare lo script stesso, è sufficiente mettere un:

ps -ef >/tmp/bash_stack_trace.$$ 

in esso, in cui si verifica il problema.

Ciò creerà un numero di file nella directory tmp che mostra l'intero elenco di processi nel momento in cui si è verificato.

È quindi possibile calcolare quale processo ha chiamato quale altro processo esaminando questo output. Questo può essere fatto manualmente o automatizzata con qualcosa di simile awk, poiché l'uscita è regolare -. Basta usare quei PID e PPID colonne di capire le relazioni tra tutti i processi a cui siete interessati

Avrete è necessario tenere d'occhio i file, dal momento che ne avrai uno per processo in modo da poterli gestire. Poiché questo è qualcosa che dovrebbe essere fatto solo durante il debug, la maggior parte delle volte quella linea sarà commentata (preceduta da #), quindi i file non verranno creati.

Per pulirli, si può semplicemente fare:

rm /tmp/bash_stack_trace.* 
+0

Questa non è una traccia dello stack però. nel migliore dei casi sarebbe una traccia exec. Ma per quello pstree -pal o ps -ef --forest sarebbe più adatto.
Non mostra la funzione chiamata * stack *, né mostra il file e la riga del codice corrente. Di solito è il punto completo di una traccia dello stack. – Evi1M4chine

+0

Sì, ma OP ha dichiarato che desiderava solo sapere quali script chiamano quali script, quindi i dettagli su quali linee all'interno degli script non sono necessarie. Una volta che conosci lo stack exec, puoi iniziare ad aggiungere istruzioni di debug come 'set -x' ai singoli script per una traccia più fine. – paxdiablo

+0

Non considero questa una risposta alla domanda, almeno non nel senso comune della traccia dello stack. – akostadinov

0

aggiungendo pstree -p -u ` whoami ` >> uscita nello script sarà probabilmente ottenere le informazioni necessarie.

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

Mentre questo e '-x' sono utili, sembra che non aiuti OP, in quanto funziona solo sulle chiamate di funzione all'interno dello stesso script. – ephemient

+0

Vedere la risposta di Mircea Vutcovici sopra. Questo è quello che cercavo: i = 0; while caller $ i; do ((i ++)); fatto –

-1

Si potrebbe provare qualcosa di simile

strace -f -e execve script.sh 
14

Un semplice script che ho scritto qualche giorno fa ...

# 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] 

... e un semplice test.

test

Spero vi piaccia.

+0

Questo non funziona su Mac OS X. :(Non che tu affermi di averlo fatto, ma solo per coloro che sono alla ricerca di una soluzione Mac, non è così. –

+2

Sì, utilizza strutture di Linux (la domanda era su Linux, dopo tutto), mi dispiace ... –

+2

Questo ha finito per essere ciò che ha funzionato per me su Mac OS X (e Linux): mentre chiamante $ i fare i = $ ((i + 1)) done –

2

UPDATE: Il codice seguente dovrebbe funzionare. Ora ho trovato un newer answer con una versione di codice più recente che consente di inserire un messaggio nello stacktrace.

IIRC Non sono riuscito a trovare questa risposta per aggiornarlo anche in quel momento. Ma ora il codice deciso è meglio conservato in git, la versione più recente di cui sopra dovrebbe essere in questo gist.

risposta in codice corretto originale di seguito:

C'era un'altra risposta su questo da qualche parte, ma qui è una funzione da utilizzare per ottenere analisi dello stack nel senso usato ad esempio nel linguaggio di programmazione Java. Chiami la funzione e mette la traccia dello stack nella variabile $ STACK. Mostra i punti di codice che hanno portato a chiamare get_stack. Questo è in gran parte utile per complicate esecuzioni in cui una singola shell genera più frammenti di script e nidificazione.

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

Un po 'di codice utile per iniziare, ma ha dei bug. per esempio. $ {# FUNCNAME [1]} è la lunghezza del nome della prima funzione non il numero di voci nell'array FUNCNAME, e "i-1" per lineno sembra sbagliato. – Eric

+0

@Eric, grazie, ricordo di aver fatto alcune messe a punto nella mia versione di produzione che non riesco più a trovare perché ho smesso di usare la cosa tanto tempo fa. E sfortunatamente manca di aggiornare il codice qui. Ho corretto 'stack_size'. LINENO mi sembra corretto da un semplice test locale, fammi sapere se non funziona per te. Ricordo anche una differenza con 'stack_size' durante l'esecuzione di script dal file vs chiamando alcune funzioni dalla shell interattiva. Penso che possa essere fatto auto-indovinare ma non c'è tempo per giocarci subito. Ad esempio, in modalità interattiva, è necessario 'stack_size + 1'. – akostadinov

+0

@Eric, grazie a te e io potremmo eventualmente collegare le mie due risposte sull'argomento. Vedi l'aggiornamento sopra. Sentiti benvenuto a collaborare per l'essenziale. Penso che un miglioramento sarebbe quello di rilevare automaticamente quando dobbiamo fermarci su 'stack_size' o' stack_size + 1'. – akostadinov

6

È possibile utilizzare Bash Debugger http://bashdb.sourceforge.net/

Oppure, come accennato nei commenti precedenti, il caller bash built-in. Vedi: http://wiki.bash-hackers.org/commands/builtin/caller

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

Un altro modo per farlo è quello di cambiare la PS4 e attivare xtrace:

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

Mi piace particolarmente questo: i = 0; while caller $ i; do ((i ++)); done –

+0

La migliore risposta! –

Problemi correlati