konteks pemanggilan fungsi di zsh: setara dengan bash `caller`

8

Dalam bash, saya bisa menulis:

caller 0

dan menerima konteks pemanggil :

  • Nomor baris
  • Fungsi
  • Nama skrip

Ini sangat berguna untuk debugging. Diberikan:

yelp () { caller 0; }

Saya kemudian dapat menulis yelpuntuk melihat baris kode apa yang sedang dicapai.

Saya bisa menerapkan caller 0di bashsebagai:

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

Bagaimana saya bisa mendapatkan output yang sama seperti caller 0pada zsh?

Tom Hale
sumber

Jawaban:

14

Saya tidak berpikir ada yang setara dengan perintah builtin , tetapi beberapa kombinasi dari keempat variabel dari modul zsh / Parameter dapat digunakan:

funcfiletrace

Array ini berisi nomor baris absolut dan nama file yang sesuai untuk titik di mana fungsi saat ini, file sumber, atau (jika EVAL_LINENOdiatur) evalperintah dipanggil. Array memiliki panjang yang sama dengan funcsourcetracedan functrace, tetapi berbeda funcsourcetracedengan bahwa baris dan file adalah titik panggilan, bukan titik definisi, dan berbeda dari functracesemua nilai adalah nomor baris absolut dalam file, daripada relatif terhadap mulai dari suatu fungsi, jika ada.

funcsourcetrace

Array ini berisi nama file dan nomor baris dari titik-titik di mana fungsi, file sumber, dan (jika EVAL_LINENOdiatur) eval perintah yang sedang dieksekusi didefinisikan. Nomor baris adalah baris tempat ' function name' atau ' name ()' dimulai. Dalam kasus fungsi autoloaded, nomor baris dilaporkan sebagai nol. Format setiap elemen adalah filename:lineno.

Untuk fungsi yang dimuat secara otomatis dari file dalam format asli zsh, di mana hanya tubuh fungsi yang muncul dalam file, atau untuk file yang telah dieksekusi oleh sourceatau ' .' builtin, informasi jejak ditampilkan sebagai filename:0, karena seluruh file adalah definisi. Nama file sumber diselesaikan ke jalur absolut ketika fungsi dimuat atau jalur jika tidak diselesaikan.

Sebagian besar pengguna akan tertarik pada informasi dalam funcfiletracearray.

funcstack

Larik ini berisi nama fungsi, file sumber, dan perintah (jika EVAL_LINENOdisetel) eval. sedang dieksekusi. Elemen pertama adalah nama fungsi menggunakan parameter.

Array shell standar zsh_eval_contextdapat digunakan untuk menentukan jenis konstruksi shell yang dieksekusi di setiap kedalaman: perhatikan, namun, yang berada di urutan yang berlawanan, dengan item terbaru yang terakhir, dan lebih detail, misalnya termasuk entri untuk tingkat atas, kode shell utama dieksekusi baik secara interaktif atau dari skrip, yang tidak ada di $funcstack.

functrace

Array ini berisi nama dan nomor baris penelepon yang sesuai dengan fungsi yang sedang dieksekusi. Format setiap elemen adalah name:lineno. Penelepon juga ditampilkan untuk file bersumber; pemanggil adalah titik di mana perintah sourceatau ' .' dieksekusi.

Perbandingan:

foo.bash:

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

foo () {
    yelp
}

foo

foo.zsh:

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

foo () {
    yelp
}

foo

Hasil:

$ 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

Jadi, nilai yang sesuai berada di ${funcfiletrace[1]}dan ${funcstack[-1]}. Memodifikasi yelpke:

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

Outputnya adalah:

foo.zsh:7 foo

yang cukup dekat dengan bash's

7 foo foo.bash
muru
sumber
3

Berdasarkan jawaban muru , saya mengimplementasikan fungsi berikut yang berfungsi pada keduanya {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

Outputnya adalah:

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