Bagaimana saya bisa menemukan di mana fungsi bash tertentu didefinisikan?


Jawaban:


32

Aktifkan debugging. Dari manual Bash :

extdebug

Jika diset pada permintaan shell, atau dalam file startup shell, atur untuk menjalankan profil debugger sebelum shell dimulai, identik dengan --debuggeropsi. Jika disetel setelah pemanggilan, perilaku yang dimaksudkan untuk digunakan oleh para debugger diaktifkan:

  • The -Fpilihan untuk declarebuiltin (lihat Bash builtin ) menampilkan sumber nama file dan nomor baris yang sesuai untuk setiap nama fungsi yang disediakan sebagai argumen.

Contoh:

$ bash --debugger
$ declare -Ff quote
quote 143 /usr/share/bash-completion/bash_completion

Dan memang:

$ nl -ba /usr/share/bash-completion/bash_completion | sed -n 143,150p
   143  quote()
   144  {
   145      local quoted=${1//\'/\'\\\'\'}
   146      printf "'%s'" "$quoted"
   147  }
   148
   149  # @see _quote_readline_by_ref()
   150  quote_readline()

Ini adalah solusi yang bagus dan sederhana. Seperti misalnya Anda, itu bahkan tidak diperlukan untuk memulai shell baru: shopt -s extdebug; declare -Ff quote; shopt -u extdebug.
jarno

2
@ Jarno ah, yah, bertentangan dengan nama saya, saya menggunakan zsh. Itu sebabnya saya memulai shell baru. : D
bashity mcbashface

Apakah ada metode serupa untuk menemukan lokasi deklarasi alias ?
FedonKadifeli

@ fedon pendekatan saya yang berbelit-belit dan rumit di bawah ini seharusnya berhasil.
terdon

1
Anda bisa membuat ini fungsi seperti ini: find_function()( shopt -s extdebug; declare -F "$@"; ). Dengan parens pada badan fungsi, dieksekusi dalam subkulit dan perubahan ke shopts tidak memengaruhi pemanggil. Dan -fsepertinya tidak dibutuhkan.
wjandrea

15

Ini sebenarnya lebih rumit daripada yang muncul pada awalnya. File mana yang dibaca oleh shell Anda tergantung pada jenis shell yang sedang Anda jalankan. Apakah itu interaktif atau tidak, apakah itu login atau shell non-login dan apa kombinasi di atas. Untuk mencari semua file default yang dapat dibaca oleh shell yang berbeda, Anda dapat melakukannya (mengubah $functionNameke nama sebenarnya dari fungsi yang Anda cari):

grep "$functionName" ~/.bashrc ~/.profile ~/.bash_profile ~/.bash.login \
                     ~/.bash_aliases /etc/bash.bashrc /etc/profile \
                     /etc/profile.d/* /etc/environment 2> /dev/null

Jika itu tidak berhasil, Anda mungkin memanggil file non-default menggunakan .atau aliasnya source. Untuk menemukan kasus seperti itu, jalankan:

grep -P '(^|\s)(\.|source)\s+' ~/.bashrc ~/.profile ~/.bash_profile \
                               ~/.bash.login ~/.bash_aliases /etc/bash.bashrc \
                               /etc/profile /etc/profile.d/* /etc/environment 2> /dev/null

Itu mungkin perlu penjelasan. Yang -Pmemungkinkan Perl Compatible Regular Expressions (PCRE) yang memungkinkan kami menggunakan sintaks regex yang lebih menarik. Secara khusus:

  • (^|\s): cocok dengan awal suatu baris ( ^) atau spasi putih ( \s).
  • (\.|source)\s+: cocok dengan .karakter literal ( \.) atau kata source, tetapi hanya jika mereka diikuti oleh satu atau lebih karakter spasi putih.

Inilah yang memberi saya pada sistem saya:

$ grep -P '(^|\s)(\.|source)\s+' ~/.bashrc ~/.profile ~/.bash_profile \
>                                ~/.bash.login ~/.bash_aliases /etc/bash.bashrc \
>                                /etc/profile /etc/profile.d/* /etc/environment 2> /dev/null
/home/terdon/.bashrc:   . /etc/bashrc
/home/terdon/.bashrc:   . /etc/bash_completion
/home/terdon/.bashrc:. $HOME/scripts/git-prompt.sh
/home/terdon/.bashrc:#  echo -n "$n : "; grep "^CA"  $n |perl -e 'my ($a,$c)=0; while(<>){$c++;next if /cellular_component_unknown/; next if /biological_process/; $a++} print "$a Classes of $c annotated (" . $a*100/$c . ")\n"' 
/etc/bash.bashrc:[ -r /usr/share/bash-completion/bash_completion   ] && . /usr/share/bash-completion/bash_completion
/etc/profile:       test -r "$profile" && . "$profile"
/etc/profile:   . /etc/bash.bashrc
/etc/profile.d/locale.sh:    . "$XDG_CONFIG_HOME/locale.conf"
/etc/profile.d/locale.sh:    . "$HOME/.config/locale.conf"
/etc/profile.d/locale.sh:    . /etc/locale.conf
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch
/etc/profile.d/Z97-byobu.sh:        . /usr/bin/byobu-launch

Seperti yang Anda lihat, bagaimanapun, ini akan mencetak seluruh baris yang cocok. Yang benar-benar kami minati adalah daftar nama file yang dipanggil, bukan baris yang memanggil mereka. Anda bisa mendapatkannya dengan ini, lebih rumit, regex:

grep -hPo '(^|\s)(\.|source)\s+\K\S+' ~/.bashrc ~/.profile ~/.bash_profile \
                                      ~/.bash.login ~/.bash_aliases \
                                      /etc/bash.bashrc /etc/profile \
                                      /etc/profile.d/* /etc/environment 2> /dev/null

The -hbendera menekan pencetakan nama file di mana pertandingan ditemukan, yang greptidak secara default ketika diberitahu untuk mencari melalui beberapa file. The -oberarti "hanya mencetak bagian pencocokan dari garis". Hal-hal tambahan yang ditambahkan ke regex adalah:

  • \K: abaikan apa pun yang cocok dengan titik ini. Ini adalah trik PCRE yang memungkinkan Anda menggunakan regex kompleks untuk menemukan pasangan Anda, tetapi tidak menyertakan bagian yang cocok saat menggunakan -obendera grep .

Di sistem saya, perintah di atas akan kembali:

$ grep -hPo '(^|\s)(\.|source)\s+\K\S+' ~/.bashrc ~/.profile ~/.bash_profile \
>                                       ~/.bash.login ~/.bash_aliases \
>                                       /etc/bash.bashrc /etc/profile \
>                                       /etc/profile.d/* /etc/environment 2> /dev/null
/etc/bashrc
/etc/bash_completion
$HOME/scripts/git-prompt.sh
$a*100/$c
")\n"'
/usr/share/bash-completion/bash_completion
"$profile"
/etc/bash.bashrc
"$XDG_CONFIG_HOME/locale.conf"
"$HOME/.config/locale.conf"
/etc/locale.conf
/usr/bin/byobu-launch
/usr/bin/byobu-launch
/usr/bin/byobu-launch
/usr/bin/byobu-launch
/usr/bin/byobu-launch

Perhatikan bahwa saya kebetulan menggunakan .diikuti oleh ruang yang tidak digunakan untuk sumber tetapi itu karena saya memiliki alias yang memanggil bahasa lain, bukan bash. Itulah yang memberi keanehan $a*100/$cdan ")\n"'pada output di atas. Tapi itu bisa diabaikan.

Terakhir, inilah cara menyatukan semua itu dan mencari nama fungsi di semua file default dan semua file yang menjadi sumber file default Anda:

grep_function(){
  target="$@"
  files=( ~/.bashrc ~/.profile ~/.bash_profile ~/.bash.login 
         ~/.bash_aliases /etc/bash.bashrc /etc/profile 
         /etc/profile.d/* /etc/environment)
    while IFS= read -r file; do
      files+=( "$file" )
    done < <(grep -hPo '(^|\s)(\.|source)\s+\K\S+'  "${files[@]}" 2>/dev/null)
    for file in "${files[@]}"; do
      ## The tilde of ~/ can break this
      file=$(sed 's|~/|'"$HOME"'/|g' <<<"$file")
      if [[ -e $file ]]; then
        grep -H "$target" -- "$file"
      fi
    done
}

Tambahkan baris-baris itu ke Anda ~/.bashrcdan Anda kemudian dapat menjalankan (saya menggunakan fooBarsebagai contoh nama fungsi):

grep_function fooBar

Misalnya, jika saya memiliki baris ini di ~/.bashrc:

. ~/a

Dan file tersebut ~/aadalah:

$ cat ~/a
fooBar(){
  echo foo
}

Saya harus menemukannya dengan:

$ grep_function fooBar
/home/terdon/a:fooBar(){

Solusi Anda adalah contoh skrip pendidikan yang hebat, tetapi saya menemukan solusi bashity mcbashface lebih sederhana.
jarno

@ Jarno dan itu jauh, lebih sederhana! :)
terdon

Dukungan array+=
D. Ben Knoble

@ D.BenKnoble yang mereka lakukan? Maksud Anda selain array+="foo"menambahkan string fooke elemen pertama array?
terdon

1
@ D.BenKnoble terima kasih! Saya tidak tahu bash arrays mendukung notasi itu!
terdon

2

The biasa per-user dotfile bashmembaca adalah ~/.bashrc. Namun, mungkin sumber file lain sangat baik, saya misalnya ingin menjaga alias dan fungsi dalam file terpisah yang disebut ~/.bash_aliasesdan ~/.bash_functions, yang membuatnya lebih mudah menemukannya. Anda dapat mencari .bashrcuntuk sourceperintah dengan:

grep -E '(^\s*|\s)(\.|source)\s' /home/USERNAME/.bashrc

Setelah Anda memiliki daftar file yang dibuat pengguna, Anda dapat mencarinya dan pengguna .bashrcdengan satu greppanggilan, misalnya untuk fungsi foopengaturan saya:

grep foo /home/USERNAME/.bash{rc,_aliases,_functions}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.