В основному мені потрібно запустити скрипт із шляхами, пов’язаними з розташуванням файлу скрипта оболонки, як я можу змінити поточний каталог на той самий каталог, де знаходиться файл сценарію?
В основному мені потрібно запустити скрипт із шляхами, пов’язаними з розташуванням файлу скрипта оболонки, як я можу змінити поточний каталог на той самий каталог, де знаходиться файл сценарію?
Відповіді:
У Bash ви повинні отримати те, що вам потрібно так:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
readlink
(див. Відповідь Ал. Нижче)
$BASH_SOURCE
замість цього $0
, оскільки $0
не завжди міститься шлях викликаного сценарію, наприклад, коли "джерело" сценарію.
$BASH_SOURCE
є специфічним для Bash, питання взагалі про скрипт оболонки.
CUR_PATH=$(pwd)
або pwd
повернути поточний каталог (який не повинен бути батьківським сценарієм dir)!
$BASH_SOURCE
, і він повертає те, що мені потрібно. Мій скрипт викликається з іншого сценарію і $0
повертається .
, $BASH_SOURCE
повертаючи правий підкаталог (у моєму випадку scripts
).
Оригінальний пост містить рішення (ігноруйте відповіді, вони не додають нічого корисного). Цікава робота виконується згаданою командою unix readlink
з опцією -f
. Працює, коли сценарій викликається як абсолютним, так і відносним шляхом.
Для баш, ш, кш:
#!/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/a/246128/59087
readlink
. Ось чому я рекомендував використовувати pushd / popd (вбудовані для bash).
-f
зробити readlink
щось інше на OS X (Lion) та, можливо, на BSD. stackoverflow.com/questions/1055671 / ...
-f
взагалі не підтримується в OS X (як у Lion); там ви можете або відмовитись від -f
вирішення, щонайменше, на одному рівні непрямості, наприклад pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")"
, або можете прокатати свій власний рекурсивний скрипт, що слідує за символьними посиланнями, як продемонстровано у зв’язаному дописі.
sh /some/other/directory/script.sh)
, у цьому випадку .
буде ваш pwd, а не/some/other/directory
Попередній коментар до відповіді сказав це, але легко пропустити серед усіх інших відповідей.
При використанні bash:
echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"
dirname "$BASH_SOURCE"
Натомість слід використовувати для обробки пробілів у $ BASH_SOURCE.
"$(dirname "${BASH_SOURCE[0]}")"
Якщо припустити, що ви використовуєте bash
#!/bin/bash
current_dir=$(pwd)
script_dir=$(dirname $0)
echo $current_dir
echo $script_dir
Цей скрипт повинен надрукувати каталог, у якому ви перебуваєте, а потім каталог, у якому знаходиться скрипт. Наприклад, при виклику з нього зі /
скриптом у /home/mez/
ньому виводиться
/
/home/mez
Пам'ятайте, що при призначенні змінних з виводу команди введіть команду $(
і )
- або ви не отримаєте потрібного виводу.
Якщо ви використовуєте 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}"
pushd
/ popd
на cd $(dirname "${0}")
і cd -
змусити його працювати на інших оболонках, якщо вони мають pwd -L
.
Як пропонує Марко:
BASEDIR=$(dirname $0)
echo $BASEDIR
Це працює, якщо ви не виконаєте скрипт з того самого каталогу, де знаходиться скрипт, і в цьому випадку ви отримаєте значення "."
Щоб обійти цю проблему, використовуйте:
current_dir=$(pwd)
script_dir=$(dirname $0)
if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi
Тепер ви можете використовувати змінну current_dir у всьому сценарії, щоб звернутися до каталогу сценаріїв. Однак це все ще може мати проблему символьного посилання.
Найкраща відповідь на це питання відповіла тут:
Отримання вихідного каталогу сценарію Bash зсередини
І це:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
Однокласник, який дасть вам повне ім'я каталогу скрипта, незалежно від того, звідки він викликається.
Щоб зрозуміти, як це працює, ви можете виконати такий сценарій:
#!/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'"
Давайте зробимо це POSIX oneliner:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
Тестували на багатьох сумісних з Борном оболонках, включаючи BSD.
Наскільки я знаю, я є автором, і я розміщую його у відкритому доступі. Для отримання додаткової інформації дивіться: https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/
cd: too many arguments
якщо пробіли в шляху, і повертається $PWD
. (очевидно виправлення, але тільки показує, скільки крайових справ є насправді)
Якщо ви хочете отримати фактичний каталог скриптів (незалежно від того, ви викликаєте сценарій за допомогою символьного посилання чи безпосередньо), спробуйте:
BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"
Це працює як на Linux, так і на macOS. Я не міг бачити, щоб хтось тут згадував realpath
. Не впевнений, чи є у цього підходу якісь недоліки.
на macOS вам потрібно встановити, coreutils
щоб використовувати realpath
. Наприклад: brew install coreutils
.
ВСТУП
Ця відповідь виправляє дуже ламану, але шокуючу відповідь цієї теми (написав TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
ЧОМУ ВИКОРИСТОВУВАННЯ dirname "$ 0" НА ВЛАСНІЙ НЕ ПРАЦЮЄ?
dirname $ 0 буде працювати, лише якщо користувач запустить скрипт дуже специфічним чином. Мені вдалося знайти декілька ситуацій, коли ця відповідь не відповідає і руйнує сценарій.
Перш за все, давайте розберемося, як працює ця відповідь. Він отримує каталог сценаріїв, роблячи
dirname "$0"
$ 0 являє собою першу частину команди, що викликає скрипт (це в основному введена команда без аргументів:
/some/path/./script argument1 argument2
$ 0 = "/ деякі / шлях /./ скрипт"
dirname в основному знаходить останнє / у рядку і обрізає його там. Отже, якщо ви робите:
dirname /usr/bin/sha256sum
ви отримаєте: / usr / bin
Цей приклад добре працює, оскільки / usr / bin / sha256sum - це правильно відформатований шлях, але
dirname "/some/path/./script"
не буде працювати добре, і дасть вам:
BASENAME="/some/path/." #which would crash your script if you try to use it as a path
Скажіть, ви перебуваєте в тому ж режимі, що і ваш сценарій, і ви запускаєте його за допомогою цієї команди
./script
$ 0 у цій ситуації буде ./script та dirname $ 0 дадуть:
. #or BASEDIR=".", again this will crash your script
Використання:
sh script
Без введення повного шляху також буде дано BASEDIR = ".
Використання відносних каталогів:
../some/path/./script
Дає dirname $ 0:
../some/path/.
Якщо ви перебуваєте в каталозі / some і ви викликаєте скрипт таким чином (зверніть увагу на відсутність / на початку, знову ж відносний шлях):
path/./script.sh
Ви отримаєте це значення для dirname $ 0:
path/.
і ./path/./script (інша форма відносного шляху) дає:
./path/.
Єдині дві ситуації, в яких базується $ 0 , - якщо користувач використовує sh або touch, щоб запустити сценарій, оскільки обидва отримають $ 0:
$0=/some/path/script
який дасть вам шлях, який ви можете використовувати з dirname.
РІШЕННЯ
Ви маєте обліковий запис і виявляти кожну з вищезазначених ситуацій і застосовувати виправлення, якщо воно виникне:
#!/bin/bash
#this script will only work in bash, make sure it's installed on your system.
#set to false to not see all the echos
debug=true
if [ "$debug" = true ]; then echo "\$0=$0";fi
#The line below detect script's parent directory. $0 is the part of the launch command that doesn't contain the arguments
BASEDIR=$(dirname "$0") #3 situations will cause dirname $0 to fail: #situation1: user launches script while in script dir ( $0=./script)
#situation2: different dir but ./ is used to launch script (ex. $0=/path_to/./script)
#situation3: different dir but relative path used to launch script
if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "$0") gives: '"$BASEDIR";fi
if [ "$BASEDIR" = "." ]; then BASEDIR="$(pwd)";fi # fix for situation1
_B2=${BASEDIR:$((${#BASEDIR}-2))}; B_=${BASEDIR::1}; B_2=${BASEDIR::2}; B_3=${BASEDIR::3} # <- bash only
if [ "$_B2" = "/." ]; then BASEDIR=${BASEDIR::$((${#BASEDIR}-1))};fi #fix for situation2 # <- bash only
if [ "$B_" != "/" ]; then #fix for situation3 #<- bash only
if [ "$B_2" = "./" ]; then
#covers ./relative_path/(./)script
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/${BASEDIR:2}"; else BASEDIR="/${BASEDIR:2}";fi
else
#covers relative_path/(./)script and ../relative_path/(./)script, using ../relative_path fails if current path is a symbolic link
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/$BASEDIR"; else BASEDIR="/$BASEDIR";fi
fi
fi
if [ "$debug" = true ]; then echo "fixed BASEDIR=$BASEDIR";fi
Цей однолінійний вказівник повідомляє, де знаходиться сценарій оболонки, не має значення, чи ви запустили його, чи якщо ви його отримали . Крім того, він вирішує будь-які символічні посилання, якщо це так:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
До речі, я думаю, ви використовуєте / bin / bash .
Стільки відповідей, всі правдоподібні, кожен з профі та кон та злегка різними цілями (які, мабуть, слід зазначити для кожного). Ось ще одне рішення, яке відповідає основній меті - як зрозуміти, так і працювати в усіх системах, на всіх bash (жодних припущень щодо bash-версій readlink
чи pwd
варіантів), і розумно виконує те, що ви очікуєте, що відбудеться (наприклад, вирішення символьних посилань - це цікава проблема, але зазвичай це не те, що вам потрібно?
Кожен компонент зберігається в окремій змінній, яку можна використовувати окремо:
# script path, filename, directory
PROG_PATH=${BASH_SOURCE[0]} # this script's name
PROG_NAME=${PROG_PATH##*/} # basename of script (strip path)
PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"
Натхненний відповіддю Синяка
read < <(readlink -f $0 | xargs dirname)
cd $REPLY
Це повинно зробити трюк:
echo `pwd`/`dirname $0`
Це може виглядати некрасиво, залежно від того, як його викликали і cwd, але слід дістати вас туди, куди вам потрібно піти (або ви можете налаштувати струну, якщо вам все одно, як вона виглядає).
`pwd`/`dirname $0`
але все ж може не вдатися до посилань