Как я могу найти, где определена определенная функция bash?

28

Есть много функций, которые можно использовать в оболочке Bash. Их определения могут быть перечислены set, но как найти, в каких файлах определены определенные пользователем функции?

Ярно
источник
1
Посмотрите на: askubuntu.com/questions/463462/…
ФедонКадифели

Ответы:

32

Включите отладку. Из руководства Bash :

extdebug

Если установлено при вызове оболочки или в файле запуска оболочки, организуйте выполнение профиля отладчика до запуска оболочки, идентично --debuggerпараметру. Если установлено после вызова, поведение, предназначенное для использования отладчиками, включено:

  • -FВариант к declareвстроенной команде (см Bash Встроенные командам ) отображает имя исходного файла и номер строки , соответствующий каждое имя функции , подаваемое в качестве аргумента.

Пример:

$ 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()
bashity mcbashface
источник
Это отличное и простое решение. Что же касается вашего примера, это даже не требуется , чтобы начать новую оболочку: shopt -s extdebug; declare -Ff quote; shopt -u extdebug.
Ярно
2
@ Ярно, ну, вопреки моему имени, я использую Zsh. Вот почему я начал новую оболочку. : D
bashity mcbashface
Есть ли подобный метод, чтобы найти места объявления псевдонимов ?
ФедонКадифели
@ Fedon мой замысловатый и сложный подход ниже должен работать.
Тердон
1
Вы могли бы сделать эту функцию следующим образом: find_function()( shopt -s extdebug; declare -F "$@"; ). С паренами в теле функции, он выполняется в подоболочке, и изменение в 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 Compatible Regular Expressions (PCRE) , которые позволяют нам использовать некоторые любитель синтаксис регулярных выражений. В частности:

  • (^|\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

Как вы можете видеть, однако, это напечатает всю совпавшую линию. Что нас действительно интересует, так это список имен файлов, а не строка, которая их вызывает. Вы можете получить те с этим, более сложным, регулярным выражением:

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означает «печатать только на соответствующий участок линии». Дополнительный материал, добавленный в регулярное выражение:

  • \K: игнорировать что-либо подходящее до этого момента. Это трюк PCRE, который позволяет вам использовать сложное регулярное выражение для поиска соответствия, но не включать эту совпадающую часть при использовании -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

Обратите внимание, что у меня есть случай использования .пробела, который не используется для поиска источников, но это потому, что у меня есть псевдоним, который вызывает другой язык, а не bash. Это то, что дает странный $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(){
terdon
источник
Ваше решение является отличным учебным примером сценариев, но я считаю, что решение bashity mcbashface проще.
Ярно
@jarno, и это намного, намного проще! :)
тердон
Поддержка массивов+=
Д.Бен Нобл
@ D.BenKnoble они делают? Вы имеете в виду кроме array+="foo"добавления строки fooк 1-му элементу массива?
тердон
1
@ D.BenKnoble спасибо! Я не знал, что массивы bash поддерживают эту запись!
Тердон
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}
Десерт
источник