текущий - bash путь к файлу




Получение исходного каталога сценария Bash изнутри (20)

Вот один из лайнеров, совместимый с POSIX:

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

# test
echo $SCRIPT_PATH

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

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

$ ./application

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

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

Объяснение:

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

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

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


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

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

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


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

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

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

[[email protected] ~]$ pwd
/home/matt
[[email protected] ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[[email protected] ~]$ cd /tmp
[[email protected] tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

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

dirname "$0"

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

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

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

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

Вот сценарий, демонстрирующий каждый из них, 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))"

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

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

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

>>>$ /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

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

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

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

`dirname $0`

или ( preferably ):

$(dirname "$0")

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

real=$(realpath $(dirname $0))

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

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

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

Редактировать:

Из комментария, похоже, не работает с Mac OS. Я понятия не имею, почему это так. Какие-либо предложения?


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

DIR=$(cd "$(dirname "$0")"; pwd)

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

# 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

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

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

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

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

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

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

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

cd "`dirname "$0"`"

надеюсь, это поможет


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

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

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

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

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

Вышеупомянутое должно охватывать ваши потребности в поиске текущего скрипта в 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"

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

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

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

#!/bin/bash

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

echo "Directory is $directory"

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

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

Хотя просто

cd `dirname $0`

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


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

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

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


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

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

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

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

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

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

Связанные с:


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

SCRIPT_DIR=$(dirname $(cd "$(dirname "$BASH_SOURCE")"; pwd))

Хм, если в пути basename & dirname просто не собирается его резать, а идти по пути сложно (что, если родитель не экспортировал PATH!). Однако оболочка должна иметь открытый дескриптор для своего скрипта, а в bash дескриптор - 255.

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

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


#!/bin/sh
PRG="$0"

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

scriptdir=`dirname "$PRG"`

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

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

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

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 && 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 && pwd )"

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

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

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

#!/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" )" && 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'

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )




directory