web-dev-qa-db-ja.com

zshでの関数の呼び出しコンテキスト:bash `caller`と同等

Bashでは、私は書くことができます:

caller 0

caller コンテキストの受信:

  • 行番号
  • 関数
  • スクリプト名

これはデバッグに非常に役立ちます。与えられた:

Yelp () { caller 0; }

次に、Yelpと書いて、到達しているコード行を確認します。

bashcaller 0を次のように実装できます:

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

zshcaller 0と同じ出力を取得するにはどうすればよいですか?

8
Tom Hale

組み込みのコマンドに相当するものはないと思いますが、 zsh/Parameterモジュール からのこれら4つの変数のいくつかの組み合わせは利用される:

_funcfiletrace
_

この配列には、現在の関数、ソースファイル、または(_EVAL_LINENO_が設定されている場合)evalコマンドが呼び出されたポイントの絶対行番号と対応するファイル名が含まれます。配列の長さはfuncsourcetraceおよびfunctraceと同じですが、funcsourcetraceとは異なり、行とファイルは呼び出しのポイントであり、定義のポイントではありません。 functraceとは異なります。すべての値は、関数の開始からの相対位置ではなく、ファイル内の絶対行番号です。

_funcsourcetrace
_

この配列には、関数、ソースファイル、および(_EVAL_LINENO_が設定されている場合)現在実行されているevalコマンドが定義されたポイントのファイル名と行番号が含まれます。行番号は、「_function name_」または「name ()」が開始された行です。自動ロードされた関数の場合、行番号はゼロとして報告されます。各要素の形式は_filename:lineno_です。

関数の本体のみがファイルで発生するネイティブzsh形式のファイルから自動ロードされる関数、またはsourceまたは '_._'ビルトインによって実行されたファイルの場合、トレースファイル全体が定義であるため、情報は_filename:0_として表示されます。ソースファイル名は、関数のロード時に絶対パスに解決されるか、そうでなければ解決されます。

代わりに、ほとんどのユーザーはfuncfiletrace配列の情報に関心があります。

_funcstack
_

この配列には、関数の名前、ソースファイル、および(_EVAL_LINENO_が設定されている場合)evalコマンドが含まれます。現在実行中です。最初の要素は、パラメーターを使用する関数の名前です。

標準のShell配列_zsh_eval_context_を使用して、各深度で実行されるShell構成のタイプを決定できます。ただし、これは逆の順序で行われ、最新のアイテムが最後になり、より詳細になります。トップレベルのエントリを含む例。対話的にまたはスクリプトから実行されるメインのシェルコード。これは_$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
_

これはbashにかなり近い

_7 foo foo.bash
_
13
muru

muru's answer に基づいて、両方で機能する次の関数を実装しました{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
3
Tom Hale