У меня есть bash script, который должен знать свой полный путь. Я пытаюсь найти широко совместимый способ сделать это, не заканчивая относительными или напуганными способами. Мне нужно поддерживать bash, а не sh, csh и т.д.
Что я нашел до сих пор:
Принятый ответ на Получение исходной директории bash script изнутри" адреса, получающего путь от script через dirname $0
, это нормально, но может возвращать относительный путь (например, .
), что является проблемой, если вы хотите сменить каталоги в script и указате путь в каталог script. Тем не менее, dirname
будет частью головоломки.
Принятый ответ на "Bash script абсолютный путь с OSX" (конкретный OS X, но ответ работает независимо) дает функцию, который будет проверять, будет ли выглядеть $0
относительным, и если это так, то предварительно подкроет $PWD
. Но результат все равно может иметь относительные биты (хотя в целом он абсолютный) — например, если script находится t
в каталоге /usr/bin
, и вы находитесь в /usr
, и вы вводите bin/../bin/t
для запуска (да, это запутанно), вы получаете /usr/bin/../bin
как путь к каталогу script. Какой работает, но...
Решение readlink
на этой странице, которое выглядит следующим образом:
# Absolute path to this script. /home/user/bin/foo.sh
SCRIPT=$(readlink -f $0)
# Absolute path this script is in. /home/user/bin
SCRIPTPATH=`dirname $SCRIPT`
Но readlink
не является POSIX и, по-видимому, решение полагается на GNU readlink
, где BSD по какой-то причине не работает (у меня нет доступа к BSD-подобной системе для проверки).
Итак, различные способы сделать это, но все они имеют свои оговорки.
Что было бы лучше? Где "лучше" означает:
Вот что я придумал (отредактируйте: плюс некоторые хитрости, предоставленные sfstewman, levigroker, Кайл Стрэнд и Роб Кеннеди), что, похоже, в основном соответствует моему "лучшему" критерии:
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
Эта строка SCRIPTPATH
кажется особенно круговой, но нам она нужна, а не SCRIPTPATH=`pwd`
, чтобы правильно обрабатывать пробелы и символические ссылки.
Обратите внимание также, что эзотерические ситуации, такие как выполнение script, который не поступает из файла в доступной файловой системе вообще (что совершенно возможно), не обслуживаются там (или в любом другом ответы, которые я видел).
$PATH
и вы называете его bash scriptname
. В таком случае $0
не содержит никакого пути, только имя scriptname
.
Я удивлен, что команда realpath
здесь не упоминается. Я понимаю, что он широко переносится/переносится.
Ваше начальное решение будет:
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
И чтобы оставить символические ссылки неразрешенными в соответствии с вашими предпочтениями:
SCRIPT=`realpath -s $0`
SCRIPTPATH=`dirname $SCRIPT`
realpath
, упаковщики наверняка постарались разделить realpath в другой пакет. (текущая версия coreutils - 8.21, выпущенная 2013-02-04. Версия, которую я использую (для Arch Linux), выглядит как эта, 8.21, и пакет включает в себя realpath
, как и любой правильный пакет;)
SCRIPT_PATH=$(dirname $(realpath -s $0))
Самый простой способ, которым я нашел полный канонический путь в bash, - использовать cd
и pwd
:
ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
Использование ${BASH_SOURCE[0]}
вместо $0
создает такое же поведение независимо от того, вызывается ли script как <name>
или source <name>
dalvik/dx/etc/dx
.
Мне просто пришлось пересмотреть этот вопрос сегодня и нашел https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself. Он описывает решение, которое Я использовал и в прошлом.
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
Там больше вариантов при связанном ответе, например. для случая, когда сам script является символической ссылкой.
Не использует параметр -f
в readlink, поэтому должен работать в bsd/mac-osx
.
dot)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`]"
Script должен быть на диске где-то, пусть он будет по сети. Если вы попытаетесь запустить этот script из PIPE, он не будет работать
wget -o /dev/null -O - http://host.domain/dir/script.sh |bash
С технической точки зрения, это undefined.
Практически говоря, нет разумного способа обнаружить это. (совлокальный процесс не может получить доступ к env родителя)
~/scripts/myscript
и символическая ссылка в ~/bin/myscript
которая указывала на ../scripts/myscript
. Запуск ~/bin/myscript
из ~/
заставил его думать, что местоположение скрипта было ~/
. Решение здесь работало нормально, которое выглядит очень похоже на ваше решение
Как насчет использования:
SCRIPT_PATH=$(dirname `which $0`)
which
выводит на печать полный путь исполняемого файла, который был бы выполнен, когда переданный аргумент был введен в приглашении оболочки (в котором содержится $0)
dirname
разделяет суффиксы без каталогов из имени файла
Следовательно, в итоге вы получаете полный путь к script, независимо от того, был ли указан путь или нет.
dirname ./myscript
возвращает .
, Это может быть не то, что вы хотите.
Поскольку по умолчанию для моей Linux-системы не установлен realpath, для меня работает следующее:
SCRIPT="$(readlink --canonicalize-existing "$0")"
SCRIPTPATH="$(dirname "$SCRIPT")"
$SCRIPT
будет содержать путь к реальному пути к script и $SCRIPTPATH
реальному пути к каталогу, содержащему script.
Прежде чем использовать это, прочитайте комментарии этого ответа.
Ответ на этот вопрос очень поздно, но я использую:
SCRIPT=$( readlink -m $( type -p $0 )) # Full path to script
BASE_DIR=`dirname ${SCRIPT}` # Directory script is run in
NAME=`basename ${SCRIPT}` # Actual name of script even if linked
-m
Я полагаю, что OP ищет решение, которое не зависит от расширенной функциональности GNU readlink.
Мы разместили наш собственный продукт realpath-lib в GitHub для бесплатного и свободного использования сообщества.
Бесстыдный плагин, но с этой библиотекой Bash вы можете:
get_realpath <absolute|relative|symlink|local file>
Эта функция является ядром библиотеки:
function get_realpath() {
if [[ -f "$1" ]]
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
. Он бесплатный, чистый, прост и хорошо документирован, поэтому его также можно использовать в учебных целях, и, без сомнения, его можно улучшить. Попробуйте его на разных платформах.
Обновление. После некоторого пересмотра и тестирования мы заменили указанную выше функцию тем, что достигает одного и того же результата (без использования dirname, только pure Bash), но с большей эффективностью:
function get_realpath() {
[[ ! -f "$1" ]] && 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
, которая предоставляет возможность разрешать символические ссылки на физическую систему. По умолчанию он сохраняет неизменяемые символические ссылки.
sh совместимый способ:
SCRIPT_HOME=`dirname $0 | while read a; do cd $a && pwd && break; done`
while read a
части? Почему бы просто не cd $(dirname $a) && pwd
?
$( ... )
не будет работать в оболочке без bash. Я видел плохой скрипт, который использует выражение $( ... )
и начинает #!/bin/sh
Bin #!/bin/sh
много раз. Я рекомендую написать #!/bin/bash
в начале или прекратить использование выражения $( ... )
. Этот пример для 1-й рекомендации.
Вы можете попытаться определить следующую переменную:
CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
или вы можете попробовать следующую функцию в bash:
realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
Эта функция принимает 1 аргумент. Если аргумент уже имеет абсолютный путь, напечатайте его как есть, иначе напечатайте $PWD
variable + имя файла (без префикса ./
).
по теме:
Рассмотрение этой проблемы еще раз: есть очень популярное решение, которое упоминается в этом потоке, имеющем начало здесь:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
Я остался в стороне от этого решения из-за использования имени dirname - он может представлять кросс-платформенные трудности, особенно если script необходимо заблокировать по соображениям безопасности. Но как чистая альтернатива Bash, как насчет использования:
DIR="$( cd "$( echo "${BASH_SOURCE[0]%/*}" )" && pwd )"
Будет ли это вариант?
PATH=/path/to/your/script:$PATH yourscript.sh
. К сожалению, это нужно Баш
Легко читается? альтернатива. Игнорирует символические ссылки
#!/bin/bash
currentDir=$(
cd $(dirname "$0")
pwd
)
echo -n "current "
pwd
echo script $currentDir
bash: currentDir: command not found
Попробуйте следующее:
cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
readlink
не работает таким образом в BSD (и, следовательно, в OS X), и вопрос касался альтернативы.
/home/me/script.sh
), но возвращает только .
когда скрипт выполняется с помощью sh script.sh
или ./script.sh
Если мы используем Bash, я считаю, что это самый удобный способ, поскольку он не требует вызовов для любых внешних команд:
THIS_PATH="${BASH_SOURCE[0]}";
THIS_DIR=$(dirname $THIS_PATH)
$(dirname
вызывает dirname (1).
просто: BASEDIR=$(readlink -f $0 | xargs dirname)
нет фантастических операторов
Е.И.В..
Принятое решение имеет неудобное (для меня) не "исходное":
если вы называете это "source ../../yourScript
", $0
будет "bash
"!
Следующая функция (для bash >= 3.0) дает мне правильный путь, однако script может быть вызван (напрямую или через source
с абсолютным или относительным путем):
(по "правильному пути", я имею в виду полный абсолютный путь script, который называется, даже при вызове с другого пути, напрямую или с помощью "source
" )
#!/bin/bash
echo $0 executed
function bashscriptpath() {
local _sp=$1
local ascript="$0"
local asp="$(dirname $0)"
#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]}
, чтобы вернуть фактический script.
Возможно, приемлемый ответ на следующий вопрос может помочь.
Как я могу получить поведение readlink -f GNU на Mac?
Учитывая, что вы просто хотите канонизировать имя, которое вы получаете от конкатенации $PWD и $0 (если предположить, что $0 не является абсолютным для начала) Просто используйте серию регулярных выражений вдоль строки abs_dir=${abs_dir//\/.\//\/}
и т.д.
Да, я знаю, что это выглядит ужасно, но он будет работать и будет чистым bash.
pwd
, что я считаю неуклюжим в своем решении. Регулярное выражение интересно. Я не могу не беспокоиться о крайних случаях, хотя.
Еще один способ сделать это:
shopt -s extglob
selfpath=$0
selfdir=${selfpath%%+([!/])}
while [[ -L "$selfpath" ]];do
selfpath=$(readlink "$selfpath")
if [[ ! "$selfpath" =~ ^/ ]];then
selfpath=${selfdir}${selfpath}
fi
selfdir=${selfpath%%+([!/])}
done
echo $selfpath $selfdir
Я использовал следующий подход успешно некоторое время (не на OSX, хотя), и он использует только встроенную оболочку и обрабатывает случай "source foobar.sh", насколько я видел.
Одной из проблем с приведенным ниже примером кода (hastly together) является то, что функция использует $PWD, которая может быть или не быть правильной во время вызова функции. Таким образом, это должно быть обработано.
#!/bin/bash
function canonical_path() {
# Handle realtive vs absolute path
[ ${1:0:1} == '/' ] && x=$1 || x=$PWD/$1
# 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
Просто, черт возьми, я немного взломал на script, который делает вещи чисто текстовыми, чисто в bash. Надеюсь, я поймал все крайние случаи. Обратите внимание, что ${var//pat/repl}
, о котором я упоминал в другом ответе, не работает, поскольку вы не можете заставить его заменять только кратчайшее совпадение, что является проблемой для замены /foo/../
, например. /*/../
возьмет все перед собой, а не только одну запись. И поскольку эти шаблоны не являются действительно регулярными выражениями, я не вижу, как это можно сделать для работы. Итак, вот красиво запутанное решение, с которым я придумал, наслаждаюсь.;)
Кстати, дайте мне знать, если вы найдете какие-либо необработанные случаи ребер.
#!/bin/bash
canonicalize_path() {
local path="$1"
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/"
Один вкладыш
`dirname $(realpath $0)`
Проще говоря, это то, что работает для меня:
MY_DIR=`dirname $0`
source $MY_DIR/_inc_db.sh