ฉันจะหาตำแหน่งที่กำหนดฟังก์ชัน bash ได้อย่างไร


28

มีฟังก์ชั่นมากมายที่สามารถใช้ใน Bash shell ได้ คำจำกัดความของพวกเขาสามารถแสดงรายการโดยsetแต่วิธีการค้นหาในไฟล์ที่ฟังก์ชั่นบางอย่างที่ผู้ใช้กำหนดจะถูกกำหนด?


คำตอบ:


32

เปิดการดีบัก จากคู่มือ Bash :

extdebug

หากตั้งค่าไว้ที่การเรียกใช้เชลล์หรือในไฟล์เริ่มทำงานเชลล์ให้จัดเรียงเพื่อดำเนินการโปรไฟล์ดีบักเกอร์ก่อนที่เชลล์จะเริ่มต้นซึ่งเหมือนกับ --debuggerตัวเลือก หากตั้งค่าหลังจากการเรียกใช้จะมีการเปิดใช้งานลักษณะการทำงานที่ debuggers ใช้:

  • -Fตัวเลือกไปdeclareในตัว (ดูทุบตี builtins ) แสดงชื่อแฟ้มแหล่งที่มาและจำนวนบรรทัดที่สอดคล้องกับชื่อฟังก์ชันแต่ละจำหน่ายเป็นอาร์กิวเมนต์

ตัวอย่าง:

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

และแน่นอน:

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

นี่เป็นทางออกที่ยอดเยี่ยมและเรียบง่าย shopt -s extdebug; declare -Ff quote; shopt -u extdebugในฐานะที่เป็นตัวอย่างของคุณมันไม่จำเป็นแม้จะเริ่มต้นเปลือกใหม่:
jarno

2
@jarno อ่าตรงกันข้ามกับชื่อของฉันฉันใช้ zsh นั่นเป็นเหตุผลที่ฉันเริ่มเปลือกใหม่ : D
bashity mcbashface

มีวิธีการคล้ายกันในการค้นหาสถานที่ประกาศนามแฝง
FedonKadifeli

@ เฟดกับวิธีการที่ซับซ้อนและซับซ้อนของฉันด้านล่างควรทำงาน
terdon

1
find_function()( shopt -s extdebug; declare -F "$@"; )คุณสามารถทำให้เรื่องนี้เป็นฟังก์ชั่นเช่นนี้ ด้วย parens บนเนื้อหาของฟังก์ชันมันจะถูกดำเนินการใน subshell และการเปลี่ยนแปลงใน shopts จะไม่ส่งผลกระทบต่อผู้โทร และ-fดูเหมือนจะไม่ต้องการ
wjandrea

15

อันที่จริงแล้วมันซับซ้อนกว่าที่ปรากฏในตอนแรก ไฟล์ใดที่เชลล์ของคุณอ่านขึ้นอยู่กับชนิดของเชลล์ที่คุณใช้งานอยู่ในปัจจุบัน ไม่ว่าจะเป็นแบบโต้ตอบหรือไม่ไม่ว่าจะเป็นเข้าสู่ระบบหรือเปลือกที่ไม่ใช่การเข้าสู่ระบบและสิ่งที่รวมกันของข้างต้น ในการค้นหาไฟล์เริ่มต้นทั้งหมดที่เชลล์สามารถอ่านได้คุณสามารถทำได้ (เปลี่ยน$functionNameเป็นชื่อจริงของฟังก์ชั่นที่คุณกำลังมองหา):

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

หากไม่ได้ทำงานคุณอาจจะเรียกไฟล์ที่ไม่ได้เริ่มต้นใช้หรือนามแฝงของมัน. sourceหากต้องการค้นหากรณีดังกล่าวให้เรียกใช้:

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

นั่นอาจต้องการคำอธิบายบางอย่าง -Pช่วยให้สามารถใช้งานร่วม Perl นิพจน์ปกติ (PCRE) ซึ่งให้เราใช้บางคนชอบเล่น regex ไวยากรณ์ โดยเฉพาะ:

  • (^|\s): จับคู่ส่วนต้นของบรรทัด ( ^) หรือช่องว่าง ( \s)
  • (\.|source)\s+: จับคู่ทั้งตัว.อักษร ( \.) หรือคำsourceแต่หากพวกเขาตามด้วยตัวอักษรช่องว่างอย่างน้อยหนึ่งตัว

นี่คือสิ่งที่ทำให้ฉันในระบบของฉัน:

$ 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

อย่างที่คุณเห็นอย่างไรก็ตามสิ่งนี้จะพิมพ์บรรทัดที่ตรงกันทั้งหมด สิ่งที่เราสนใจจริงๆคือรายชื่อไฟล์ที่เรียกว่าไม่ใช่บรรทัดที่เรียกพวกเขา คุณสามารถหาคนที่มีสิ่งนี้ซับซ้อนกว่า 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

-hธงยับยั้งการพิมพ์ชื่อไฟล์ที่การแข่งขันถูกพบซึ่งgrepไม่โดยค่าเริ่มต้นเมื่อบอกว่าจะค้นหาผ่านหลายไฟล์ ความ-oหมาย "พิมพ์เฉพาะส่วนที่ตรงกันของบรรทัด" สิ่งพิเศษที่เพิ่มเข้าไปใน regex คือ:

  • \K: ละเว้นสิ่งที่ตรงกับจุดนี้ นี่เป็นเคล็ดลับ PCRE ที่ให้คุณใช้ regex ที่ซับซ้อนเพื่อค้นหาคู่ของคุณ แต่ไม่รวมส่วนที่ตรงกันเมื่อใช้การ-oตั้งค่าสถานะgrep

ในระบบของฉันคำสั่งดังกล่าวจะกลับมา:

$ 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

โปรดทราบว่าฉันบังเอิญมีการใช้.พื้นที่ที่ไม่ได้ใช้ในการจัดหา แต่นั่นเป็นเพราะฉันมีนามแฝงที่เรียกภาษาอื่นไม่ใช่ทุบตี นั่นคือสิ่งที่ให้ความแปลก$a*100/$cและ")\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
}

เพิ่มบรรทัดเหล่านั้นในของคุณ~/.bashrcและคุณสามารถเรียกใช้ (ฉันใช้fooBarเป็นชื่อฟังก์ชั่นตัวอย่าง):

grep_function fooBar

ตัวอย่างเช่นหากฉันมีบรรทัดนี้ใน~/.bashrc:

. ~/a

และไฟล์~/aคือ:

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

ฉันควรหามันด้วย:

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

วิธีการแก้ปัญหาของคุณเป็นตัวอย่างสคริปต์การศึกษาที่ยอดเยี่ยม แต่ฉันพบว่าโซลูชันของ bashity mcbashface นั้นเรียบง่ายขึ้น
jarno

@jarno และมันก็ง่ายกว่ามาก! :)
terdon

การสนับสนุนอาร์เรย์+=
D. Ben Knoble

@ D.BenKnoble พวกเขาทำอะไร คุณหมายถึงนอกเหนือจากการarray+="foo"ผนวกสตริงfooไปยังองค์ประกอบที่ 1 ของอาร์เรย์?
terdon

1
@ D.BenKnoble ขอบคุณ! ฉันไม่รู้อาร์เรย์ bash ที่รองรับสัญกรณ์นั้น!
terdon

2

ปกติต่อผู้ใช้ dotfile อ่านคือbash ~/.bashrcอย่างไรก็ตามมันอาจเป็นแหล่งไฟล์อื่น ๆ ได้อย่างดีเช่นฉันต้องการเก็บนามแฝงและฟังก์ชั่นในไฟล์แยกต่างหากที่เรียกว่า~/.bash_aliasesและ~/.bash_functionsซึ่งทำให้การค้นหาง่ายขึ้นมาก คุณสามารถค้นหา.bashrcสำหรับsourceคำสั่งด้วย:

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

เมื่อคุณมีรายการไฟล์ที่ผู้ใช้สร้างขึ้นคุณสามารถค้นหาไฟล์และผู้ใช้.bashrcด้วยการgrepโทรเพียงครั้งเดียวเช่นฟังก์ชั่นfooสำหรับการตั้งค่าของฉัน:

grep foo /home/USERNAME/.bash{rc,_aliases,_functions}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.