определение пути к полученному сценарию оболочки

Во всех отношениях, типичным современным дистрибутивом Linux (Ubuntu, Debian, Red Hat, Fedora, Slackware, и т.д.) является Unix, но строго говоря, никакая система не может утверждать, что была Unix без того, чтобы быть сертифицированным, таким образом, вместо этого люди говорят, что они подобны Unix. Они вдохновлены Unix и продолжают его культуру.

Это также относится к системам BSD.

Mac OS X является сертифицированный Unix, таким образом, это - Unix и на имя и действительно. (и это на самом деле основано на BSD).

Нужно отметить, что, так как сам Linux является просто ядром, это может использоваться для создания неподобных Unix систем (таких как Android).

84
08.12.2010, 18:32
14 ответов

В tcsh, $_ в начале сценария будет содержать местоположение, если файл был получен и $0 содержит его, если это было выполнено.

#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
    echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
    echo "run $0"
endif

В Bash:

#!/bin/bash
[[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"
67
27.01.2020, 19:30
  • 1
    работы, я просто имел случай для использования этого в tcsh и заметил, что он не работает без хижины. Кажется немного нечетным, чтобы поведение изменилось, если Вы просто получаете его, не выполняя его... –  Cascabel 20.04.2011, 19:28
  • 2
    tcsh версия также, кажется, не работает, если сценарий получен нев интерактивном режиме (например, от cshrc). Я, может казаться, не нахожу способ получить информацию в этом случае. Какие-либо мысли? –  Cascabel 09.05.2011, 21:04
  • 3
    Определение источника его работает на меня без хижины. > tcsh --version\n tcsh 6.14.00 (Astron) 2005-03-25 (i486-intel-linux) options wide,nls,dl,al,kan,rh,nd,color,filec. До определения источника его нев интерактивном режиме, исходный файл включен в родительский файл, как будто это была на самом деле часть его (неразличимо так), как Вы упоминаете в своем исходном вопросе. Я думаю, что Ваше позиционное обходное решение параметра является, вероятно, лучшим подходом. Однако обычный вопрос, "почему делают Вы хотите сделать, это" и обычный ответ на ответ, "не делают, это - делает это вместо этого", где "это" должно часто хранить... –  Paused until further notice. 10.05.2011, 18:33
  • 4
    @clacke: Я нахожу, что во всех версиях Bash, который я протестировал от 2.05b до 4.2.37, включая 4.1.9, это . и source работавший тождественно в этом отношении. Отметьте это $_ должен быть получен доступ в первом операторе в файле, иначе он будет содержать последний аргумент предыдущей команды. Мне нравится включать хижину для моей собственной ссылки, таким образом, я знаю то, что окружает, она, как предполагается, для и для редактора, таким образом, она использует подсветку синтаксиса. –  Paused until further notice. 04.03.2013, 13:51
  • 5
    Ха-ха. Очевидно, я тестировал первым выполнением source, затем выполнение .. Я приношу извинения за то, что был некомпетентен. Они действительно идентичны. Так или иначе, $BASH_SOURCE работы. –  clacke 05.03.2013, 09:35

Я думаю, что Вы могли использовать $BASH_SOURCE переменная. Это возвращает путь, который выполнялся:

pbm@tauri ~ $ /home/pbm/a.sh 
/home/pbm/a.sh
pbm@tauri ~ $ ./a.sh
./a.sh
pbm@tauri ~ $ source /home/pbm/a.sh 
/home/pbm/a.sh
pbm@tauri ~ $ source ./a.sh
./a.sh

Таким образом на следующем шаге мы должны проверить, относителен ли путь или нет. Если это не относительно, все в порядке. Если это, мы могли бы проверить путь с pwd, конкатенируйте с / и $BASH_SOURCE.

32
27.01.2020, 19:30
  • 1
    И отметьте это source поиски в $PATH если имя не содержит a /. Поисковый порядок зависит от опций оболочки, обратитесь к руководству для деталей. –  Gilles 'SO- stop being evil' 08.12.2010, 21:09
  • 2
    Так, что-то как mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)" работал бы? –  Kevin Cantu 08.12.2010, 21:36
  • 3
    Спасибо, быстрый и полезный ответ. Dennis выигрывает зеленую галочку для предоставления ответа tcsh также. @Gilles: Право, я действительно находил это в документации. К счастью, для моего варианта использования я почти наверняка не должен волноваться об этом. –  Cascabel 08.12.2010, 21:46

Это решение относится только к башу, а не к tcsh. Обратите внимание, что обычно поставляемый ответ ${BASH_SOURCE[0]} не будет работать, если попытаться найти путь изнутри функции.

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

echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

Если вы хотите следовать по симлинкам, используйте readlink на пути, который вы получаете выше, рекурсивно или не рекурсивно.

Вот скрипт, чтобы попробовать его и сравнить с другими предложенными решениями. Вызовите его как source test1/test2/test_script.sh или bash test1/test2/test_script.sh.

#
# Location: test1/test2/test_script.sh
#
echo $0
echo $_
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_dir="$(dirname "${cur_file}")"
source "${cur_dir}/func_def.sh"

function test_within_func_inside {
    echo ${BASH_SOURCE}
    echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}

echo "Testing within function inside"
test_within_func_inside

echo "Testing within function outside"
test_within_func_outside

#
# Location: test1/test2/func_def.sh
#
function test_within_func_outside {
    echo ${BASH_SOURCE}
    echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}

Причина, по которой работает один лайнер, объясняется использованием переменной окружения BASH_SOURCE и ее ассоциированной FUNCNAME.

BASH_SOURCE

Переменная массива, членами которой являются имена исходных файлов, в которых определены соответствующие имена функций оболочки в переменной массива FUNCNAME. Функция оболочки ${FUNCNAME[$i]} определена в файле ${BASH_SOURCE[$i]} и вызывается из ${BASH_SOURCE[$i+1]}.

FUNCNAME

- переменная массива, содержащая имена всех функций оболочки, находящихся в стеке вызовов выполнения. Элемент с индексом 0 является именем любой выполняемой в данный момент функции оболочки. Самый нижний элемент (элемент с наибольшим индексом) является "главным". Эта переменная существует только при выполнении функции оболочки. Присвоения FUNCNAME не имеют никакого эффекта и возвращают статус ошибки. Если FUNCNAME не установлено, то она теряет свои специальные свойства, даже если впоследствии сбрасывается.

Эту переменную можно использовать с BASH_LINENO и BASH_SOURCE. Каждый элемент FUNCNAME имеет соответствующие элементы в BASH_LINENO и BASH_SOURCE для описания стека вызовов. Например, из файла ${BASH_SOURCE[$i+1]} по номеру строки ${BASH_LINENO[$i]} был вызван ${FUNCNAME[$i]}. Встроенный вызывающий абонент отображает текущий стек вызовов, используя эту информацию.

[Источник: Бэш-руководство]

19
27.01.2020, 19:30

Для тщательности и пользы искателей, вот то, что они делают... Это - общественная Wiki, поэтому не стесняйтесь добавлять эквиваленты другой оболочки (очевидно, $BASH_SOURCE будет отличаться).

test.sh:

#! /bin/sh
called=$_
echo $called
echo $_
echo $0
echo $BASH_SOURCE

test2.sh:

#! /bin/sh
source ./test.sh

Bash:

$./test2.sh
./test2.sh
./test2.sh
./test2.sh
./test.sh
$ sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
./test.sh

Тире

$./test2.sh
./test2.sh
./test2.sh
./test2.sh

$/bin/sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh

$

Zsh

$ ./test2.sh
./test.sh
./test.sh
./test.sh

$ zsh test.sh

echo
test.sh

$
18
27.01.2020, 19:30
  • 1
    я не понимаю: почему called=$_; echo $called; echo $_? Не будет эта печать $_ дважды? –  Ciro Santilli 新疆改造中心法轮功六四事件 14.09.2014, 10:17
  • 2
    @CiroSantilli: Не всегда, прочитайте руководство Bash по $_ специальный параметр: "При запуске оболочки набор к абсолютному пути раньше вызывал сценарий оболочки или сценарий оболочки, выполняемый, как передано в среде или списке аргументов. Впоследствии, расширяется до последнего аргумента предыдущей команде, после расширения. Также набор к полному пути раньше вызывал каждую команду, выполняемую и помещенную в среду, экспортируемую в ту команду. При проверке почты этот параметр содержит название почтового файла". –  Adam Rosenfield 17.09.2014, 22:14

На самом деле, "dirname 0$" получит Вас путь к сценарию, но необходимо интерпретировать его немного:

$ cat bash0
#!/bin/bash
echo \$0=$0
dirname $0
$ bash0    # "." appears in PATH right now.
$0=./bash0
.
$ ./bash0
$0=./bash0
.
$ $PWD/bash0
$0=/home/00/bediger/src/ksh/bash0
/home/00/bediger/src/ksh
$ $PWD/../ksh/bash0
$0=/home/00/bediger/src/ksh/../ksh/bash0
/home/00/bediger/src/ksh/../ksh
$ ../ksh/bash0
$0=../ksh/bash0
../ksh

Необходимо подготовиться обрабатывать "." как имя каталога при некоторых общих обстоятельствах. Я экспериментировал бы немного, поскольку я помню dirname встроенное к ksh выполнение вещей немного по-другому, когда"." появляется в ПУТИ.

-3
27.01.2020, 19:30
  • 1
    Это - полученный сценарий, не выполняемый сценарий. $0 просто содержит "удар" для интерактивной оболочки, и это - весь полученный сценарий, видит. –  Cascabel 08.12.2010, 18:32

Для оболочки удара я нашел ответ @Dennis Williamson самым полезным, но это не работало в случае sudo. Это делает:

if ( [[ $_ != $0 ]] && [[ $_ != $SHELL ]] ); then
    echo "I'm being sourced!"
    exit 1
fi
0
27.01.2020, 19:30

У меня это сработало в bash, dash, ksh и zsh:

if test -n "$BASH" ; then script=$BASH_SOURCE
elif test -n "$TMOUT"; then script=${.sh.file}
elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
else script=$0
fi

echo $script

Вывод для этих оболочек:

BASH source: ./myscript
ZSH source: ./myscript
KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
BASH: ./myscript
ZSH: ./myscript
KSH: /home/pbrannan/git/theme/src/theme/web/myscript
DASH: ./myscript

Я попытался заставить его работать для csh / tcsh, но это слишком сложно; Я придерживаюсь POSIX.

18
20.08.2021, 13:38

Меня немного смутил вики-ответ сообщества (от Шона Дж. Гоффа ), поэтому я написал сценарий, чтобы разобраться во всем. О $_я нашел это:Использование _в качестве переменной среды, переданной команде . Это переменная среды, поэтому ее значение легко проверить неправильно.

Ниже приведен скрипт, затем его вывод. Они также находятся в этой сути .

тест -оболочка -по умолчанию -variable.sh

#!/bin/bash

# test-shell-default-variables.sh

# Usage examples (you might want to `sudo apt install zsh ksh`):
#
# ./test-shell-default-variables.sh dash bash
# ./test-shell-default-variables.sh dash bash zsh ksh
# ./test-shell-default-variables.sh dash bash zsh ksh | less -R

# `-R` in `less -R` to have less pass escape sequences directly to the terminal
# so we have colors.


# The "invoking with name `sh`" tests are commented because for every shell I
# tested (dash, bash, zsh and ksh), the output was the same as that of dash.

# The `test_expression` function also work with expansion changes. You can try
# lines like `test_expression '{BASH_SOURCE:-$0}'`.

echolor() {
    echo -e "\e[1;36m$@\e[0m"
}

tell_file() {
    echo File \`"$1"\` is:
    echo \`\`\`
    cat "$1"
    echo \`\`\`
    echo
}

SHELL_ARRAY=("$@")

test_command() {
    for shell in "${SHELL_ARRAY[@]}"
    do
        prepare "$shell"
        cmd="$(eval echo $1)"
        # echo "cmd: $cmd"
        printf '%-4s: ' "$shell"
        { env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
        teardown
    done
    echo
}

prepare () {
    shell="$1"
    PATH="$PWD/$shell/sh:$PATH"
}

teardown() {
    PATH="${PATH#*:}"
}


###
### prepare
###
for shell in "${SHELL_ARRAY[@]}"
do
    mkdir "$shell"
    ln -sT "/bin/$shell" "$shell/sh"
done

echo > printer.sh
echo '../printer.sh' > sourcer.sh
rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"

tell_file sourcer.sh

###
### run
###
test_expression() {
    local expr="$1"

    # prepare
    echo "echo $expr" > printer.sh
    tell_file printer.sh

    # run
    cmd='$shell./printer.sh'
    echolor "\`$cmd\` (simple invocation) ($expr):"
    test_command "$cmd"

    # cmd='sh./printer.sh'
    # echolor "\`$cmd\` (when executable name is \`sh\`) ($expr):"
    # test_command "$cmd"

    cmd='$shell./sourcer.sh'
    echolor "\`$cmd\` (via sourcing) ($expr):"
    test_command "$cmd"

    # cmd='sh./sourcer.sh'
    # echolor "\`$cmd\` (via sourcing, when name is \`sh\`) ($expr):"
    # test_command "$cmd"

    cmd='$shell./linked.sh'
    echolor "\`$cmd\` (via symlink) ($expr):"
    test_command "$cmd"

    # cmd='sh./linked.sh'
    # echolor "\`$cmd\` (via symlink, when name is \`sh\`) ($expr):"
    # test_command "$cmd"

    echolor "------------------------------------------"
    echo
}

test_expression '$BASH_SOURCE'
test_expression '$0'
test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
test_expression '$_'

###
### teardown
###
for shell in "${SHELL_ARRAY[@]}"
do
    rm "$shell/sh"
    rm -d "$shell"
done

rm sourcer.sh
rm linked.sh
rm printer.sh

Выход./test-shell-default-variables.sh {da,ba,z,k}sh

File `sourcer.sh` is:
```
../printer.sh
```

File `printer.sh` is:
```
echo $BASH_SOURCE
```

`$shell./printer.sh` (simple invocation) ($BASH_SOURCE):
dash: 
bash:./printer.sh
zsh : 
ksh : 

`$shell./sourcer.sh` (via sourcing) ($BASH_SOURCE):
dash: 
bash:./printer.sh
zsh : 
ksh : 

`$shell./linked.sh` (via symlink) ($BASH_SOURCE):
dash: 
bash:./linked.sh
zsh : 
ksh : 

------------------------------------------

File `printer.sh` is:
```
echo $0
```

`$shell./printer.sh` (simple invocation) ($0):
dash:./printer.sh
bash:./printer.sh
zsh :./printer.sh
ksh :./printer.sh

`$shell./sourcer.sh` (via sourcing) ($0):
dash:./sourcer.sh
bash:./sourcer.sh
zsh :./printer.sh
ksh :./sourcer.sh

`$shell./linked.sh` (via symlink) ($0):
dash:./linked.sh
bash:./linked.sh
zsh :./linked.sh
ksh :./linked.sh

------------------------------------------

File `printer.sh` is:
```
echo $(/bin/true x y; true a b c; echo $_)
```

`$shell./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
dash: 
bash: c
zsh : c
ksh : 

`$shell./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
dash: 
bash: c
zsh : c
ksh : 

`$shell./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
dash: 
bash: c
zsh : c
ksh : 

------------------------------------------

File `printer.sh` is:
```
echo $_
```

`$shell./printer.sh` (simple invocation) ($_):
dash: 
bash: bash
zsh : 
ksh : 

`$shell./sourcer.sh` (via sourcing) ($_):
dash: 
bash: bash
zsh :./printer.sh
ksh : 

`$shell./linked.sh` (via symlink) ($_):
dash: 
bash: bash
zsh : 
ksh : 

------------------------------------------

Что мы узнали?

$BASH_SOURCE

  • $BASH_SOURCEработает в bash и только в bash.
  • Единственная разница с $0заключается в том, что текущий файл был получен другой файл. В таком случае,$BASH_PROFILEсодержит имя исходный файл, а не исходный файл.

$0

  • В zsh $0имеет то же значение, что и $BASH_SOURCEв bash.

$_

  • $_остается нетронутым тире и ksh.
  • В bash и zsh $_затухает до последнего аргумента последнего вызова.
  • bash инициализирует $_как "bash".
  • zsh оставляет $_нетронутым. (при поиске это `просто результат правила «последнего аргумента» ).

Симлинки

  • Когда скрипт вызывается через символическую ссылку, ни одна переменная не содержит ссылка на место назначения ссылки, только ее имя.

кш

  • Что касается этих тестов, то ksh ведет себя как dash.

ш

  • Когда bash или zsh вызываются через символическую ссылку с именем sh, в отношении этих тестов они ведут себя как dash.
3
20.08.2021, 13:38

tl;drscript=$(readlink -e -- "${BASH_SOURCE}")(для bash очевидно)


$BASH_SOURCEтестовые примеры

данный файл/tmp/source1.sh

echo '$BASH_SOURCE '"(${BASH_SOURCE})"
echo 'readlink -e $BASH_SOURCE'\
     "($(readlink -e -- "${BASH_SOURCE}"))"

sourceфайл разными способами

sourceиз/tmp

$> cd /tmp

$> source source1.sh
$BASH_SOURCE (source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$> source./source1.sh
$BASH_SOURCE (./source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$> source /tmp/source1.sh
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

sourceиз/

cd /
$> source /tmp/source1.sh
$0 (bash)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

sourceиз разных относительных путей /tmp/aи/var

$> cd /tmp/a

$> source../source1.sh
$BASH_SOURCE (../source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$> cd /var

$> source../tmp/source1.sh
$BASH_SOURCE (../tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

относительно$0

во всех случаях, если в скрипт добавлена ​​команда

echo '$0 '"(${0})"

затем sourceскрипт всегда печатается

$0 (bash)

однако , если скрипт был запустить , например.

$> bash /tmp/source1.sh

, тогда $0будет строковым значением /tmp/source1.sh.

$0 (/tmp/source1.sh)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
0
20.08.2021, 13:38

этот ответ описывает, как lsofи немного магии grep — единственное, что, кажется, имеет шанс работать с вложенными исходными файлами под tcsh:

/usr/sbin/lsof +p $$ | grep -oE /.\*source_me.tcsh
0
20.08.2021, 13:38
wdir="$PWD"; [ "$PWD" = "/" ] && wdir=""
case "$0" in
  /*) scriptdir="${0%/*}";;
  *) scriptdir="$wdir/${0#./}"; scriptdir="${scriptdir%/*}";;
esac
echo "$scriptdir"

Возможно, это не будет работать с символическими ссылками или исходными файлами, но будет работать с обычными файлами. Взято за ссылку отсюда. @кенорб Нет имени каталога, ссылка для чтения, BASH _SOURCE.

-2
20.08.2021, 13:38

Самая сложная часть заключается в том, чтобы найти текущий исходный файл для оболочки dash, используемой в качестве замены sh в Ubuntu. Следующий фрагмент кода можно использовать в исходном скрипте, чтобы определить его абсолютный путь. Протестировано в bash, zsh и dash, вызываемых как dash, так и sh.

NB :зависит от современной утилиты realpath (1)из пакета GNU coreutils

NB :lsof (1 )варианты также должны быть проверены, потому что аналогичные советы как с этой, так и с других страниц не работали для меня в Ubuntu 18 и 19, поэтому мне пришлось изобретать это заново.

getShellName() {
    [ -n "$BASH" ] && echo ${BASH##/*/} && return
    [ -n "$ZSH_NAME" ] && echo $ZSH_NAME && return
    echo ${0##/*/}
}

getCurrentScript() {
    local result
    case "$(getShellName)" in
        bash )  result=${BASH_SOURCE[0]}
                ;;
        zsh )   emulate -L zsh
                result=${funcfiletrace[1]%:*}
                ;;
        dash | sh )
                result=$(
                    lsof -p $$ -Fn  \
                    | tail --lines=1  \
                    | xargs --max-args=2  \
                    | cut --delimiter=' ' --fields=2
                )
                result=${result#n}
                ;;
        * )     result=$0
                ;;
    esac
    echo $(realpath $result)
}
0
20.08.2021, 13:38

Если вам нужен абсолютный путь к каталогу, содержащему скрипт, вы можете использовать этот фрагмент:

BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]:-$0}" )" >/dev/null 2>&1 && pwd )"

Если вам нужен только относительный путь к скрипту:

SCRIPT_PATH="${BASH_SOURCE[0]:-$0}"

Эти фрагменты должны работать на разных версиях bash и zsh.

0
20.08.2021, 13:38

Я использую приведенный выше код, чтобы получить путь к исходному файлу bash:

$(readlink -f ${BASH_SOURCE[0]})
-1
20.08.2021, 13:38

Теги

Похожие вопросы