Надежный способ для скрипта bash получить полный путь к себе? [дубликат]
этот вопрос уже есть ответ здесь:
у меня есть скрипт bash, который должен знать свой полный путь. Я пытаюсь найти широко-совместимый способ сделать это, не заканчивая с относительной или симпатичный пути. Мне нужно только поддержка bash, не sh, csh и т. д.
что я нашел до сих пор:
принятый ответ на "получение исходного каталога скрипта Bash изнутри" адреса, получающие путь скрипта через
dirname
, что хорошо, но это может вернуть относительные путь (например,.
), что является проблемой, если вы хотите изменить каталоги в скрипте и путь все еще указывает на каталог скрипта. И все же ... --3--> будет частью головоломки.принятый ответ на "абсолютный путь сценария Bash с OSX" (OS X конкретный, но ответ работает независимо) дает функцию, которая будет проверять, чтобы увидеть, если
выглядит относительным, и если так будет pre-pend
$PWD
к нему. Но результат все равно может иметь относительные биты (хотя в целом он абсолютный) - например, если скриптt
в каталоге/usr/bin
и ты/usr
и вы типbin/../bin/t
чтобы запустить его (да, это запутанно), вы в конечном итоге с/usr/bin/../bin
как путь к каталогу скрипта. Который работает, но...-
на
readlink
решение на этой странице, который выглядит так:# Absolute path to this script. /home/user/bin/foo.sh SCRIPT=$(readlink -f ) # Absolute path this script is in. /home/user/bin SCRIPTPATH=`dirname $SCRIPT`
но
readlink
не POSIX и, по-видимому, решение зависит от GNUreadlink
где BSD не будет работать по какой-то причине (у меня нет доступа к BSD-подобной системе для проверять.)
Итак, различные способы сделать это, но у всех есть свои предостережения.
что было бы лучшим способом? Где "лучше" означает:
- дает мне абсолютный путь.
- извлекает фанки-биты даже при вызове запутанным способом (см. Комментарий к #2 выше). (Например, по крайней мере, умеренно канонизирует путь.)
- полагается только на bash-isms или вещи, которые почти наверняка будут на самых популярных вкусах * системы nix (GNU / Linux, BSD и BSD-подобные системы, такие как OS X и т. д.).
- избегает вызова внешних программ, если это возможно (например, предпочитает встроенные bash).
- (Обновлено, спасибо за предупреждение,wich) не нужно разрешать символические ссылки (на самом деле, я бы предпочел, чтобы он оставил их в покое, но это не требование).
23 ответов
вот что я придумал (edit: плюс некоторые настройки, предоставляемые sfstewman, levigroker, Кайл Сумка и Роб Кеннеди), это, кажется, в основном соответствует моим" лучшим " критериям:
SCRIPTPATH="$( cd "$(dirname "")" ; pwd -P )"
Это SCRIPTPATH
линия кажется особенно Окольной, но нам она нужна, а не SCRIPTPATH=`pwd`
для правильной обработки пробелов и символических ссылок.
обратите внимание также, что эзотерические ситуации, такие как выполнение сценария, который не исходящий из файла в доступной файловой системе вообще (что вполне возможно), не обслуживается там (или в любом из других ответов, которые я видел).
Я удивлен, что realpath
команда здесь не упоминается. Насколько я понимаю, он широко переносится / портируется.
ваше начальное решение становится:
SCRIPT=`realpath `
SCRIPTPATH=`dirname $SCRIPT`
и оставить символические ссылки неразрешенными в соответствии с вашими предпочтениями:
SCRIPT=`realpath -s `
SCRIPTPATH=`dirname $SCRIPT`
самый простой способ, который я нашел, чтобы получить полный канонический путь в bash является использование cd
и pwd
:
ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
используя ${BASH_SOURCE[0]}
вместо производит такое же поведение независимо от того, вызывается ли скрипт как
<name>
или source <name>
Мне просто нужно было вернуться к этой проблеме сегодня и найти https://stackoverflow.com/a/246128/1034080. Он разрабатывает решение, которое я использовал в прошлом, а также.
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
в связанном ответе есть больше вариантов, например, для случая, когда сам скрипт является символической ссылкой.
получить абсолютный путь скрипта
Не использовать -f
опция в readlink, поэтому должна работать в bsd / mac-osx
поддерживает
- источник ./ script (при вызове
.
точка оператора) - абсолютный путь /путь/до/скрипта
- относительный путь ./ script
- / path / dir1/../директория dir2/dir3/../ script
- при вызове из symlink
- если ссылка вложена например)
foo->dir1/dir2/bar bar->./../doe doe->script
- когда вызывающий абонент изменяет имя скриптов
я ищу угловые случаи, когда этот код не работает. Пожалуйста, дайте мне знать.
код
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
while([ -h "${SCRIPT_PATH}" ]); do
cd "`dirname "${SCRIPT_PATH}"`"
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
done
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH="`pwd`";
popd > /dev/null
echo "srcipt=[${SCRIPT_PATH}]"
echo "pwd =[`pwd`]"
известный issuse
скрипт должно быть где-то на диске пусть будет по сети. если вы попытаетесь запустить этот скрипт из трубы, он не будет работать
wget -o /dev/null -O - http://host.domain/dir/script.sh |bash
Технически говоря, это не определено.
Практически говоря, нет никакого разумного способа обнаружить это. (co-process не может получить доступ к env родителя)
а как насчет:
SCRIPT_PATH=$(dirname `which `)
which
печатает в stdout полный путь исполняемого файла, который был бы выполнен, когда переданный аргумент был введен в командной строке (что и содержит $0)
dirname
удаляет суффикс без каталога из имени файла
следовательно, вы получаете полный путь к скрипту, независимо от того, был ли указан путь или нет.
поскольку realpath не установлен по умолчанию в моей системе Linux, для меня работает следующее:
SCRIPT="$(readlink --canonicalize-existing "")"
SCRIPTPATH="$(dirname "$SCRIPT")"
$SCRIPT
будет содержать реальный путь к файлу сценария и $SCRIPTPATH
реальный путь к каталогу, содержащему скрипт.
перед использованием этого читать комментарии ответ.
отвечая на этот вопрос слишком поздно, но я использую:
SCRIPT=$( readlink -m $( type -p )) # Full path to script
BASE_DIR=`dirname ${SCRIPT}` # Directory script is run in
NAME=`basename ${SCRIPT}` # Actual name of script even if linked
мы разместили наш собственный продукт realpath-lib на GitHub для свободного и свободного использования сообщества.
бесстыдный плагин, но с этой библиотекой Bash вы можете:
get_realpath <absolute|relative|symlink|local file>
эта функция является ядром библиотеки:
function get_realpath() {
if [[ -f "" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try 'cd .; cd -;' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi
# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success
}
он не требует каких-либо внешних зависимостей, просто Bash 4+. Также содержит функции для get_dirname
, get_filename
, get_stemname
и validate_path validate_realpath
. Это бесплатно, чисто, просто и хорошо документировано, поэтому его можно использовать и в учебных целях, и, без сомнения, можно улучшить. Попробуйте это на разных платформах.
Update: после некоторого обзора и тестирования мы заменили вышеуказанную функцию чем-то, что достигает того же результата (без использования dirname, только pure Bash), но с лучшей эффективностью:
function get_realpath() {
[[ ! -f "" ]] && return 1 # failure : file does not exist.
[[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
return 0 # success
}
это также включает в себя параметр среды no_symlinks
это обеспечивает возможность разрешения символических ссылок на физическую систему. По умолчанию он сохраняет символические ссылки нетронутый.
вы можете попытаться определить следующую переменную:
CWD="$(cd -P -- "$(dirname -- "")" && pwd -P)"
или вы можете попробовать следующую функцию в bash:
realpath () {
[[ = /* ]] && echo "" || echo "$PWD/${1#./}"
}
эта функция принимает 1 аргумент. Если аргумент уже имеет абсолютный путь, выведите его как есть, иначе выведите $PWD
переменная + аргумент filename (без ./
префикс).
по теме:
возможно, принятый ответ на следующий вопрос может помочь.
как я могу получить поведение GNU readlink-f на Mac?
учитывая, что вы просто хотите канонизировать имя, которое вы получаете от объединения $PWD и $0 (предполагая, что $0 не является абсолютным для начала), просто используйте серию замен регулярных выражений по строке abs_dir=${abs_dir//\/.\//\/}
и такие.
Да, я знаю, что это выглядит ужасно, но это будет работать и чистый Баш.
рассмотрение этой проблемы снова: есть очень популярное решение, на которое ссылаются в этом потоке, который имеет свое происхождение здесь:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
Я держался подальше от этого решения из - за использования dirname-он может представлять кросс-платформенные трудности, особенно если сценарий должен быть заблокирован по соображениям безопасности. Но как чистая альтернатива Bash, как насчет использования:
DIR="$( cd "$( echo "${BASH_SOURCE[0]%/*}" )" && pwd )"
будет ли это вариант?
легко читается? альтернатива. Игнорирует симлинки
#!/bin/bash
currentDir=$(
cd $(dirname "")
pwd
)
echo -n "current "
pwd
echo script $currentDir
принятое решение имеет неудобное (для меня), чтобы не быть "источником":
если вы позвоните из"source ../../yourScript
", будет "
bash
"!
следующая функция (для bash >= 3.0) дает мне правильный путь, однако скрипт может быть вызван (напрямую или через source
, С абсолютным или относительным путем):
(под "правильным путем" я подразумеваю полный абсолютный путь вызываемого скрипта, даже при вызове с другого пути, напрямую или с "source
")
#!/bin/bash
echo executed
function bashscriptpath() {
local _sp=
local ascript=""
local asp="$(dirname )"
#echo "b1 asp '$asp', b1 ascript '$ascript'"
if [[ "$asp" == "." && "$ascript" != "bash" && "$ascript" != "./.bashrc" ]] ; then asp="${BASH_SOURCE[0]%/*}"
elif [[ "$asp" == "." && "$ascript" == "./.bashrc" ]] ; then asp=$(pwd)
else
if [[ "$ascript" == "bash" ]] ; then
ascript=${BASH_SOURCE[0]}
asp="$(dirname $ascript)"
fi
#echo "b2 asp '$asp', b2 ascript '$ascript'"
if [[ "${ascript#/}" != "$ascript" ]]; then asp=$asp ;
elif [[ "${ascript#../}" != "$ascript" ]]; then
asp=$(pwd)
while [[ "${ascript#../}" != "$ascript" ]]; do
asp=${asp%/*}
ascript=${ascript#../}
done
elif [[ "${ascript#*/}" != "$ascript" ]]; then
if [[ "$asp" == "." ]] ; then asp=$(pwd) ; else asp="$(pwd)/${asp}"; fi
fi
fi
eval $_sp="'$asp'"
}
bashscriptpath H
export H=${H}
ключ должен обнаружить "source
" дело и использовать ${BASH_SOURCE[0]}
чтобы вернуть фактический скрипт.
Если мы используем Bash, я считаю, что это самый удобный способ, поскольку он не требует вызовов каких-либо внешних команд:
THIS_PATH="${BASH_SOURCE[0]}";
THIS_DIR=$(dirname $THIS_PATH)
просто для ада я сделал немного взлома скрипта, который делает вещи чисто текстуально, чисто в bash. Надеюсь, я поймал все крайние случаи. Обратите внимание, что ${var//pat/repl}
то, что я упомянул в другом ответе, не работает, так как вы не можете заменить его только кратчайшим возможным совпадением, что является проблемой для замены /foo/../
, например,/*/../
примет все, а не только одну запись. И поскольку эти шаблоны на самом деле не являются регулярными, я не вижу, как это можно сделать работа. Итак, вот красиво запутанное решение, которое я придумал, наслаждайтесь. ;)
кстати, дайте мне знать, если вы найдете какие-либо необработанные крайние случаи.
#!/bin/bash
canonicalize_path() {
local path=""
OIFS="$IFS"
IFS=$'/'
read -a parts < <(echo "$path")
IFS="$OIFS"
local i=${#parts[@]}
local j=0
local back=0
local -a rev_canon
while (($i > 0)); do
((i--))
case "${parts[$i]}" in
""|.) ;;
..) ((back++));;
*) if (($back > 0)); then
((back--))
else
rev_canon[j]="${parts[$i]}"
((j++))
fi;;
esac
done
while (($j > 0)); do
((j--))
echo -n "/${rev_canon[$j]}"
done
echo
}
canonicalize_path "/.././..////../foo/./bar//foo/bar/.././bar/../foo/bar/./../..//../foo///bar/"
я использовал следующий подход успешно некоторое время (не на OSX, хотя), и он использует только встроенную оболочку и обрабатывает " источник foobar.sh-дело, насколько я знаю.
одна проблема с приведенным ниже (поспешно собранным) примером кода заключается в том, что функция использует $PWD, который может быть или не быть правильным во время вызова функции. Так что с этим нужно разобраться.
#!/bin/bash
function canonical_path() {
# Handle realtive vs absolute path
[ ${1:0:1} == '/' ] && x= || x=$PWD/
# Change to dirname of x
cd ${x%/*}
# Combine new pwd with basename of x
echo $(pwd -P)/${x##*/}
cd $OLDPWD
}
echo $(canonical_path "${BASH_SOURCE[0]}")
type [
type cd
type echo
type pwd
еще один способ сделать это:
shopt -s extglob
selfpath=
selfdir=${selfpath%%+([!/])}
while [[ -L "$selfpath" ]];do
selfpath=$(readlink "$selfpath")
if [[ ! "$selfpath" =~ ^/ ]];then
selfpath=${selfdir}${selfpath}
fi
selfdir=${selfpath%%+([!/])}
done
echo $selfpath $selfdir