Aufrufkontext der Funktion in zsh: Äquivalent zu bash `caller`

8

In Bash kann ich schreiben:

caller 0

und erhalten Sie den Anruferkontext :

  • Zeilennummer
  • Funktion
  • Skriptname

Dies ist äußerst nützlich für das Debuggen. Gegeben:

yelp () { caller 0; }

Ich kann dann schreiben yelp, um zu sehen, welche Codezeilen erreicht werden.

Ich kann implementieren caller 0in bash: als

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

Wie kann ich die gleiche Ausgabe wie caller 0in erhalten zsh?

Tom Hale
quelle

Antworten:

13

Ich glaube nicht , es gibt einen eingebauten Befehl gleichwertig, aber eine Kombination dieser vier Variablen aus dem zsh / Parameter - Modul kann verwendet werden:

funcfiletrace

Dieses Array enthält die absoluten Zeilennummern und die entsprechenden Dateinamen für den Punkt, an dem die aktuelle Funktion, die Quelldatei oder der Befehl (falls EVAL_LINENOfestgelegt) evalaufgerufen wurde. Das Array hat die gleiche Länge wie funcsourcetraceund functrace, unterscheidet sich jedoch darin, funcsourcetracedass die Zeile und die Datei der Aufrufpunkt und nicht der Definitionspunkt sind, und unterscheidet sich functracedarin, dass alle Werte in Dateien absolute Zeilennummern sind und nicht relativ zum Start einer Funktion, falls vorhanden.

funcsourcetrace

Dieses Array enthält die Dateinamen und Zeilennummern der Punkte, an denen die aktuell ausgeführten Funktionen, Quelldateien und (falls EVAL_LINENOfestgelegt) evalBefehle definiert wurden. Die Zeilennummer ist die Zeile, in der das ' function name' oder ' name ()' begonnen hat. Bei einer automatisch geladenen Funktion wird die Zeilennummer als Null gemeldet. Das Format jedes Elements ist filename:lineno.

Für Funktionen, die automatisch aus einer Datei im nativen zsh-Format geladen werden, wobei nur der Hauptteil der Funktion in der Datei vorkommt, oder für Dateien, die von den sourceoder ' .' eingebauten Dateien ausgeführt wurden , werden die Ablaufverfolgungsinformationen wie folgt angezeigt filename:0, da die gesamte Datei die ist Definition. Der Name der Quelldatei wird beim Laden der Funktion in einen absoluten Pfad aufgelöst oder der Pfad dazu wird anderweitig aufgelöst.

Die meisten Benutzer werden stattdessen an den Informationen im funcfiletraceArray interessiert sein .

funcstack

Dieses Array enthält die Namen der Funktionen, Quelldateien und (falls EVAL_LINENOfestgelegt) evalBefehle. wird gerade ausgeführt. Das erste Element ist der Name der Funktion, die den Parameter verwendet.

Das Standard-Shell-Array zsh_eval_contextkann verwendet werden, um den Typ des Shell-Konstrukts zu bestimmen, das in jeder Tiefe ausgeführt wird: Beachten Sie jedoch, dass das neueste Element zuletzt in umgekehrter Reihenfolge vorliegt und detaillierter ist, z. B. mit einem Eintrag für toplevel, wobei der Haupt-Shell-Code entweder interaktiv oder aus einem Skript ausgeführt wird, das in nicht vorhanden ist $funcstack.

functrace

Dieses Array enthält die Namen und Zeilennummern der Aufrufer, die den aktuell ausgeführten Funktionen entsprechen. Das Format jedes Elements ist name:lineno. Anrufer werden auch für Quelldateien angezeigt. Der Aufrufer ist der Punkt, an dem der Befehl sourceoder ' .' ausgeführt wurde.

Vergleichen:

foo.bash::

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

foo () {
    yelp
}

foo

foo.zsh::

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

foo () {
    yelp
}

foo

Die Ergebnisse:

$ 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

Die entsprechenden Werte sind also in ${funcfiletrace[1]}und ${funcstack[-1]}. Ändern yelpzu:

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

Die Ausgabe ist:

foo.zsh:7 foo

Das ist ziemlich nah an Bashs

7 foo foo.bash
muru
quelle
3

Basierend auf Murus Antwort habe ich die folgende Funktion implementiert, die in beiden funktioniert {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

Die Ausgabe ist:

$ ./yelp
yelp::20 
yelp:foo:19
Tom Hale
quelle