Сценарий оболочки Unix узнайте, в каком каталоге находится файл сценария?
в основном мне нужно запустить сценарий с путями, связанными с расположением файла сценария оболочки, как я могу изменить текущий каталог в тот же каталог, где находится файл сценария?
15 ответов
в Bash вы должны получить то, что вам нужно:
#!/usr/bin/env bash
BASEDIR=$(dirname "")
echo "$BASEDIR"
исходное сообщение содержит решение (игнорируйте ответы, они не добавляют ничего полезного). Интересная работа выполняется упомянутой командой unix readlink
с опцией -f
. Работает, когда скрипт вызывается как абсолютным, так и относительным путем.
для bash, sh, ksh:
#!/bin/bash
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "")
# 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 ""`
# Absolute path this script is in, thus /home/user/bin
set SCRIPTPATH=`dirname "$SCRIPT"`
echo $SCRIPTPATH
см. также:https://stackoverflow.com/a/246128/59087
более ранний комментарий к ответу сказал это, но его легко пропустить среди всех других ответов.
при использовании bash:
echo this file: $BASH_SOURCE
echo this dir: `dirname $BASH_SOURCE`
предполагая, что вы используете bash
#!/bin/bash
current_dir=$(pwd)
script_dir=$(dirname )
echo $current_dir
echo $script_dir
этот скрипт должен распечатать каталог, в котором вы находитесь, а затем каталог, в котором находится скрипт. Например, при вызове из /
со скриптом в /home/mez/
, он выводит
/
/home/mez
помните, что при назначении переменных из выходных данных команды оберните команду в $(
и )
- или вы не получите желаемого результата.
Если вы используете bash....
#!/bin/bash
pushd $(dirname "") > /dev/null
basedir=$(pwd -L)
# Use "pwd -P" for the path without links. man bash for more info.
popd > /dev/null
echo "${basedir}"
Как предлагает theMarko:
BASEDIR=$(dirname )
echo $BASEDIR
это работает, если вы не выполняете скрипт из того же каталога, где находится скрипт, и в этом случае вы получаете значение '.'
чтобы обойти эту проблему, используйте:
current_dir=$(pwd)
script_dir=$(dirname )
if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi
теперь вы можете использовать переменную current_dir во всем скрипте для ссылки на каталог скриптов. Однако это все еще может иметь проблему symlink.
лучший ответ на этот вопрос был дан ответ здесь:
получение исходного каталога скрипта Bash изнутри
и:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
One-liner, который даст вам полное имя каталога скрипта независимо от того, откуда он вызывается.
чтобы понять, как это работает, можно выполнить следующий скрипт:
#!/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="/"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
Испытано на много Bourne-совместимых раковин включая BSD одни.
насколько я знаю, я автор, и я положил его в общественное достояние. Для получения дополнительной информации см.: https://www.jasan.tk/posix/2017/05/11/posix_shell_dirname_replacement
вдохновленный blueyed это
read < <(readlink -f | xargs dirname)
cd $REPLY
так много ответов, все правдоподобные, каждый с pro и con и слегка отличающимися целями (которые, вероятно, должны быть указаны для каждого). Вот еще одно решение, которое отвечает основной цели как быть ясным, так и работать во всех системах, на всех bash (никаких предположений о версиях bash или readlink
или pwd
options) и разумно делает то ,что вы ожидаете (например, разрешение символических ссылок-интересная проблема, но обычно это не то, что вы действительно хотите), обрабатывать крайние случаи как пробелы в путях и т. д. игнорирует любые ошибки и использует по умолчанию, если есть какие-либо вопросы.
каждый компонент хранится в отдельной переменной, которую можно использовать индивидуально:
# 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)"
введение
ответ коррекция очень сломанный, но потрясающе верхний голосовал ответ этой темы (написанный TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "")
echo "$BASEDIR"
почему использование dirname "$0 " самостоятельно не работает?
dirname $0 будет работать, только если пользователь запускает скрипт очень определенным образом. Я смог найти несколько ситуаций, в которых этот ответ терпит неудачу и сбой сценария.
прежде всего, давайте разберемся, как работает этот ответ. Он получает каталог сценариев, делая
dirname ""
$0 представляет собой первую часть команды, вызывающей скрипт (это в основном введенная команда без аргументов:
/some/path/./script argument1 argument2
$0= " / some/path/./ скрипт"
dirname в основном находит последний / в строке и усекает его там. Так что если вы do:
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
скажем, вы находитесь в том же dir, что и ваш скрипт, и вы запускаете его с помощью этой команды
./script
$0 в этой ситуации будет ./ скрипт и 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/.
и ./путь.// script (другая форма относительного пути) дает:
./path/.
только две ситуации, когда basedir $0 будет работать, если пользователь использует sh или touch для запуска скрипта, потому что оба приведут к $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=";fi
#The line below detect script's parent directory. is the part of the launch command that doesn't contain the arguments
BASEDIR=$(dirname "") #3 situations will cause dirname to fail: #situation1: user launches script while in script dir ( =./script)
#situation2: different dir but ./ is used to launch script (ex. =/path_to/./script)
#situation3: different dir but relative path used to launch script
if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "") 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.
это должно сделать трюк:
echo `pwd`/`dirname `
это может выглядеть уродливо в зависимости от того, как он был вызван и cwd, но должен привести вас туда, куда вам нужно идти (или вы можете настроить строку, если вам интересно, как она выглядит).