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


8

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

caller 0

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

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

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

yelp () { caller 0; }

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

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

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

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

Ответы:


14

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

funcfiletrace

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

funcsourcetrace

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

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

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

funcstack

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

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

functrace

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

Сравнение:

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

3

Основываясь на ответе Муру , я реализовал следующую функцию, которая работает в обоих случаях {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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.