контекст вызова функции в zsh: эквивалент bash `caller`

В bash я могу написать:

caller 0 

и получить контекст звонящего :

  • Номер строки
  • функция
  • Название сценария

Это чрезвычайно полезно для отладки. Дано:

 yelp () { caller 0; } 

Затем я могу написать yelp чтобы увидеть, какие строки кода достигаются.

Я могу реализовать caller 0 в bash как:

 echo "${BASH_LINENO[0]} ${FUNCNAME[1]} ${BASH_SOURCE[1]" 

Как я могу получить такой же вывод, как caller 0 в zsh ?

    Я не думаю, что есть встроенный эквивалент команды , но можно использовать некоторую комбинацию этих четырех переменных из модуля zsh / Parameter :

     funcfiletrace 

    Этот массив содержит абсолютные номера строк и соответствующие имена файлов для точки, в которой была вызвана текущая функция, исходный файл или (если установлен EVAL_LINENO ) команда eval . Массив имеет ту же длину, что и funcsourcetrace и functrace , но отличается от funcsourcetrace тем, что строка и файл являются точкой вызова, а не точкой определения, и отличается от functrace тем, что все значения являются абсолютными номерами строк в файлах, а скорее чем относительно начала функции, если есть.

     funcsourcetrace 

    Этот массив содержит имена файлов и номера строк точек, в которых были определены функции, исходные файлы и (если установлен EVAL_LINENO ) выполняемые в данный момент команды eval . Номер строки – это строка, с которой начинаются « name () function name » или « name () ». В случае автозагрузки функции номер строки указывается как ноль. Формат каждого элемента – filename:lineno .

    Для функций, автоматически загружаемых из файла в собственном формате zsh, где в файле присутствует только тело функции, или для файлов, которые были выполнены source или ‘ . Встроенные данные трассировки отображаются в виде filename:0 , поскольку весь файл является определением. Имя исходного файла преобразуется в абсолютный путь, когда функция загружена или путь к ней разрешен иным образом.

    Большинство пользователей заинтересуются информацией в массиве funcfiletrace .

     funcstack 

    Этот массив содержит имена функций, исходных файлов и (если установлен EVAL_LINENO ) команды eval . в настоящее время выполняется. Первый элемент – это имя функции, использующей параметр.

    Стандартный массив оболочки zsh_eval_context может использоваться для определения типа конструкции оболочки, выполняемой на каждой глубине: обратите внимание, однако, что это в обратном порядке, с последним последним элементом, и это более подробно, например, включает в себя запись для верхнего уровня основной код оболочки выполняется либо в интерактивном режиме, либо из сценария, которого нет в $funcstack .

     functrace 

    Этот массив содержит имена и номера строк вызывающих абонентов, соответствующие выполняемым в данный момент функциям. Формат каждого элемента – name:lineno . Вызывающие также показаны для исходных файлов; вызывающий является точкой, где source или ‘ . Команда была выполнена.

    Сравнение:

    foo.bash :

     #! /bin/bash yelp() { caller 0 } foo () { yelp } foo 

    foo.zsh :

     #! /bin/zsh yelp() { print -l -- $funcfiletrace - $funcsourcetrace - $funcstack - $functrace } foo () { yelp } foo 

    Результаты, достижения:

     $ bash foo.bash 7 foo foo.bash $ zsh foo.zsh foo.zsh:7 foo.zsh:10 - foo.zsh:2 foo.zsh:6 - yelp foo - foo:1 foo.zsh:10 

    Итак, соответствующие значения находятся в ${funcfiletrace[1]} и ${funcstack[-1]} . Изменение yelp до:

     yelp() { print -- $funcfiletrace[1] $funcstack[-1] } 

    Выход:

     foo.zsh:7 foo 

    что довольно близко к Башу

     7 foo foo.bash 

    Основываясь на ответе Муру , я реализовал следующую функцию, которая работает в обоих {ba,z}sh :

     $ cat yelp #!/bin/zsh # Say the file, line number and optional message for debugging # Inspired by bash's `caller` builtin # Thanks to https://unix.stackexchange.com/a/453153/143394 function yelp () { # shellcheck disable=SC2154 # undeclared zsh variables in bash if [[ $BASH_VERSION ]]; then local file=${BASH_SOURCE[1]} func=${FUNCNAME[1]} line=${BASH_LINENO[0]} else # zsh emulate -L zsh # because we may be sourced by zsh `emulate bash -c` # $funcfiletrace has format: file:line local file=${funcfiletrace[1]%:*} line=${funcfiletrace[1]##*:} local func=${funcstack[2]} [[ $func =~ / ]] && func=source # $func may be filename. Use bash behaviour fi echo "${file##*/}:$func:$line $*" > /dev/tty } foo () { yelp; } yelp foo 

    Выход:

     $ ./yelp yelp::20 yelp:foo:19