Как получить абсолютный путь к сценарию оболочки на MacOS? - PullRequest
30 голосов
/ 22 апреля 2011

readlink -f не существует в MacOS. Единственное работающее решение для Mac OS, которое мне удалось найти в сети, выглядит так:

if [[ $(echo $0 | awk '/^\//') == $0 ]]; then
    ABSPATH=$(dirname $0)
else
    ABSPATH=$PWD/$(dirname $0)
fi

Кто-нибудь может предложить что-нибудь более изящное для этой, казалось бы, тривиальной задачи?

Ответы [ 11 ]

63 голосов
/ 22 апреля 2011

Другой (тоже довольно некрасивый) вариант:

ABSPATH=$(cd "$(dirname "$0")"; pwd)
9 голосов
/ 30 августа 2012

Получить абсолютный путь к скрипту оболочки

Откопал несколько старых скриптов из моего .bashrc, немного обновил синтаксис, добавил набор тестов.

Поддерживает

  • source ./script (при вызове оператором . dot)
  • Абсолютный путь / путь / к / сценарию
  • Относительный путь, как ./script
  • /path/dir1/../dir2/dir3/../script
  • При вызове из символической ссылки
  • Когда символическая ссылка вложена, например) foo->dir1/dir2/bar bar->./../doe doe->script
  • При изменении вызывающегоимя сценария

Он был успешно протестирован и использован в реальных проектах, однако могут быть случаи, о которых я не знаю.
Если вам удалось найти такую ​​ситуацию, пожалуйста,дайте мне знать.
(Например, я знаю, что это не работает на оболочке sh)

Код

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

С технической точки зрения, он не определен.
С практической точки зрения, нет разумного способа обнаружить это.

Используемый контрольный пример

И текущий контрольный пример, проверяющий его работоспособность.

#!/bin/bash
# setup test enviroment
mkdir -p dir1/dir2
mkdir -p dir3/dir4
ln -s ./dir1/dir2/foo bar
ln -s ./../../dir3/dir4/test.sh dir1/dir2/foo
ln -s ./dir1/dir2/foo2 bar2
ln -s ./../../dir3/dir4/doe dir1/dir2/foo2
cp test.sh ./dir1/dir2/
cp test.sh ./dir3/dir4/
cp test.sh ./dir3/dir4/doe
P="`pwd`"
echo "--- 01"
echo "base  =[${P}]" && ./test.sh
echo "--- 02"
echo "base  =[${P}]" && `pwd`/test.sh
echo "--- 03"
echo "base  =[${P}]" && ./dir1/dir2/../../test.sh
echo "--- 04"
echo "base  =[${P}/dir3/dir4]" && ./bar
echo "--- 05"
echo "base  =[${P}/dir3/dir4]" && ./bar2
echo "--- 06"
echo "base  =[${P}/dir3/dir4]" && `pwd`/bar
echo "--- 07"
echo "base  =[${P}/dir3/dir4]" && `pwd`/bar2
echo "--- 08"
echo "base  =[${P}/dir1/dir2]" && `pwd`/dir3/dir4/../../dir1/dir2/test.sh
echo "--- 09"
echo "base  =[${P}/dir1/dir2]" && ./dir1/dir2/test.sh
echo "--- 10"
echo "base  =[${P}/dir3/dir4]" && ./dir3/dir4/doe
echo "--- 11"
echo "base  =[${P}/dir3/dir4]" && ./dir3/dir4/test.sh
echo "--- 12"
echo "base  =[${P}/dir3/dir4]" && `pwd`/dir3/dir4/doe
echo "--- 13"
echo "base  =[${P}/dir3/dir4]" && `pwd`/dir3/dir4/test.sh
echo "--- 14"
echo "base  =[${P}/dir3/dir4]" && `pwd`/dir1/dir2/../../dir3/dir4/doe
echo "--- 15"
echo "base  =[${P}/dir3/dir4]" && `pwd`/dir1/dir2/../../dir3/dir4/test.sh
echo "--- 16"
echo "base s=[${P}]" && source test.sh
echo "--- 17"
echo "base s=[${P}]" && source `pwd`/test.sh
echo "--- 18"
echo "base s=[${P}/dir1/dir2]" && source ./dir1/dir2/test.sh
echo "--- 19"
echo "base s=[${P}/dir3/dir4]" && source ./dir1/dir2/../../dir3/dir4/test.sh
echo "--- 20"
echo "base s=[${P}/dir3/dir4]" && source `pwd`/dir1/dir2/../../dir3/dir4/test.sh
echo "--- 21"
pushd . >/dev/null
cd ..
echo "base x=[${P}/dir3/dir4]"
./`basename "${P}"`/bar
popd  >/dev/null

PurpleFox aka GreenFox

3 голосов
/ 04 августа 2016

Также обратите внимание, что пакет homebrew (http://brew.sh) coreutils включает realpath (ссылка создана в /opt/local/bin).

$ realpath bin
/Users/nhed/bin
3 голосов
/ 22 апреля 2011

Используя bash, я предлагаю такой подход.Сначала вы переходите в каталог, а затем берете текущий каталог, используя pwd.После этого вы должны вернуться в старый каталог, чтобы убедиться, что ваш сценарий не создает побочных эффектов для другого сценария, вызывающего его.

cd "$(dirname -- "$0")"
dir="$PWD"
echo "$dir"
cd - > /dev/null

Это решение безопасно со сложным путем.У вас никогда не возникнет проблем с пробелами или специальными символами, если вы поставите кавычки.

Примечание: / dev / null is require или "cd -" вывести путь, к которому возвращается

1 голос
/ 08 декабря 2015

Я считаю, что это полезно для символических / динамических ссылок - хотя работает только с readlink GNU (из-за флага -f):

# detect if GNU readlink is available on OS X
if [ "$(uname)" = "Darwin" ]; then
  which greadlink > /dev/null || {
    printf 'GNU readlink not found\n'
    exit 1
  }
  alias readlink="greadlink"
fi

# create a $dirname variable that contains the file dir
dirname=$(dirname "$(readlink -f "$0")")

# use $dirname to find a relative file
cat "$dirname"/foo/bar.txt
1 голос
/ 22 апреля 2011

Можете ли вы попробовать что-то подобное в вашем скрипте?

echo $(pwd)/"$0"

В моей машине это показывает:

/home/barun/codes/ns2/link_down/./test.sh

- это абсолютный путь к сценарию оболочки.

1 голос
/ 22 апреля 2011

Если вы не возражаете против использования perl:

ABSPATH=$(perl -MCwd=realpath -e "print realpath '$0'")
0 голосов
/ 14 июня 2014

Я использую приведенную ниже функцию для эмуляции «readlink -f» для сценариев, которые должны работать как в Linux, так и в Mac OS X.

#!/bin/bash
# This was re-worked on 2018-10-26 after der@build correctly
# observed that the previous version did not work.

# Works on both linux and Mac OS X.
# The "pwd -P" re-interprets all symlinks.
function read-link() {
    local path=$1
    if [ -d $path ] ; then
        local abspath=$(cd $path; pwd -P)
    else
        local prefix=$(cd $(dirname -- $path) ; pwd -P)
        local suffix=$(basename $path)
        local abspath="$prefix/$suffix"
    fi
    if [ -e $abspath ] ; then
        echo $abspath
    else
        echo 'error: does not exist'
    fi
}

# Example usage.
while (( $# )) ; do
    printf '%-24s - ' "$1"
    read-link $1
    shift
done

Это вывод для некоторых общих целей Mac OS X:

$ ./example.sh /usr/bin/which /bin/which /etc/racoon ~/Downloads
/usr/bin/which           - /usr/bin/which
/bin/which               - error: does not exist
/etc/racoon              - /private/etc/racoon
/Users/jlinoff/Downloads - /Users/jlinoff/Downloads

Вывод для некоторых целей Linux.

$ ./example.sh /usr/bin/which /bin/whichx /etc/init.d ~/Downloads
/usr/bin/which           - /usr/bin/which
/bin/whichx              - error: does not exist
/etc/init.d              - /etc/init.d
/home/jlinoff/Downloads  - /home/jlinoff/Downloads
0 голосов
/ 13 июня 2013

Если вы используете ksh, для параметра ${.sh.file} задается абсолютный путь к сценарию.Чтобы получить родительский каталог скрипта: ${.sh.file%/*}

0 голосов
/ 06 марта 2012

Это работает, если это не символическая ссылка, и, возможно, немного менее уродливо:

ABSPATH=$(dirname $(pwd -P $0)/${0#\.\/})
...