Как установить текущий рабочий каталог в каталог скрипта?

569

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

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

jameshfisher
источник
Вы не задумывались над тем, чтобы поместить скрипт-обертку где-то вроде / usr / bin для cd в (жестко запрограммированный) правильный каталог, а затем выполнить свой скрипт?
Дагг Наббит
2
Зачем вам нужен каталог скрипта? Вероятно, есть лучший способ решить основную проблему.
bstpierre
12
Я просто хотел бы отметить, что поведение, которое вы называете «явно нежелательным», на самом деле совершенно необходимо - если я буду работать, myscript path/to/fileя ожидаю, что скрипт оценит путь / к / файлу относительно моего текущего каталога, а не того каталога, в котором происходит скрипт находиться в. Кроме того, что бы вы сделали, если бы скрипт запускался, ssh remotehost bash < ./myscriptкак упоминается в BASH FAQ?
Гордон Дэвиссон
3
cd "${BASH_SOURCE%/*}" || exit
каркать

Ответы:

656
#!/bin/bash
cd "$(dirname "$0")"
ndim
источник
7
dirname возвращает '.' при использовании bash под Windows. Итак, ответ Павла лучше.
Тварох
7
Также возвращает «.» в Mac OSX
Бен Клейтон,
4
Стоит отметить, что вещи могут сломаться, если символическая ссылка составляет часть $0. В вашем скрипте вы можете ожидать, например, ../../ссылки на каталог на два уровня выше местоположения скрипта, но это не обязательно так, если символические ссылки находятся в игре.
Брайан Гордон
20
Если вы назвали сценарий как ./script, .это правильный каталог, и его изменение .также приведет к тому, что каталог scriptнаходится, то есть в текущем рабочем каталоге.
ndim
6
Если вы запустите скрипт из текущей директории, как это bash script.sh, то значение $0is script.sh. Единственный способ, которым cdкоманда будет «работать» для вас, это то, что вам не нужны неудачные команды. Если бы вы использовали set -o errexit(aka:), set -eчтобы убедиться, что ваш сценарий не проходит мимо неудачных команд, это НЕ будет работать, потому что cd script.shэто ошибка. Надежный [специфичный для bash] способcd "$(dirname ${BASH_SOURCE[0]})"
Бруно Броноски
322

Следующее также работает:

cd "${0%/*}"

Синтаксис подробно описан в этом ответе StackOverflow.

Пол Шульц
источник
17
Объяснение того, как это работает: stackoverflow.com/questions/6393551/…
kenorb
25
Не забудьте заключить в кавычки, если путь содержит пробелы. то есть cd "$ {0% / *}"
H.Rabiee
17
Это не удастся, если скрипт вызывается с bash script.sh- $0просто будет имя файла
Крис Уоттс
17
Я бы сказал, что этот ответ слишком лаконичен. Вы вряд ли узнаете что-нибудь о синтаксисе. Некоторое описание о том, как прочитать это было бы полезно.
fraxture
2
Этот трюк тоже не порождает подоболочки, поэтому хорош для скоростных фанатов.
Текнопаул
184

Попробуйте следующие простые однострочники:


Для всех UNIX / OSX / Linux

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

удар

dir=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)

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

Примечание: в Bash используйте ${BASH_SOURCE[0]}в пользу $0, иначе путь может прерваться при поиске ( source/ .).


Для Linux, Mac и других * BSD:

cd "$(dirname "$(realpath "$0")")";

Примечание: realpathдолжен быть установлен в самом популярном дистрибутиве Linux по умолчанию (например, Ubuntu), но в некоторых он может отсутствовать, поэтому вы должны установить его.

Примечание: если вы используете Bash, используйте ${BASH_SOURCE[0]}в пользу $0, иначе путь может прерваться при поиске ( source/ .).

В противном случае вы можете попробовать что-то подобное (он будет использовать первый существующий инструмент):

cd "$(dirname "$(readlink -f "$0" || realpath "$0")")"

Для Linux:

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

Использование GNU readlink на * BSD / Mac:

cd "$(dirname "$(greadlink -f "$0")")"

Примечание: вам нужно coreutilsустановить (например, 1. Установить Homebrew , 2. brew install coreutils).


В баш

В bash вы можете использовать расширения параметров для достижения этой цели, например:

cd "${0%/*}"

но это не работает, если скрипт запускается из той же директории.

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

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

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

или вот версия, взятая из .bashrcфайла Debian :

function realpath()
{
    f=$@
    if [ -d "$f" ]; then
        base=""
        dir="$f"
    else
        base="/$(basename "$f")"
        dir=$(dirname "$f")
    fi
    dir=$(cd "$dir" && /bin/pwd)
    echo "$dir$base"
}

Связанные с:

Смотрите также:

Как я могу получить поведение readlink -f GNU на Mac?

kenorb
источник
4
гораздо лучший ответ, чем популярные, потому что он разрешает системные ссылки и учитывает разные ОС. Спасибо!
Деннис
Спасибо за этот ответ. Верхний работал на моем Mac ... но что делает переключатель - в cpкоманде, @kenorb?
TobyG
3
Двойная тире ( --) используется в командах для обозначения конца параметров команды, поэтому файлы, содержащие тире или другие специальные символы, не нарушают команду. Попробуйте, например, создать файл с помощью touch "-test"и touch -- -test, затем удалите файл с помощью rm "-test"и rm -- -test, и увидите разницу.
Кенорб
1
Если бы я мог, я бы удалил свое upvote: realpath устарел unix.stackexchange.com/questions/136494/…
lsh
1
@lsh Это говорит о том, что только realpathпакет Debian устарел, а не GNU realpath. Если вы думаете, что это не ясно, вы можете предложить изменить.
Кенорб
73
cd "$(dirname "${BASH_SOURCE[0]}")"

Это просто. Оно работает.

Дэн Молдинг
источник
1
Это должен быть принятый ответ. Работает на OSX и Linux Ubuntu.
Дэвид Риссато Крус
5
Это прекрасно работает, когда сценарий B получен из другого сценария A, и вам нужно использовать пути относительно сценария B. Спасибо.
Энрико
5
Это также работает в Cygwin для тех из нас, кому не повезло иметь дело с Windows.
Бруно Броноски
3
Илиcd "${BASH_SOURCE%/*}" || exit
каркать
Работает на macOS Мохаве
Хуан Боэро
6

Принятый ответ хорошо работает для сценариев, которые не были символическими ссылками в других местах, например, в $PATH.

#!/bin/bash
cd "$(dirname "$0")"

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

ln -sv ~/project/script.sh ~/bin/; 
~/bin/script.sh

Это перейдет в ~/bin/каталог, а не в ~/project/каталог, что, вероятно, сломает ваш скрипт, если целью cdявляется включение зависимостей относительно~/project/

Безопасный ответ по символической ссылке ниже:

#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

readlink -f требуется для определения абсолютного пути к файлу, который может содержать символические ссылки.

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

Джеймс Макгиган
источник
4

Этот скрипт, кажется, работает для меня:

#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd

Командная строка pwd отражает местоположение скрипта как текущего рабочего каталога, независимо от того, откуда я его запускаю.

Amardeep AC9MF
источник
3
realpathвряд ли будет установлен везде. Что может не иметь значения, в зависимости от ситуации ОП.
bstpierre
Моя система не имеет, realpathно есть, readlinkчто похоже.
Приостановлено до дальнейшего уведомления.
2
В каком дистрибутиве по умолчанию не установлен realpath? У Ubuntu есть это.
Кенорб
1
cd "`dirname $(readlink -f ${0})`"

источник
Можете ли вы объяснить свой ответ, пожалуйста?
Зулу
1
Хотя этот код может помочь решить проблему, он не объясняет, почему и / или как он отвечает на вопрос. Предоставление этого дополнительного контекста значительно улучшило бы его долгосрочную образовательную ценность. Пожалуйста, измените свой ответ, чтобы добавить объяснение, в том числе, какие ограничения и предположения применяются.
Тоби Спейт
-5
echo $PWD

PWD - это переменная окружения.

Маверик Холдем
источник
5
Это дает только каталог, из которого вызывается, а не каталог, в котором находится скрипт.
17
-6

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

$ vim test

#!/bin/bash
pwd
:wq to save the test file.

Дать разрешение на выполнение:

chmod u+x test

Затем выполните сценарий, ./testпосле чего вы сможете увидеть текущий рабочий каталог.

Chandan
источник
2
Вопрос заключался в том, как обеспечить выполнение сценария в своем собственном каталоге, в том числе, если это не рабочий каталог. Так что это не ответ. В любом случае, зачем делать это вместо того, чтобы просто ... бежать pwd? Похоже, много потерянных нажатий клавиш для меня.
underscore_d