Unix shell script выяснит, в каком каталоге находится файл скрипта?

345

В основном мне нужно запустить script с помощью путей, связанных с расположением файла оболочки script, как я могу изменить текущий каталог в тот же каталог, где находится файл script?

  • 9
    Это действительно дубликат? Этот вопрос о "сценарии оболочки Unix", другой - о Bash.
  • 2
    @BoltClock: Этот вопрос был неправильно закрыт. Связанный вопрос о Bash. Этот вопрос о программировании оболочки Unix. Обратите внимание, что принятые ответы совершенно разные!
Показать ещё 5 комментариев
Теги:

12 ответов

389
Лучший ответ

В Bash вы должны получить то, что вам нужно вот так:

#!/usr/bin/env bash

BASEDIR=$(dirname "$0")
echo "$BASEDIR"
  • 57
    Это не будет работать, если скрипт находится на вашем пути.
  • 12
    Это не сработает, если вы вызвали скрипт по символической ссылке в другом каталоге. Чтобы это работало, вам также нужно использовать readlink (см. Ответ al ниже)
Показать ещё 15 комментариев
300

Исходное сообщение содержит решение (игнорируйте ответы, они не добавляют ничего полезного). Интересную работу выполняет упомянутая команда unix readlink с опцией -f. Работает, когда script вызывается как абсолютным, так и относительным путем.

Для bash, sh, ksh:

#!/bin/bash 
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$0")
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=$(dirname "$SCRIPT")
echo $SCRIPTPATH

Для tcsh, csh:

#!/bin/tcsh
# Absolute path to this script, e.g. /home/user/bin/foo.csh
set SCRIPT=`readlink -f "$0"`
# Absolute path this script is in, thus /home/user/bin
set SCRIPTPATH=`dirname "$SCRIPT"`
echo $SCRIPTPATH

Смотрите также: https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself

  • 0
    Это лучший способ. Обратите внимание, что проверенный ответ не будет работать, если вы сделаете: ./myscript.sh ... который сообщит, что каталогом является "." - это всегда даст вам абсолютный путь
  • 11
    Примечание. Не во всех системах есть readlink . Вот почему я рекомендовал использовать pushd / popd (встроенные в bash).
Показать ещё 11 комментариев
32

Предполагая, что вы используете bash

#!/bin/bash

current_dir=$(pwd)
script_dir=$(dirname $0)

echo $current_dir
echo $script_dir

Этот script при запуске должен печатать каталог, в котором вы находитесь, а затем каталог script находится, например, при вызове его из /(script находится в /home/mez/ ), он выводит

/
/home/mez

Помните, что при назначении переменных из вывода команды завершите команду в $(и) - или вы не получите желаемый результат. `

  • 2
    Это не будет работать, когда я вызываю скрипт из текущего каталога.
  • 0
    Не работает при запуске из симлинка.
Показать ещё 1 комментарий
27

Если вы используете bash....

#!/bin/bash

pushd $(dirname "${0}") > /dev/null
basedir=$(pwd -L)
# Use "pwd -P" for the path without links. man bash for more info.
popd > /dev/null

echo "${basedir}"
  • 3
    Вы можете заменить pushd / popd на cd $(dirname "${0}") и cd - чтобы он работал на других оболочках, если у них есть pwd -L .
  • 0
    почему вы используете здесь pushd и popd?
Показать ещё 3 комментария
25

Более ранний комментарий к ответу сказал это, но его легко пропустить среди всех других ответов.

При использовании bash:

echo this file: $BASH_SOURCE
echo this dir: `dirname $BASH_SOURCE`

Bash Справочное руководство, 5.2 Bash Переменные

  • 4
    Это то, что я искал. Мне нужно было получить абсолютный путь к исполняемому скрипту.
  • 2
    Только этот работает со сценарием в пути к среде, большинство проголосовавших не работают. благодарю вас!
Показать ещё 3 комментария
12

Как говорит Марко:

BASEDIR=$(dirname $0)
echo $BASEDIR

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

Чтобы обойти эту проблему, используйте:

current_dir=$(pwd)
script_dir=$(dirname $0)

if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi

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

  • 0
    script_dir = $ (dirname $ 0) также может вернуть ..
10
cd $(dirname $(readlink -f $0))
2

Так много ответов, все правдоподобные, каждый из которых имеет pro и con и немного отличается от целей (что, вероятно, должно быть указано для каждого). Здесь другое решение, которое отвечает основной цели как прозрачности, так и работы во всех системах, на всех bash (без предположений о версиях bash или readlink или pwd), и разумно делает то, что вы (например, устранение символических ссылок - интересная проблема, но обычно это не то, что вы на самом деле хотите), обрабатывать крайние случаи, такие как пробелы в путях и т.д., игнорирует любые ошибки и использует разумные умолчания, если есть какие-либо проблемы.

Каждый компонент хранится в отдельной переменной, которую вы можете использовать индивидуально:

# script path, filename, directory
PROG_PATH=${BASH_SOURCE[0]}      # this script name
PROG_NAME=${PROG_PATH##*/}       # basename of script (strip path)
PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"
2

Лучший ответ на этот вопрос был дан здесь:
Получение исходного каталога Bash script изнутри

И есть:

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

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

Чтобы понять, как это работает, вы можете выполнить следующее script:

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

Пусть это POSIX:

a="/$0"; a=${a%/*}; a=${a:-.}; a=${a#/}/; BASEDIR=$(cd $a; pwd)

Протестировано на многих совместимых с Bourne оболочках, включая BSD.

Насколько я знаю, я автор, и я помещаю его в общественное достояние. Для получения дополнительной информации см.: https://www.jasan.tk/posix/2017/05/11/posix_shell_dirname_replacement

  • 1
    как написано, cd: too many arguments если в пути есть пробелы, и возвращает $PWD . (очевидное исправление, но показывает, сколько на самом деле существует крайних случаев)
  • 1
    Был бы upvote. За исключением комментария от @michael, что он терпит неудачу с пробелами в пути ... Есть ли решение для этого?
Показать ещё 7 комментариев
2

Вдохновленный ответ blueyeds

read < <(readlink -f $0 | xargs dirname)
cd $REPLY
-4

Это должно сделать трюк:

echo `pwd`/`dirname $0`

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

  • 1
    Здесь проблема с escape- `pwd`/`dirname $0` stackoverflow: она, безусловно, должна выглядеть следующим образом: `pwd`/`dirname $0` но все равно может не работать с символическими ссылками

Ещё вопросы

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