Як встановити поточний робочий каталог в каталог сценарію?


569

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

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


Чи обмірковували ви розміщувати скрипт для обгортки десь на зразок / usr / bin, щоб ввести його у відповідний каталог (hardcoded), а потім виконати свій скрипт?
— Dagg Nabbit

2
Для чого потрібен каталог сценарію? Можливо, є кращий спосіб вирішити основну проблему.
— bstpierre

12
Я просто хотів би зазначити, що поведінка, яку ви називаєте "очевидно небажаною", насправді цілком необхідна - якщо я запускаю, myscript path/to/fileя очікую, що сценарій оцінить шлях / файл / файл відносно мого поточного каталогу, не залежно від каталогу, у якому відбувається сценарій Крім того, що б ви сталися зі сценарієм, з яким запускається ssh remotehost bash < ./myscriptFAQ BASH?
— Гордон Девіссон


3
cd "${BASH_SOURCE%/*}" || exit
— каре

Відповіді:


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

7
dirname повертається "." при використанні bash під Windows. Отже, відповідь Павла краща.
— Tvaroh

7
Також повертається "." в Mac OSX
— Бен Клейтон

4
Варто зазначити, що все може зламатися, якщо частина символічного посилання складається з $0. Наприклад, у вашому сценарії ви можете розраховувати на те, що ../../він посилатиметься на два рівні над розташуванням сценарію, але це не обов'язково, якщо символічні посилання відтворюються.
— Брайан Гордон

20
Якщо ви назвали скрипт як ./script, .- це правильний каталог, і зміна на .нього також виявиться в самому каталозі, де scriptзнаходиться, тобто в поточному робочому каталозі.
— ndim

6
Якщо ви запускаєте скрипт із поточного каталогу так bash script.sh, то значення $0є script.sh. Єдиний спосіб, коли cdкоманда буде "працювати" для вас, це тому, що вам не байдуже невдалі команди. Якби ви використовували set -o errexit(aka:) set -eдля того, щоб ваш скрипт не видав минулі невдалі команди, це НЕ буде працювати, оскільки cd script.shце помилка. Надійний [баш-специфічний] спосіб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просто буде назва файлу
— Chris Watts

17
Я б сказав, що ця відповідь занадто лаконічна. Ви ледве нічого не дізнаєтесь про синтаксис. Деякі описи про те, як це прочитати, було б корисно.
— матч

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"
}

Пов'язані:

Дивись також:

Як я можу отримати поведінку зчитування GNU -f на Mac?


4
набагато краща відповідь, ніж популярні, оскільки вона вирішує систематичні посилання та облік різних ОС. Дякую!
— Денніс

Дякую за цю відповідь. Верхня cpчастина працювала на моєму Mac ... але що робить перемикач у команді @kenorb?
— TobyG

3
Подвійний тире ( --) використовується в командах для позначення кінця параметрів команд, тому файли, що містять тире або інші спеціальні символи, не порушують команду. Спробуйте, наприклад, створити файл через touch "-test"і touch -- -test, а потім видаліть файл через rm "-test"та rm -- -test, і побачите різницю.
— kenorb

1
Я можу зняти свою пропозицію, якщо зможу: realpath застарілий unix.stackexchange.com/questions/136494/…
— lsh

1
@lsh У ньому говориться, що лише realpathпакет Debian застарілий, а не GNU realpath. Якщо ви вважаєте, що це не зрозуміло, ви можете запропонувати змінити.
— kenorb

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

Це легко. Це працює.


1
Це має бути прийнятою відповіддю. Працює на OSX та Linux Ubuntu.
— Девід Ріссато Крус

5
Це чудово працює, коли сценарій B отримується з іншого сценарію A і вам потрібно використовувати шляхи відносно сценарію B. Дякую.
— Енріко

5
Це також працює в Cygwin для тих, хто з нас невдалий, щоб торкнутися Windows.
— Бруно Броноський

3
Або cd "${BASH_SOURCE%/*}" || exit
— каре

Працює над macOS Mojave
— Juan Boero

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 повторює розташування скрипту як поточного робочого каталогу, незалежно від того, звідки я його запускаю.


3
realpathнавряд чи буде встановлений скрізь. Що може не мати значення, залежно від ситуації з ОП.
— bstpierre

У моїй системі немає, realpathале вона має, readlinkяка, схоже, схожа.
— Призупинено до подальшого повідомлення.

2
У котрому dist не встановлено realpath за замовчуванням? Ubuntu має його.
— kenorb


1
cd "`dirname $(readlink -f ${0})`"

Чи можете ви пояснити свою відповідь, будь ласка?
— Зулу

1
Хоча цей код може допомогти вирішити проблему, він не пояснює, чому та / або як він відповідає на питання. Забезпечення цього додаткового контексту значно покращило б його довгострокове навчальне значення. Будь ласка, відредагуйте свою відповідь, щоб додати пояснення, включаючи, які обмеження та припущення застосовуються.
— Toby Speight


-6

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

$ vim test

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

Надати дозвіл на виконання:

chmod u+x test

Потім виконайте сценарій, ./testтоді ви зможете побачити поточну робочу директорію.


2
Питання полягало у тому, як забезпечити запуск сценарію у власному каталозі, у тому числі якщо це не робочий каталог. Тож це не відповідь. У будь-якому випадку, навіщо робити це замість просто ... бігу pwd? Мені здається, багато мені марно натискає клавішу.
— підкреслюйте_d
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.