Як я можу знайти, де визначена певна функція bash?


28

Є багато функцій, які можна використовувати в оболонці Bash. Їх визначення можна перерахувати set, але як знайти, у яких файлах визначені визначені користувачем функції?


1
Подивіться на: askubuntu.com/questions/463462/…
FedonKadifeli

Відповіді:


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

Це чудове і просте рішення. Що ж стосується вашого прикладу, це навіть не потрібно , щоб почати нову оболонку: shopt -s extdebug; declare -Ff quote; shopt -u extdebug.
jarno

2
@jarno ах, ну, всупереч моєму імені, я використовую zsh. Тому я запустив нову оболонку. : D
bashity mcbashface

Чи існує подібний метод пошуку місць декларації псевдонімів ?
ФедонКадіфелі

@fedon мій згорнутий і складний підхід повинен працювати.
тердон

1
Ви могли б зробити цю функцію наступним чином: find_function()( shopt -s extdebug; declare -F "$@"; ). З паренами на тілі функції воно виконується в підрозділі, і зміна на знімки не впливає на абонента. І -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's .

У моїй системі наведена вище команда повернеться:

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

Ваше рішення є чудовим прикладом навчальних сценаріїв, але я вважаю рішення bashity mcbashface простішим.
jarno

@jarno, і це набагато, набагато простіше! :)
тердон

Підтримка масивів+=
D. Ben Knoble

@ 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}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.