tcl8.6: что является встроенным эквивалентом 'atexit ()' в stdlib или "trap" ... "EXIT" в bash? - PullRequest
3 голосов
/ 06 апреля 2020

Я ищу встроенный или стандартный пакет, который обеспечивает функциональность, аналогичную или эквивалентную stdlib atexit() и bash 'trap "..." EXIT.

. Он должен перехватывать завершение из-за любых программ c способ завершения выполнения, включая все следующие:

  • естественно достигнутый конец выполнения сценария
  • явно вызвано exit
  • uncaught error

Ответы [ 2 ]

3 голосов
/ 06 апреля 2020

В большинстве случаев все, что вам нужно сделать, чтобы перехватить такие завершения, - это перехватить команду exit.

rename exit real_exit
proc exit args {
    puts "EXITING"; flush stdout; # Flush because I'm paranoid...
    tailcall real_exit {*}$args
}

Это, очевидно, сработает, если вы вызовете ее явно, но он также вызывается, если вы просто отбрасываете конец сценария, сигнализируете о конце файла в интерактивном сеансе или если ваш сценарий содержит ошибку позже и завершается сообщением об ошибке. Это связано с тем, что вызов API Tcl C, Tcl_Exit(), работает путем вызова exit и, если это не завершает процесс, непосредственно выполняет сам выход.

Будьте осторожны со сценариями выхода BTW ; ошибки в них сложнее отладить, чем обычно.


Случаи, когда это не работает? В основном там, где сам интерпретатор не может выполнять команды (возможно, из-за того, что он был удален из-под себя) или когда какой-то сигнал закрывает интерпретатор (например, SIGINT не обрабатывается по умолчанию по разным причинам).

0 голосов
/ 07 апреля 2020

Более-менее полное atexit на основе ответа @ Донала:

proc atexit { procbody } {
    if { [catch {set oldbody [info body exit]}] } {
        rename exit builtin_exit
        set oldbody { builtin_exit $returnCode }
    }
    proc exit { {returnCode 0} } [subst -nocommands {
        apply [list [list {returnCode 0}] [list $procbody]] \$returnCode
        tailcall apply [list [list {returnCode 0}] [list $oldbody]] \$returnCode
    }]
}

Пример кода для atexit-test.tcl:

#!/usr/bin/tclsh8.6

source atexit.tcl

atexit {
    puts "EXITING($returnCode)"; flush stdout; # Flush because I'm paranoid...
}

atexit {
    puts "done($returnCode)..."; flush stdout; # Flush because I'm paranoid...
}

atexit {
    puts "almost($returnCode)..."; flush stdout; # Flush because I'm paranoid...
}

{*}$argv

puts "fell through argv for implicit exit..."

... и терминальная сессия:

$ ./atexit-test.tcl 
fell through argv for implicit exit...
almost(0)...
done(0)...
EXITING(0)
$ ./atexit-test.tcl exit
almost(0)...
done(0)...
EXITING(0)
$ ./atexit-test.tcl exit 5
almost(5)...
done(5)...
EXITING(5)
$ ./atexit-test.tcl error "unhandled exception"
unhandled exception
    while executing
"{*}$argv"
    (file "./atexit-test.tcl" line 17)
almost(1)...
done(1)...
EXITING(1)
$ 
...