выбор между $ 0 и BASH_SOURCE

120

Как выбирать между "$0"и"${BASH_SOURCE[0]}"

Это описание из GNU мне не очень помогло.

    BASH_SOURCE

 An array variable whose members are the source filenames where the
 corresponding shell function names in the FUNCNAME array variable are
 defined. The shell function ${FUNCNAME[$i]} is defined in the file
 ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}
H2ONaCl
источник
BASH_SOURCEбыл добавлен в bash-3.0-alpha. У вас может не быть этого, в зависимости от вашего режима тестирования. Я обнаружил, что он отсутствует как в ранних версиях Solaris, так и в OS X. Также см. Return: может «возвращаться» только из функции или исходного сценария в U & L.SE.
jww 05

Ответы:

174

Примечание. Для решения, совместимого с POSIX, см. Этот ответ .

${BASH_SOURCE[0]}(или, проще говоря, $BASH_SOURCE[1] ) содержит (потенциально относительный) путь содержащего скрипта во всех сценариях вызова, особенно когда скрипт получен из источника , что неверно $0.

Более того, как указывает Чарльз Даффи , вызывающая сторона $0может установить произвольное значение.
С другой стороны, $BASH_SOURCE может быть пустым, если не используется именованный файл; например:
echo 'echo "[$BASH_SOURCE]"' | bash

Следующий пример иллюстрирует это:

Сценарий foo:

#!/bin/bash
echo "[$0] vs. [${BASH_SOURCE[0]}]"

$ bash ./foo
[./foo] vs. [./foo]

$ ./foo
[./foo] vs. [./foo]

$ . ./foo
[bash] vs. [./foo]

$0является частью спецификации оболочки POSIX, тогда как BASH_SOURCE, как следует из названия, зависит от Bash.


[1] Дополнительное чтение: ${BASH_SOURCE[0]}vs$BASH_SOURCE .:

Bash позволяет ссылаться на элемент 0из с массивом переменного , используя скалярное обозначение: вместо написания ${arr[0]}, вы можете написать $arr; другими словами: если вы ссылаетесь на переменную, как на скаляр , вы получаете элемент по индексу 0.

Использование этой функции скрывает тот факт, что $arrэто массив, поэтому популярный линтер шелл-кода shellcheck.net выдает следующее предупреждение (на момент написания этой статьи):

SC2128: Расширение массива без индекса дает только первый элемент.

Замечание: хотя это предупреждение полезно, оно может быть более точным, потому что вы не обязательно получите первый элемент: 0возвращается именно элемент по индексу , поэтому, если первый элемент имеет более высокий индекс, который возможно в Bash - вы получите пустую строку; попробуй 'a[1]='hi'; echo "$a"'.
(В отличие от этого zsh, когда ренегат, действительно ли возвращать первый элемент, независимо от его индекса).

Вы можете выбрать , чтобы отказаться от этой функции из - за своей неизвестности, но это работает предсказуемо и, прагматично говоря, вы будете редко, если когда - либо, необходимость доступа к индексам другой , чем 0из массива переменных ${BASH_SOURCE[@]}.

mklement0
источник
так $ BASH_SOURCE более общий и работает в большем количестве обстоятельств?
Александр Миллс
2
@AlexanderMills Да, если вы используете Bash, $BASH_SOURCEэто лучший выбор.
mklement0
19

Эти сценарии могут помочь проиллюстрировать. Внешний сценарий вызывает средний сценарий, который вызывает внутренний сценарий:

$ cat outer.sh
#!/usr/bin/env bash
./middle.sh
$ cat middle.sh
#!/usr/bin/env bash
./inner.sh
$ cat inner.sh
#!/usr/bin/env bash
echo "\$0 = '$0'"
echo "\${BASH_SOURCE[0]} = '${BASH_SOURCE[0]}'"
echo "\${BASH_SOURCE[1]} = '${BASH_SOURCE[1]}'"
echo "\${BASH_SOURCE[2]} = '${BASH_SOURCE[2]}'"
$ ./outer.sh
$0 = './inner.sh'
$BASH_SOURCE[0] = './inner.sh'
$BASH_SOURCE[1] = ''
$BASH_SOURCE[2] = ''

Однако, если мы изменим вызовы сценария на sourceоператоры:

$ cat outer.sh
#!/usr/bin/env bash
source ./middle.sh
$ cat middle.sh
#!/usr/bin/env bash
source ./inner.sh
$ cat inner.sh
#!/usr/bin/env bash
echo "\$0 = '$0'"
echo "\${BASH_SOURCE[0]} = '${BASH_SOURCE[0]}'"
echo "\${BASH_SOURCE[1]} = '${BASH_SOURCE[1]}'"
echo "\${BASH_SOURCE[2]} = '${BASH_SOURCE[2]}'"
$ ./outer.sh
$0 = './outer.sh'
$BASH_SOURCE[0] = './inner.sh'
$BASH_SOURCE[1] = './middle.sh'
$BASH_SOURCE[2] = './outer.sh'
Хью Уолтерс
источник
1

Для переносимости используйте, ${BASH_SOURCE[0]}когда он определен, и в $0противном случае. Это дает

${BASH_SOURCE[0]:-$0}

Примечательно, что, скажем, в zsh $ 0 действительно содержит правильный путь к файлу, даже если сценарий - sourced.

user7610
источник