Làm thế nào tôi có thể tìm thấy nơi hàm bash nhất định được xác định?


28

Có nhiều hàm có thể được sử dụng trong Bash shell. Các định nghĩa của chúng có thể được liệt kê theo set, nhưng làm thế nào để tìm trong các tệp mà một số hàm do người dùng xác định được xác định?


Câu trả lời:


32

Bật gỡ lỗi. Từ hướng dẫn Bash :

extdebug

Nếu được đặt ở lệnh gọi shell hoặc trong tệp khởi động shell, hãy sắp xếp để thực thi cấu hình trình gỡ lỗi trước khi shell bắt đầu, giống hệt với --debuggertùy chọn. Nếu được đặt sau khi gọi, hành vi được sử dụng bởi trình gỡ lỗi được bật:

  • Các -Ftùy chọn vào declareBUILTIN (xem Bash builtins ) hiển thị tên file nguồn và dòng số tương ứng với mỗi tên hàm được cung cấp như một cuộc tranh cãi.

Thí dụ:

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

Và thực sự:

$ 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()

Đây là một giải pháp tuyệt vời và đơn giản. Đối với ví dụ của bạn, thậm chí không cần thiết để bắt đầu một vỏ mới : shopt -s extdebug; declare -Ff quote; shopt -u extdebug.
jarno

2
@jarno ah, tốt, trái với tên của tôi, tôi sử dụng zsh. Đó là lý do tại sao tôi bắt đầu một lớp vỏ mới. : D
bashity mcbashface

Có một phương pháp tương tự để tìm vị trí khai báo bí danh ?
FedonKadifeli

@fedon cách tiếp cận phức tạp và phức tạp của tôi dưới đây sẽ hoạt động.
terdon

1
Bạn có thể làm cho nó một chức năng như thế này : find_function()( shopt -s extdebug; declare -F "$@"; ). Với các parens trên thân hàm, nó được thực thi trong một lớp con và thay đổi đối với các cửa hàng không ảnh hưởng đến người gọi. Và -fdường như không cần thiết.
wjandrea

15

Điều này thực sự phức tạp hơn lúc đầu. Những tập tin nào được đọc bởi shell của bạn phụ thuộc vào loại shell nào bạn đang chạy. Cho dù đó là tương tác hay không, cho dù đó là một đăng nhập hoặc một vỏ không đăng nhập và sự kết hợp của những điều trên. Để tìm kiếm trong tất cả các tệp mặc định có thể được đọc bởi các shell khác nhau, bạn có thể thực hiện (thay đổi $functionNametên thực của chức năng bạn đang tìm kiếm):

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

Nếu điều đó không làm việc, bạn có thể gọi một tệp không mặc định bằng cách sử dụng .hoặc bí danh của nó source. Để tìm trường hợp như vậy, chạy:

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

Điều đó có lẽ cần một số lời giải thích. Cho -Pphép biểu thức tương thích thường xuyên tương thích Perl (PCRE) cho phép chúng tôi sử dụng một số cú pháp regex fancier. Đặc biệt:

  • (^|\s): khớp với phần đầu của một dòng ( ^) hoặc khoảng trắng ( \s).
  • (\.|source)\s+: khớp với một ký .tự bằng chữ ( \.) hoặc từ source, nhưng chỉ khi chúng được theo sau bởi một hoặc nhiều ký tự khoảng trắng.

Đây là những gì mang lại cho tôi trên hệ thống của tôi:

$ 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

Như bạn có thể thấy, tuy nhiên, điều này sẽ in toàn bộ dòng phù hợp. Điều chúng tôi thực sự quan tâm là danh sách các tên tệp được gọi, không phải là dòng đang gọi chúng. Bạn có thể nhận được những thứ có điều này, phức tạp hơn, 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

Các -hcờ ngăn chặn việc in ấn của tên tập tin mà một trận đấu đã được tìm thấy, trong đó grepthực hiện theo mặc định khi nói đến tìm kiếm thông qua nhiều file. Có -onghĩa là "chỉ in phần phù hợp của dòng". Các công cụ bổ sung được thêm vào regex là:

  • \K: bỏ qua bất cứ điều gì phù hợp cho đến thời điểm này. Đây là một thủ thuật PCRE cho phép bạn sử dụng một biểu thức chính quy phức tạp để tìm trận đấu của mình nhưng không bao gồm phần phù hợp đó khi sử dụng -ocờ của grep .

Trên hệ thống của tôi, lệnh trên sẽ trả về:

$ 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

Lưu ý rằng tôi tình cờ sử dụng .một không gian không được sử dụng để tìm nguồn cung ứng nhưng đó là vì tôi có một bí danh đang gọi một ngôn ngữ khác, không phải là bash. Đó là những gì mang lại sự kỳ lạ $a*100/$c")\n"'ở đầu ra ở trên. Nhưng điều đó có thể bị bỏ qua.

Cuối cùng, đây là cách kết hợp tất cả những thứ đó lại với nhau và tìm kiếm một tên hàm trong tất cả các tệp mặc định tất cả các tệp mà các tệp mặc định của bạn đang tìm nguồn:

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
}

Thêm các dòng đó vào của bạn ~/.bashrcvà sau đó bạn có thể chạy (Tôi đang sử dụng fooBarlàm tên hàm ví dụ):

grep_function fooBar

Ví dụ: nếu tôi có dòng này trong ~/.bashrc:

. ~/a

Và tập tin ~/alà:

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

Tôi nên tìm nó với:

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

Giải pháp của bạn là một ví dụ về kịch bản giáo dục tuyệt vời, nhưng tôi thấy giải pháp của bashity mcbashface đơn giản hơn.
jarno

@jarno và nó thì đơn giản hơn nhiều! :)
terdon

Hỗ trợ mảng+=
D. Ben Knoble

@ D.BenKnoble họ làm gì? Ý bạn là ngoài array+="foo"việc nối thêm chuỗi foovào phần tử thứ 1 của mảng?
terdon

1
@ D.BenKnoble cảm ơn! Tôi không biết mảng bash hỗ trợ ký hiệu đó!
terdon

2

Các thông thường cho mỗi người dùng dotfile bashđọc là ~/.bashrc. Tuy nhiên, nó có thể cung cấp nguồn rất tốt cho các tệp khác, ví dụ như tôi muốn giữ bí danh và hàm trong các tệp riêng biệt được gọi ~/.bash_aliases~/.bash_functionsđiều này giúp việc tìm kiếm chúng dễ dàng hơn nhiều. Bạn có thể tìm kiếm .bashrccho sourcelệnh với:

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

Khi bạn có danh sách các tệp do người dùng tạo, bạn có thể tìm kiếm chúng và người dùng chỉ .bashrcbằng một grepcuộc gọi, ví dụ như chức năng foocho thiết lập của tôi:

grep foo /home/USERNAME/.bash{rc,_aliases,_functions}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.