Получить исходный каталог скрипта Bash из самого скрипта

4227

Как получить путь к каталогу, в котором находится Bash script, внутри которого script?

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

$ ./application
  • 58
    Ни одно из текущих решений не будет работать, если в конце имени каталога есть какие-либо символы новой строки - они будут удалены при подстановке команды. Чтобы обойти это, вы можете добавить не-символ новой строки внутри подстановки команды - DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)" - и удалить его без подстановка команды - DIR="${DIR%x}" .
  • 73
    @ jpmc26 Есть две очень распространенные ситуации: аварии и саботаж. Сценарий не должен быть непредсказуемым, потому что кто-то где-то сделал mkdir $'\n' .
Показать ещё 17 комментариев
Теги:
directory

54 ответа

5897
Лучший ответ
#!/bin/bash

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

полезный однострочный, который даст вам полное имя каталога скрипта независимо от того, откуда он вызывается.

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

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

Этот последний будет работать с любой комбинацией псевдонимов, source, bash -c, символических ссылок и т.д.

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

Кроме того, $CDPATH ошибки $CDPATH и побочные эффекты вывода stderr, если у пользователя есть умный переопределение cd для перенаправления вывода на stderr (включая escape-последовательности, например, при вызове update_terminal_cwd >&2 для Mac). Добавление >/dev/null 2>&1 в конце вашей команды cd позаботится об обеих возможностях.

Чтобы понять, как это работает, попробуйте запустить эту более подробную форму:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

И это напечатает что-то вроде:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
  • 0
    Это также не работает, если оболочка вызывается с помощью явной команды bash , как вам нужно было бы сделать, если вы хотите вызвать с параметром оболочки, как в sh -x myscript.sh
  • 2
    Для последнего обновления попробуйте следующее: DIR = "$ (cd -P" $ (dirname "$ 0") "&& pwd)" --- это даст вам абсолютный разыменованный путь, то есть разрешит все символические ссылки.
Показать ещё 60 комментариев
686

Используйте dirname "$ 0" :

<Предварительно > <код > #!/Bin/Баш echo "У вас запущен script basename` basename "$ 0" `, dirname` dirname '$ 0 "` "echo" Настоящий рабочий каталог -` pwd` " Код >

использование pwd не будет работать, если вы не используете script из каталога, в котором он содержится.

  [matt @server1 ~] $pwd
/Главная/матовый
[matt @server1 ~] $./test2.sh
script, который вы используете, имеет basename test2.sh, dirname.
Настоящий рабочий каталог /home/matt 
[matt @server1 ~] $cd/tmp
[matt @server1 tmp] $~/test2.sh
script, который вы используете, имеет basename test2.sh, dirname/home/matt
Настоящий рабочий каталог /tmp 
Код>
  • 23
    Для переносимости за пределы bash $ 0 не всегда может быть достаточно. Возможно, вам придется заменить «type -p $ 0», чтобы эта работа работала, если команда была найдена в пути.
  • 7
    @Darron: вы можете использовать только type -p если скрипт исполняемый. Это также может открыть тонкую дыру, если скрипт выполняется с использованием bash test2.sh и существует другой скрипт с таким же именем, исполняемый где-то еще.
Показать ещё 14 комментариев
383

Команда dirname является самой простой, просто анализируя путь до имени файла с переменной $0 (script name):

dirname "$0"

Но, как указал матовый b, возвращаемый путь отличается в зависимости от того, как вызывается script. pwd не выполняет эту работу, потому что это только говорит вам, что представляет собой текущий каталог, а не каталог, в котором находится script. Кроме того, если выполняется символическая ссылка на script, вы получите вероятно, относительный) путь туда, где находится ссылка, а не фактический script.

Некоторые другие упомянули команду readlink, но по своему простейшему вы можете использовать:

dirname "$(readlink -f "$0")"

readlink будет разрешать путь script к абсолютному пути из корня файловой системы. Таким образом, любые пути, содержащие одиночные или двойные точки, тильды и/или символические ссылки, будут разрешены для полного пути.

Здесь script, демонстрирующий каждый из них, whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

Запуск этого script в моем домашнем каталоге, используя относительный путь:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

Опять же, но используя полный путь к script:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

Теперь изменяющиеся каталоги:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

И, наконец, используя символическую ссылку для выполнения script:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat
  • 9
    readlink не будет доступен на некоторых платформах при установке по умолчанию. Старайтесь избегать его использования, если можете
  • 30
    будьте осторожны, чтобы все заключить в кавычки, чтобы избежать проблем с export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" : export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
Показать ещё 7 комментариев
179
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd 'dirname "$SCRIPT_PATH"'; 
  SCRIPT_PATH='readlink "${SCRIPT_PATH}"'; done
fi
cd 'dirname ${SCRIPT_PATH}' > /dev/null
SCRIPT_PATH='pwd';
popd  > /dev/null

Работает для всех версий, включая

  • при вызове через мягкую ссылку с множеством глубин,
  • когда файл
  • когда скрипт называется командой " source " . (точка).
  • когда arg $0 изменяется от вызывающего.
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

В качестве альтернативы, если сам скрипт bash является относительной символической ссылкой, вы хотите следовать ей и возвращать полный путь связанного скрипта:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd 'dirname "$SCRIPT_PATH"'; SCRIPT_PATH='readlink "${SCRIPT_PATH}"'; done
fi
cd 'dirname ${SCRIPT_PATH}' > /dev/null
SCRIPT_PATH='pwd';
popd  > /dev/null

SCRIPT_PATH задается полным путем, как бы он ни назывался.
Просто убедитесь, что вы нашли это в начале скрипта.

Этот комментарий и код Copyleft, выбираемая лицензия под GPL2.0 или более поздняя версия или CC-SA 3.0 (CreativeCommons Share Alike) или позже. (c) 2008. Все права защищены. Никаких гарантий. Вы были предупреждены.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656

  • 4
    Ницца! Можно сделать короче, заменив "pushd [...] popd / dev / null" на SCRIPT_PATH = readlink -f $(dirname "${VIRTUAL_ENV}") ;
  • 1
    На данный момент это самая «стабильная» версия, которую я видел. Спасибо!
Показать ещё 15 комментариев
93

Короткий ответ:

`dirname $0`

или (предпочтительно):

$(dirname "$0")
  • 13
    Это не сработает, если вы поставите скрипт. "источник мой / script.sh"
  • 5
    тогда ничего не будет
Показать ещё 4 комментария
89

Вы можете использовать $BASH_SOURCE

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

Обратите внимание, что вам нужно использовать #!/bin/ bash, а не #!/bin/sh, так как его расширение bash

  • 9
    Когда я это сделаю ./foo/script , тогда $(dirname $BASH_SOURCE) будет ./foo .
  • 0
    также работает с источником /. оператор!
64

Это должно сделать это:

DIR=$(dirname "$(readlink -f "$0")")

Работает с символическими ссылками и пробелами в пути. См. Man-страницы для dirname и readlink.

Изменить:

С комментария, похоже, не работает с Mac OS. Я понятия не имею, почему это так. Любые предложения?

  • 5
    с вашим решением, вызывая скрипт как ./script.sh показывает . вместо полного пути к каталогу
  • 3
    В MacOS нет опции -f для readlink. Используйте вместо этого stat . Но все же, это показывает . если вы находитесь в «этом», реж.
Показать ещё 2 комментария
57

pwd можно использовать для поиска текущего рабочего каталога, а dirname для поиска каталога определенного файла (команда, которая была запущена, - $0, поэтому dirname $0 должна предоставить вам каталог текущий script).

Однако dirname дает точно часть каталога имени файла, которая скорее всего будет относиться к текущему рабочему каталогу. Если по какой-то причине ваш script должен изменить каталог, то вывод из dirname становится бессмысленным.

Я предлагаю следующее:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

Таким образом, вы получаете абсолютный, а не относительный каталог.

Так как script будет запущен в отдельном экземпляре bash, нет необходимости восстанавливать рабочий каталог впоследствии, но если вы по какой-либо причине хотите вернуться в свой script, вы можете легко присвойте значение pwd переменной перед изменением каталога для будущего использования.

Хотя просто

cd `dirname $0`

решает конкретный сценарий в вопросе, я считаю, что абсолютный путь к более полезному обычно.

  • 8
    Вы можете сделать все это в одной строке следующим образом: DIRECTORY = $ (cd dirname $0 && pwd)
  • 0
    Это не работает, если скрипт использует другой скрипт, и вы хотите знать имя последнего.
33

Я не думаю, что это так же просто, как другие сделали это. pwd не работает, так как текущий каталог не обязательно является каталогом с script. $0 также не всегда содержит информацию. Рассмотрим следующие три способа вызова script.

./script

/usr/bin/script

script

В первом и третьем способах $0 не имеет полной информации о пути. Во втором и третьем случае pwd не работает. Единственный способ получить dir в третьем способе - пропустить путь и найти файл с правильным совпадением. В принципе, код должен будет повторить то, что делает ОС.

Один из способов сделать то, что вы просите, - просто скопировать данные в каталог /usr/share и указать его полным путем. В любом случае данные не будут находиться в каталоге /usr/bin, так что, вероятно, это нужно сделать.

  • 9
    Если вы намерены опровергнуть его комментарий, ДОКАЗАТЬ, что скрипт МОЖЕТ получить доступ к тому месту, где он хранится, с примером кода.
31
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
  • 0
    Это намного короче выбранного ответа. И, кажется, работает так же хорошо. Это заслуживает 1000 голосов, чтобы люди не упустили это из виду.
  • 1
    Как подробно объясняют многие из предыдущих ответов, ни $0 ни pwd не гарантируют правильную информацию в зависимости от того, как вызывается скрипт.
27

Получает текущий рабочий каталог в Mac OS X 10.6.6:

DIR=$(cd "$(dirname "$0")"; pwd)
25
$(dirname "$(readlink -f "$BASH_SOURCE")")
25

Это конкретный Linux, но вы можете использовать:

SELF=$(readlink /proc/$$/fd/255)
  • 1
    Это также специфично для bash, но, возможно, поведение bash изменилось? /proc/fd/$$/255 указывает на tty, а не на каталог. Например, в моей текущей оболочке входа в систему дескрипторы файлов 0, 1, 2 и 255 ссылаются на /dev/pts/4 . В любом случае, в руководстве по bash не упоминается fd 255, поэтому, вероятно, неразумно зависеть от этого поведения. \
  • 2
    Интерактивная оболочка! = Скрипт. В любом случае realpath ${BASH_SOURCE[0]}; Казалось бы, лучший путь.
21

Вот однострочный совместимый с POSIX:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH
  • 3
    Я добился успеха с этим, когда запускал скрипт сам или с помощью sudo, но не при вызове source ./script.sh
  • 0
    И происходит сбой, когда cd настроен для печати нового пути.
16

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

Также многие полагают, что вы используете script из оболочки, поэтому забудьте, когда вы откроете новый script по умолчанию ваш дом.

Попробуйте этот каталог для размера:

/var/No one/Мысль/О пространствах Бытие/В каталоге/Имя/И здесь ваш файл .text

Это становится правильным, независимо от того, как и где вы его запускаете.

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

Итак, чтобы сделать это действительно полезным здесь, как перейти в каталог запуска script:

cd "`dirname "$0"`"

Надеюсь, что поможет

  • 3
    Не работает, если скрипт получен из другого скрипта.
15

Вот простой, правильный способ:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

Пояснение:

  • ${BASH_SOURCE[0]} - полный путь к script. Значение этого параметра будет правильным, даже если источник script будет получен, например. source <(echo 'echo $0') печатает bash, а при замене его ${BASH_SOURCE[0]} будет напечатан полный путь к script. (Конечно, это предполагает, что вы в порядке, принимая зависимость от Bash.)

  • readlink -f - Рекурсивно разрешает любые символические ссылки по указанному пути. Это расширение GNU и недоступно для (например) BSD-систем. Если вы используете Mac, вы можете использовать Homebrew для установки GNU coreutils и вытеснить это с помощью greadlink -f.

  • И, конечно, dirname получает родительский каталог пути.

  • 0
    SCRIPT_DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
15

Я бы использовал что-то вроде этого:

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi
  • 0
    Это настоящий! Работает и с простым sh ! Проблема с простыми решениями на основе dirname "$0" : если скрипт находится в $PATH и вызывается без пути, он даст неверный результат.
  • 0
    @ Notinlist Не так. Если скрипт найден через PATH , $0 будет содержать абсолютное имя файла. Если скрипт вызывается с относительным или абсолютным именем файла, содержащим / , $0 будет содержать это.
14

Небольшой пересмотр решения e-satis и 3bcdnlklvc04a указал в их ответ

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}    

Это должно работать во всех случаях, которые они перечисляли.

РЕДАКТИРОВАТЬ: предотвратить появление popd после неудачного pushd, благодаря konsolebox

  • 0
    Это прекрасно работает, чтобы получить «настоящее» имя, а не просто символическую ссылку. Спасибо!
  • 1
    Лучше SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
Показать ещё 2 комментария
13
#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`
11

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

  • Абсолютные пути или относительные пути
  • Софт для файлов и каталогов
  • Вызов script, bash script, bash -c script, source script или . script
  • Пробелы, вкладки, новые строки, юникод и т.д. в каталогах и/или имени файла
  • Имена файлов, начинающиеся с дефиса

Если вы работаете в Linux, кажется, что использование дескриптора proc - лучшее решение для поиска полностью разрешенного источника текущего script (в интерактивном сеансе ссылка указывает на соответствующий /dev/pts/X):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

У этого есть немного уродства, но исправление компактно и легко понять. Мы не используем только примитивы bash, но я в порядке, потому что readlink значительно упрощает задачу. echo X добавляет X в конец строки переменной, чтобы никакие конечные пробелы в имени файла не ели, а замена параметра ${VAR%X} в конце строки избавляется от X, Поскольку readlink добавляет новую строку (которая обычно будет использоваться в подстановке команд, если не для нашего предыдущего обмана), мы тоже должны избавиться от нее. Это проще всего выполнить с помощью схемы цитирования $'', которая позволяет использовать escape-последовательности, такие как \n для представления новых строк (это также то, как вы можете легко сделать коварно названные каталоги и файлы).

Вышеупомянутое должно охватывать ваши потребности в поиске текущего script в Linux, но если у вас нет файловой системы proc в вашем распоряжении или если вы пытаетесь найти полностью разрешенный путь некоторых другой файл, то, может быть, вы найдете приведенный ниже код полезным. Это лишь небольшая модификация от вышеупомянутого однострочного. Если вы играете со странным каталогом/именами файлов, проверка вывода с помощью ls и readlink информативна, так как ls выдаст "упрощенные" пути, заменив ? на такие вещи, как новые строки.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"
  • 0
    Я получаю /dev/pts/30 с bash на рабочем столе Ubuntu 14.10.
  • 0
    @DanDascalescu Использование однострочника? Или полный фрагмент кода внизу? И вы кормили его хитрыми путями?
Показать ещё 3 комментария
11

$_ стоит упомянуть в качестве альтернативы $0. Если вы используете script из bash, принятый ответ можно сократить до:

DIR="$( dirname "$_" )"

Обратите внимание, что это должно быть первое выражение в вашем script.

  • 3
    Это сломается, если вы source или . сценарий. В этих ситуациях $_ будет содержать последний параметр последней команды, которую вы выполнили перед . , $BASH_SOURCE работает каждый раз.
10

Для систем, имеющих GNU coreutils readlink (например, linux):

$(readlink -f "$(dirname "$0")")

Нет необходимости использовать BASH_SOURCE когда $0 содержит имя файла сценария.

  • 1
    если сценарий не был получен. или 'source', в этом случае это будет все равно какой сценарий получен, или, если из командной строки, '-bash' (tty login) или 'bash' (вызывается через 'bash -l') или '/ bin / bash '(вызывается как интерактивная оболочка без авторизации)
  • 0
    Я добавил вторую пару цитат вокруг вызова dirname . Необходим, если путь к каталогу содержит пробелы.
8

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

Вот простой сценарий:

DIR=$(dirname "${BASH_SOURCE[0]}")  # get the directory name
DIR=$(realpath "${DIR}")    # resolve its full path if need be
  • 0
    realpath исходит из GNU coreutils , но кто-нибудь знает, насколько он распространен среди других дисков?
8

Итак... Я считаю, что у меня есть этот. Поздно к вечеринке, но я думаю, что некоторые из них оценят, что именно здесь они сталкиваются с этой нитью. Комментарии должны быть объяснены.

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi
8

Попробуйте использовать:

real=$(realpath $(dirname $0))
  • 0
    Все, что я хочу знать, - почему этот путь не годится? Это казалось не плохим и правильным для меня. Может ли кто-нибудь объяснить, почему за него проголосовали?
  • 6
    realpath не является стандартной утилитой.
Показать ещё 4 комментария
7

Попробуйте следующее кросс-совместимое решение:

CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"

как realpath или readlink не всегда доступны (в зависимости от операционной системы), а ${BASH_SOURCE[0]} доступен только в оболочке bash.

В качестве альтернативы вы можете попробовать следующую функцию в bash:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

Эта функция принимает 1 аргумент. Если аргумент уже имеет абсолютный путь, напечатайте его как есть, в противном случае напечатайте $PWD variable + имя файла (без ./ префикса).

по теме:

  • 0
    Пожалуйста, объясните больше о функции realpath.
  • 1
    Функция @Chris realpath принимает 1 аргумент. Если аргумент уже имеет абсолютный путь, выведите его как есть, в противном случае выведите $PWD + filename (без префикса ./ ).
Показать ещё 1 комментарий
7

Хмм, если в basename и dirname пути просто не будет сокращаться и ходить по пути тяжело (что, если родитель не экспортировал PATH!). Однако оболочка должна иметь открытую ручку для своего script, а в bash дескриптор # 255.

SELF=`readlink /proc/$$/fd/255`

работает для меня.

  • 0
    Я получаю /dev/pts/30 с bash на Ubuntu 14.10 Desktop, вместо того, чтобы каталог, из которого я запускаю скрипт.
6

Это работает в bash -3.2:

path="$( dirname "$( which "$0" )" )"

Вот пример его использования:

Скажем, у вас есть каталог ~/bin, который находится в $PATH. У вас есть script A внутри этого каталога. Это источник s script ~/bin/lib/B. Вы знаете, где включен script относительно исходного (подкаталог lib), но не там, где он относится к текущему каталогу пользователя.

Это решается следующим (внутри A):

source "$( dirname "$( which "$0" )" )/lib/B"

Не имеет значения, где пользователь или как он называет script, это всегда будет работать.

  • 2
    Точка на which очень спорно. type , hash и другие встроенные функции делают то же самое лучше в bash. which вроде более переносимо, хотя на самом деле это не то же самое, which используется в других оболочках, таких как tcsh, в которых он встроен.
  • 0
    "Всегда"? Не за что. which является внешним инструментом, у вас нет оснований полагать, что он ведет себя идентично родительской оболочке.
5

Лучшим компактным решением, на мой взгляд, будет:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

Нет никакой зависимости ни от чего, кроме Bash. Использование dirname, readlink и basename в конечном итоге приведет к проблемам совместимости, поэтому их лучше всего избегать, если это вообще возможно.

  • 1
    Вы, вероятно, должны добавить косую черту к этому: "$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )" . У вас будут проблемы с корневым каталогом, если вы этого не сделаете. Кроме того, почему вы даже должны использовать эхо?
  • 0
    dirname и basename стандартизированы POSIX, так почему бы не использовать их? Ссылки: dirname , basename
5

Ни один из них не работал для bash script, запущенного Finder в OS X - я закончил использование:

SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
sed s:.*/Volumes/:/Volumes/:`"

Не очень, но он выполняет свою работу.

5

Это единственный способ, которым я убедился в достоверности:

SCRIPT_DIR=$(dirname $(cd "$(dirname "$BASH_SOURCE")"; pwd))
  • 2
    это дает мне каталог с последней удаленной записью, то есть путь к контейнеру контейнера скрипта.
4

Подводя итог многим ответам:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

Используемые команды

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

Выход:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

См https://pastebin.com/J8KjxrPF

4

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

thisScriptPath=`realpath $0`
thisDirPath=`dirname $thisScriptPath`
echo $thisDirPath
  • 2
    Обратите внимание, что нет необходимости прибегать к эхо. Просто вызов dirname выведет имя каталога. И, в этом случае, вызов echo без правильного цитирования переменной потенциально приведет к другому выводу, поскольку он будет заполнять пробелы. В целом, чище просто написать dirname "$(realpath "$0")"
  • 0
    Вы правы, что это можно сжать в одну строку. Разбивка на отдельные переменные предназначена для иллюстративных / самодокументированных целей. Линия эха только там, как POC
2

$0 не является надежным способом получения текущего пути script. Например, это мой .xprofile:

#!/bin/bash
echo "$0 $1 $2"
echo "${BASH_SOURCE[0]}"
# $dir/my_script.sh &

cd/tmp && ~/.xprofile && & source ~/.xprofile

/home/puchuu/.xprofile  
/home/puchuu/.xprofile
-bash  
/home/puchuu/.xprofile

Поэтому используйте BASH_SOURCE вместо этого.

2

Используйте комбинацию readlink для канонизации имени (с бонусом после его возврата к исходному, если это символическая ссылка) и dirname для извлечения имени каталога:

script="`readlink -f "${BASH_SOURCE[0]}"`"
dir="`dirname "$script"`"
1

ЕСЛИ ВАШ ОБЩИЙ СЦЕНАРИЙ - СИМЛИНК, тогда это способ сделать это

#!/usr/bin/env bash

dirn="$(dirname "$0")"
rl="$(readlink "$0")";
exec_dir="$(dirname $(dirname "$rl"))";
my_path="$dirn/$exec_dir";
X="$(cd $(dirname ${my_path}) && pwd)/$(basename ${my_path})"

X - это каталог, содержащий ваш скрипт bash (исходный файл, а не символическую ссылку). Я клянусь Богом, что это работает, и это единственный способ, которым я знаю это правильно.

0

Еще один вариант похвалить все другие отличные ответы

$ (cd "$ (dirname" $ {BASH_SOURCE [0]} ")"; pwd) "

  • 0
    Это не имеет функционального преимущества перед ответом alanwj, который является более надежным и был опубликован задолго до этого. В принятом сообществе ответе на вики также уже есть это решение.
0

Вот как я работаю над своими скриптами: pathvar = "$ (cd" $(dirname $0) "& & pwd)" Это скажет вам, из какого каталога запускается Launcher (текущий script).

0

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

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

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

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

Здесь a script, чтобы попробовать и сравнить его с другими предлагаемыми решениями. Вызовите его как 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 [$ я + 1]}.

имя_функции

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

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

[Источник: Bash manual]

0

Вот отрывок из моего ответа на shell script: проверить имя каталога и преобразовать в нижний регистр, в котором я демонстрирую не только то, как решить эту проблему с очень простой Утилиты, указанные в POSIX, я также адресую , как очень просто сохранить результаты функции в возвращаемой переменной...

... Ну, как вы можете видеть, с некоторой помощью, я натолкнулся на довольно простое и очень мощное решение:

Я могу передать функцию своего рода переменной мессенджера и разыменовать любое явное использование результирующего аргумента функции $1 name с eval по мере необходимости, а после завершения процедуры функции я использую eval и обратную косую черту цитируя трюк, чтобы присвоить переменной моего мессенджера значение, которое я хочу, даже не зная его имени.

В полном раскрытии,... [Я нашел переменную часть сообщения этого] и Rich sh tricks, и я также выложил соответствующую часть его страницы ниже моего собственного отрывка ответа.

... ВЫПИСКА: ...

Хотя не строго POSIX, realpath является основным приложением GNU с 2012 года. Полное раскрытие: никогда не слышал об этом, прежде чем я заметил его в TOC info coreutils и сразу подумал о [связанном] вопросе, но используя следующую функцию, как показано, должен быть надежно (скоро POSIXLY?) И, надеюсь, эффективно предоставить своему вызывающему абоненту абсолютно исходный $0:

% _abs_0() { 
> o1="${1%%/*}"; ${o1:="${1}"}; ${o1:=`realpath -s "${1}"`}; eval "$1=\${o1}"; 
> }  
% _abs_0 ${abs0:="${0}"} ; printf %s\\n "${abs0}"
/no/more/dots/in/your/path2.sh

EDIT: Возможно, стоит отметить, что в этом решении используется расширение параметра POSIX, чтобы сначала проверить, на самом деле требуется расширение и разрешение вообще, прежде чем пытаться это сделать. Это должно вернуть абсолютно sourced $0 через переменную messenger (с заметным исключением, что она сохранит symlinks) как эффективно, как я мог себе представить, что это можно сделать , является ли путь уже абсолютным. ...

( незначительное редактирование: перед тем, как найти realpath в документах, я по крайней мере уменьшил мою версию [версии ниже], чтобы не зависеть от поля времени [как это было в первая команда ps], но, честное предупреждение, после тестирования некоторые из них менее убедительны. ps полностью надежен в своем расширении пропускной способности)

С другой стороны, вы можете сделать это:

ps ww -fp $$ | grep -Eo '/[^:]*'"${0#*/}"

eval "abs0=${`ps ww -fp $$ | grep -Eo ' /'`#?}"

... И от Rich sh tricks: ...

Возвращаемые строки из функции оболочки

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

Попробуйте следующее:

func () {
body here
eval "$1=\${foo}"
}

Конечно, ${foo} можно заменить любой подстановкой. Ключевым трюком здесь является линия eval и использование экранирования. "$1" расширяется, когда аргумент eval создается главным парсером команды. Но "${foo}" не расширяется на этом этапе, поскольку цитируется "$". Вместо этого его расширение, когда eval оценивает его аргумент. Если неясно, почему это важно, подумайте о том, как это будет плохо:

foo='hello ; rm -rf /'
dest=bar
eval "$dest=$foo"

Но, конечно, следующая версия совершенно безопасна:

foo='hello ; rm -rf /'
dest=bar
eval "$dest=\$foo"

Обратите внимание, что в исходном примере "$1" использовался, чтобы позволить вызывающему лицу передать имя целевой переменной в качестве аргумента функции. Если вашей функции необходимо использовать команду shift, например, чтобы обрабатывать оставшиеся аргументы как "$@", тогда может быть полезно сохранить значение "$1" во временной переменной в начале функции.

0

Я обычно делаю:

LIBDIR=$(dirname "$(readlink -f "$(type -P $0 || echo $0)")")
source $LIBDIR/lib.sh
-1

Основываясь на этом ответе, я предлагаю уточненную версию, которая получает SCRIPT_HOME как содержащую папку любого текущего запуска bash script

s=${BASH_SOURCE[0]} ; s=`dirname $s` ; SCRIPT_HOME=`cd $s ; pwd`
echo $SCRIPT_HOME
-1

Посмотрите на тест внизу со странными именами каталогов.

Чтобы изменить рабочий каталог на тот, где находится Bash script, вы должны попробовать этот простой, проверенный и проверить с помощью shellcheck:

#!/bin/bash --
cd "$(dirname "${0}")"/. || exit 2

Тест:

$ ls 
application
$ mkdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47testdir" "")"
$ mv application *testdir
$ ln -s *testdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47symlink" "")"
$ ls -lb
total 4
lrwxrwxrwx 1 jay stacko   46 Mar 30 20:44 \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'symlink -> \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'testdir
drwxr-xr-x 2 jay stacko 4096 Mar 30 20:44 \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'testdir
$ *testdir/application && printf "SUCCESS\n" ""
SUCCESS
$ *symlink/application && printf "SUCCESS\n" ""
SUCCESS
-1

Очень поздно обсуждать, но попробуйте что-то вроде этого:

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

}

function get_dirname(){

local realpath="$(get_realpath "$1")"
if (( $? )) # true when non-zero.
then
    return $? # failure
fi
echo "${realpath%/*}"
return 0 # success

}

# Then from the top level:
get_dirname './script.sh'

# Or Within a script:
get_dirname "$0"

# Can even test the outcome!
if (( $? )) # true when non-zero.
then
    exit 1 # failure
fi

Эти функции и связанные с ними инструменты являются частью нашего продукта, который был бесплатно предоставлен сообществу и может быть найден в GitHub как realpath-lib. Он прост, чист и хорошо документирован (отлично подходит для обучения), чистый Bash и не имеет зависимостей. Хорошо подходит и для кросс-платформенного использования. Итак, для приведенного выше примера, в script вы могли бы просто:

source '/path/to/realpath-lib'

get_dirname "$0"

if (( $? )) # true when non-zero.
then
    exit 1 # failure
fi

Что все!

-1

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

[ "$(dirname $0)" = '.' ] && SOURCE_DIR=$(pwd) || SOURCE_DIR=$(dirname $0);
ls -l $0 | grep -q ^l && SOURCE_DIR=$(ls -l $0 | awk '{print $NF}');

Первая строка назначает источник на основе значения pwd, если он запускается из текущего пути или dirname, если вызывается из другого места.

Вторая строка рассматривает путь, чтобы увидеть, является ли она символической ссылкой, и если это так, обновляет SOURCE_DIR до местоположения самой ссылки.

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

-1

Я хочу убедиться, что script запущен в своем каталоге. Так

cd $(dirname $(which $0) )

После этого, если вы действительно хотите знать, где вы работаете, выполните следующую команду.

DIR=$(/usr/bin/pwd)
  • 4
    Чем это отличается от других ответов?
-2

Выбранный ответ работает очень хорошо. Я публикую свое решение для тех, кто ищет более короткие альтернативы, которые все еще касаются поиска, выполнения, полных путей, относительных путей и символических ссылок. Наконец, это будет работать на MacOS, учитывая, что нельзя предполагать, что доступна версия readlink для GNU coreutils.

Суть в том, что он не использует Bash, но его легко использовать в сценарии Bash. Хотя OP не накладывает никаких ограничений на язык решения, вероятно, лучше всего, чтобы большинство оставалось в мире Bash. Это просто альтернатива, и, возможно, непопулярная.

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

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"
  • 0
    В этом вопросе ничего не говорилось о MacOS. Я думаю, что этот ответ должен начинаться с «Другой подход - использовать PHP вместо того, чтобы полагаться на BASH». Потому что это раскрывается только в конце ответа.
-2

(Примечание: этот ответ прошел через множество изменений, поскольку я улучшил оригинал. По состоянию на последнюю ревизию никто еще не прокомментировал или не проголосовал.)

Я добавляю этот ответ как можно больше для моей собственной выгоды - помнить об этом и собирать комментарии - как для кого-то другого. Ключевая часть ответа заключается в том, что я уменьшаю объем проблемы: я запрещаю косвенное выполнение script по пути (как в пути /bin/sh [script относительно компонента пути]). Это можно обнаружить, потому что $0 будет относительным путем, который не разрешает какой-либо файл относительно текущей папки. Я считаю, что прямое выполнение с использованием "#!" механизм всегда приводит к абсолютному $0, в том числе, когда script находится на пути. Я также требую, чтобы имя пути и любые пути по цепочке символических ссылок содержали только разумное подмножество символов, в частности не '\n', ' > ', '*' или '?'. Это необходимо для логики синтаксического анализа. Есть еще несколько неявных ожиданий, которые я не буду вдаваться (см. Предыдущий ответ < 1 > ), и я не пытаюсь обрабатывать преднамеренные саботажа в размере 0 долларов США (так что учитывайте любые последствия для безопасности). Я ожидаю, что это будет работать практически на любой Unix-подобной системе с Bourne-like/bin/sh.

Комментарии и предложения приветствуются!

< 1 > https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself

#!/bin/sh
(
    path="${0}"
    while test -n "${path}"; do
        # Make sure we have at least one slash and no leading dash.
        expr "${path}" : / > /dev/null || path="./${path}"
        # Filter out bad characters in the path name.
        expr "${path}" : ".*[*?<>\\]" > /dev/null && exit 1
        # Catch embedded new-lines and non-existing (or path-relative) files.
        # $0 should always be absolute when scripts are invoked through "#!".
        test "`ls -l -d "${path}" 2> /dev/null | wc -l`" -eq 1 || exit 1
        # Change to the folder containing the file to resolve relative links.
        folder=`expr "${path}" : "\(.*/\)[^/][^/]*/*$"` || exit 1
        path=`expr "x\`ls -l -d "${path}"\`" : "[^>]* -> \(.*\)"`
        cd "${folder}"
        # If the last path was not a link then we are in the target folder.
        test -n "${path}" || pwd
    done
)
-2

Нет вил (кроме подсели) и может обрабатывать формы "чужие" пути, такие как символы с символами новой строки, как некоторые утверждают:

IFS= read -rd '' DIR < <([[ $BASH_SOURCE != */* ]] || cd "${BASH_SOURCE%/*}/" >&- && echo -n "$PWD")
-2
cur_dir=`old=\`pwd\`; cd \`dirname $0\`; echo \`pwd\`; cd $old;`
-2
function getScriptAbsoluteDir { # fold>>
    # @description used to get the script path
    # @param $1 the script $0 parameter
    local script_invoke_path="$1"
    local cwd=`pwd`

    # absolute path ? if so, the first character is a /
    if test "x${script_invoke_path:0:1}" = 'x/'
    then
        RESULT=`dirname "$script_invoke_path"`
    else
        RESULT=`dirname "$cwd/$script_invoke_path"`
    fi
} # <<fold
  • 0
    извините, я немного не getScriptAbsoluteDir с bash-скриптом, могу ли я вызвать эту функцию, просто набрав getScriptAbsoluteDir или local currdir='getScriptAbsoluteDir' ?
  • 3
    -1: ключевое слово function недоступно в оболочках POSIX и является излишне несовместимым bash / ksh / & c. расширение. Кроме того, если у вас достаточно новая оболочка, чтобы иметь это расширение, вам не нужен тестовый хак "x [...]".
Показать ещё 1 комментарий
-2
ME=`type -p $0`
MDIR="${ME%/*}"
WORK_DIR=$(cd $MDIR && pwd)
  • 0
    Дает неверные результаты, если скрипт вызывается из его каталога с помощью bash script.sh .
-4

Этот однострочный файл работает на CYGWIN, даже если script был вызван из Windows с помощью bash -c <script>:

set mydir="$(cygpath "$(dirname "$0")")"
-7
FOLDERNAME=${PWD##*/}

это самый быстрый способ, который я знаю

  • 3
    Это просто начинается с pwd и не возвращает путь, по которому находится текущий исполняемый скрипт.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню