Ответ 1
Оболочка POSIX-оболочки (sh
) $BASH_SOURCE
равна $0
. см. снизу для справочной информации
Предостережение. Важным отличием является то, что , если ваш script находится в исходной оболочке с .
), снизу ниже не будет работать должным образом. ниже ниже
Обратите внимание, что в приведенных ниже фрагментах я изменил DIR
на DIR
, потому что он лучше не использовать имена переменных с прописными буквами, чтобы избежать столкновений с переменными среды и специальными переменными оболочки.
Префикс CDPATH=
заменяет > /dev/null
в исходной команде: $CDPATH
устанавливается в пустую строку, чтобы гарантировать, что cd
никогда не будет эхом ничего.
В простейшем случае это будет (эквивалент команды OP):
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
Если вы также хотите разрешить результирующий путь каталога к своей конечной цели, если каталог и/или его компоненты являются символическими ссылками, добавьте -P
в команду pwd
:
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
Предостережение. Это НЕ так же, как поиск script собственного исходного каталога происхождения:
Скажем, ваш script foo
символически связан с /usr/local/bin/foo
в $PATH
, но его истинный путь /foodir/bin/foo
.
Вышеописанное будет сообщать /usr/local/bin
, поскольку разрешение символьной ссылки (-P
) применяется к каталогу /usr/local/bin
, а не к самому script.
Чтобы найти собственный каталог script исходного каталога, вам нужно будет проверить путь script, чтобы увидеть, является ли он символической ссылкой и, если да, следуйте (цепочка ) символических ссылок на конечный файл цели, а затем извлечь путь к каталогу из канонического пути целевого файла.
GNU readlink -f
(лучше: readlink -e
) может сделать это для вас, но readlink
не является утилитой POSIX.
Хотя на платформах BSD, включая OSX, есть утилита readlink
, а на OSX она не поддерживает функциональность -f
. Тем не менее, чтобы показать, насколько проста задача, если readlink -f
доступен: dir=$(dirname "$(readlink -f -- "$0")")
.
На самом деле существует нет утилиты POSIX для разрешения символических ссылок на файлы. Есть способы обойти это, но они громоздки и не полностью надежны:
следующая, совместимая с POSIX функция оболочки реализует то, что GNU readlink -e
делает, и является <сильным > достаточно надежным решением, которое работает только в двух случаях с редким краем
- пути со встроенными символами новой строки (очень редко)
- имена файлов, содержащие литеральную строку
->
(также редко)
С помощью этой функции, названной rreadlink
, определенной, , определяется путь script истинного каталога происхождения:
dir=$(dirname -- "$(rreadlink "$0")")
rreadlink()
исходный код - место перед вызовами в скриптах:
rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`.
target=$1 fname= targetDir= CDPATH=
# Try to make the execution environment as predictable as possible:
# All commands below are invoked via `command`, so we must make sure that `command`
# itself is not redefined as an alias or shell function.
# (Note that command is too inconsistent across shells, so we don't use it.)
# `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
# an external utility version of it (e.g, Ubuntu).
# `command` bypasses aliases and shell functions and also finds builtins
# in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
# to happen.
{ \unalias command; \unset -f command; } >/dev/null 2>&1
[ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
while :; do # Resolve potential symlinks until the ultimate target is found.
[ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
fname=$(command basename -- "$target") # Extract filename.
[ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
if [ -L "$fname" ]; then
# Extract [next] target path, which may be defined
# *relative* to the symlink own directory.
# Note: We parse `ls -l` output to find the symlink target
# which is the only POSIX-compliant, albeit somewhat fragile, way.
target=$(command ls -l "$fname")
target=${target#* -> }
continue # Resolve [next] symlink target.
fi
break # Ultimate target reached.
done
targetDir=$(command pwd -P) # Get canonical dir. path
# Output the ultimate target canonical path.
# Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
if [ "$fname" = '.' ]; then
command printf '%s\n' "${targetDir%/}"
elif [ "$fname" = '..' ]; then
# Caveat: something like /var/.. will resolve to /private (assuming /[email protected] -> /private/var), i.e. the '..' is applied
# AFTER canonicalization.
command printf '%s\n' "$(command dirname -- "${targetDir}")"
else
command printf '%s\n' "${targetDir%/}/$fname"
fi
)
Чтобы быть надежным и предсказуемым, функция использует command
, чтобы гарантировать, что вызываются только встроенные оболочки или внешние утилиты (игнорирует перегрузки в формах псевдонимов и функций).
Он тестировался в последних версиях следующих оболочек: bash
, dash
, ksh
, zsh
.
Как обрабатывать вызовы с исходными кодами:
tl; dr:
Использование только функций POSIX:
- Вы не можете определить путь script в исходном вызове (кроме
zsh
, который, однако, обычно не действует какsh
). - может определить, будет ли ваш script использоваться только в том случае, если ваш script будет получен непосредственно с помощью оболочки (например, в файле оболочки/файле инициализации, возможно, через цепочка источников), сравнивая
$0
с исполняемым именем/контуром оболочки (кромеzsh
, где, как отмечено$0
, действительно является текущим script). Напротив (кромеzsh
), script был получен из другого script, который сам был вызван непосредственно, содержит этот путь script в$0
. - Чтобы решить эти проблемы,
bash
,ksh
иzsh
имеют нестандартные функции, которые позволяют определять фактический путь script даже в сценариях с исходными кодами, а также определять, w370 > является источником или нет; например, вbash
,$BASH_SOURCE
всегда содержит пробег script, независимо от того, является ли он источником или нет, а[[ $0 != "$BASH_SOURCE" ]]
может использоваться для проверки того, будет ли источник script отправлен.
Чтобы показать, почему это невозможно сделать, проанализируйте команду ответ Уолтера:
# NOT recommended - see discussion below.
DIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
- (Два аспекта:
- Использование
-P
дважды избыточно - достаточно использовать его сpwd
. - В команде отсутствует блокировка вывода
cd
потенциала stdout, если$CDPATH
устанавливается.)
- Использование
-
command -v -- "$0"
-
command -v -- "$0"
предназначен для покрытия одного дополнительного сценария: если script получается из интерактивной оболочки,$0
обычно содержит простое имя исполняемого файла оболочки (sh
), и в этом случаеdirname
просто вернет.
(потому что то, чтоdirname
неизменно делает, когда задан аргумент без компонента пути).command -v -- "$0"
затем возвращает этот абсолютный путь оболочки через поиск$PATH
(/bin/sh
). Обратите внимание, однако, что в командах для входа на некоторых платформах (например, OSX) есть имя файла с префиксом-
в$0
(-sh
), и в этом случаеcommand -v -- "$0"
не работает должным образом (возвращает пустую строку). - И наоборот,
command -v -- "$0"
может ошибочно работать в двух сценариях, не относящихся к источникам, в которые непосредственно запускается исполняемый файл оболочкиsh
, с аргументом script в качестве аргумента:- если сам script не является исполняемым:
command -v -- "$0"
может возвращать пустую строку, в зависимости от того, какая конкретная оболочка действует какsh
для данной системы:bash
,ksh
иzsh
return пустая строка; толькоdash
эхо-сигналы$0
спецификация POSIX. дляcommand
явно не указано, должен лиcommand -v
при применении к пути файловой системы возвращать только исполняемые файлы - это то, чтоbash
,ksh
иzsh
do - но вы может утверждать, что это подразумевается самой цельюcommand
; Любопытно, чтоdash
, который обычно является наиболее совместимым гражданином POSIX, отклоняется от стандарта здесь. Напротив,ksh
является единственным гражданином модели здесь, поскольку он является единственным, который сообщает только исполняемые файлы и сообщает им абсолютный (хотя и не нормированный) путь, как того требует спецификация. - если script является исполняемым, но не в
$PATH
, а вызов использует его простое имя файла (например,sh myScript
),command -v -- "$0"
также возвращает пустую строку, за исключениемdash
.
- если сам script не является исполняемым:
- Учитывая, что каталог script не может быть определен при получении script - поскольку
$0
затем не содержит эту информацию (кромеzsh
, которая обычно не действует какsh
) - нет хорошего решения этой проблемы.- Возврат пути к каталогу исполняемого файла оболочки в этой ситуации ограничен ограниченным использованием - это, в конце концов, не каталог script - за исключением, возможно, более позднего использования этого пути в тесте, чтобы определить, есть ли script.
- Более надежным подходом было бы просто непосредственно протестировать
$0
:[ "$0" = "sh" ] || [ "$0" = "-sh" ] || [ "$0" = "/bin/sh" ]
- Более надежным подходом было бы просто непосредственно протестировать
- Однако даже это не работает, если script получен из другого script (который сам был вызван непосредственно), потому что
$0
затем просто содержит путь sourcing script.
- Возврат пути к каталогу исполняемого файла оболочки в этой ситуации ограничен ограниченным использованием - это, в конце концов, не каталог script - за исключением, возможно, более позднего использования этого пути в тесте, чтобы определить, есть ли script.
- Учитывая ограниченную полезность
command -v -- "$0"
в сценариях источников и тот факт, что он разбивает два сценария, не относящихся к источникам, мой голос за НЕиспользует его, что оставляет нам:- Все сценарии, не связанные с источником.
- В запросах с источниками вы не можете определить путь script, и в лучшем случае в ограниченных обстоятельствах вы можете определить, происходит ли поиск источника:
- При непосредственном использовании оболочки (например, из профиля оболочки/файла инициализации)
$dir
заканчивается либо содержащим.
, если исполняемый файл оболочки был вызван как простое имя файла (применениеdirname
к простому filename всегда возвращает.
) или путь к исполняемому каталогу оболочки в противном случае..
не может быть надежно отличен от невостребованного вызова из текущего каталога. - Когда источник из другого script (который сам по себе также не был найден),
$0
содержит этот путь script, а источник script не имеет возможности сообщить, является ли этот случай.
- При непосредственном использовании оболочки (например, из профиля оболочки/файла инициализации)
-
Фоновая информация:
POSIX определяет поведение $0
по сценариям оболочки здесь.
По существу, $0
должен отражать путь к файлу script, как указано, что подразумевает:
- НЕ полагайтесь на
$0
, содержащий абсолютный путь. -
$0
содержит абсолютный путь, только если:- вы явно указываете абсолютный путь; например.:
-
~/bin/myScript
(при условии, что сам script является исполняемым) -
sh ~/bin/myScript
-
- вы вызываете исполняемый файл script по простому имени файла, который требует, чтобы он исполнялся и в
$PATH
; за кулисами система преобразуетmyScript
в абсолютный путь и затем выполняет ее; например.:-
myScript # executes /home/jdoe/bin/myScript, for instance
-
- вы явно указываете абсолютный путь; например.:
-
Во всех остальных случаях
$0
будет отображать путь script , как указано:- При явном вызове
sh
с script это может быть просто имя файла (например,sh myScript
) или относительный путь (например,sh ./myScript
) - При непосредственном вызове исполняемого файла script это может быть относительный путь (например,
./myScript
) - обратите внимание, что простое имя файла найдет только скрипты в$PATH
).
- При явном вызове
На практике bash
, dash
, ksh
и zsh
проявляют это поведение.
В отличие от POSIX НЕ задает значение $0
при поиске script (используя специальную встроенную утилиту .
( "точка" )), поэтому вы не можете полагаться на него, и на практике поведение отличается от оболочки.
- Таким образом, вы не можете вслепую использовать
$0
, когда ваш script является источником и ожидает стандартизованного поведения.- На практике
bash
,dash
иksh
оставляют$0
нетронутыми при поиске сценариев, что означает, что$0
содержит значение$0
вызывающего абонента или, более точно, значение$0
самого последнего вызывающего абонента в цепочке вызовов, которая не была получена сама по себе; таким образом,$0
может указывать либо на исполняемый файл оболочки, либо на путь другого (непосредственно вызываемого) script, который был источником текущего. - В отличие от этого,
zsh
, как одиночный диссидент, действительно сообщает текущий путь script в$0
. И наоборот,$0
не будет указывать, является ли источник script или нет. - Вкратце: используя только функции POSIX, вы не можете точно сказать, находится ли script под рукой, а не то, что находится в пути script, а также то, что отношение
$0
к текущий script путь.
- На практике
- Если вам необходимо обработать эту ситуацию, , вы должны определить конкретную оболочку под рукой и получить доступ к ее нестандартным функциям:
-
bash
,ksh
иzsh
все предлагают свои собственные способы получения запущенного пути script, даже если он был создан.
-
Для полноты: значение $0
в других контекстах:
- Внутри функции оболочки POSIX требует, чтобы
$0
оставался неизменным; поэтому, независимо от того, какое значение оно имеет вне функции, оно также будет внутри.- На практике
bash
,dash
иksh
ведут себя таким образом. - Опять же,
zsh
является одиночным диссидентом и сообщает имя функции.
- На практике
- В оболочке, принявшей командную строку через параметр
-c
при запуске, это первый операнд (необязательный аргумент), который устанавливает$0
; например.:-
sh -c 'echo \$0: $0 \$1: $1' foo one # -> '$0: foo $1: one'
-
bash
,dash
,ksh
иzsh
все ведут себя таким образом.
-
- В противном случае, в оболочке, не выполняющей файл script,
$0
- это значение первого аргумента, переданного родительским процессом оболочки, как правило, имя или путь оболочки (например,sh
или/bin/sh
); Это включает:- интерактивная оболочка
- Caveat: некоторые платформы, в частности OSX, всегда создают оболочки входа при создании интерактивных оболочек и добавляют
-
к имени оболочки перед тем, как поместить его в$0
, чтобы сигнализировать shell, что это оболочка _login; таким образом, по умолчанию$0
сообщает-bash
, а неbash
, в интерактивных оболочках OSX.
- Caveat: некоторые платформы, в частности OSX, всегда создают оболочки входа при создании интерактивных оболочек и добавляют
- оболочка, которая читает команды из stdin
- это также относится к подключению файла script к оболочке через stdin (например,
sh < myScript
)
- это также относится к подключению файла script к оболочке через stdin (например,
-
bash
,dash
,ksh
иzsh
все ведут себя таким образом.
- интерактивная оболочка