从脚本本身获取Bash脚本的源目录

时间:2008-09-12 20:39:57

标签: bash directory

如何获取Bash脚本所在目录的路径,里面该脚本?

例如,假设我想将Bash脚本用作另一个应用程序的启动器。我想将工作目录更改为Bash脚本所在的目录,因此我可以对该目录中的文件进行操作,如下所示:

$ ./application

73 个答案:

答案 0 :(得分:5854)

#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

是一个有用的单行程序,它将为您提供脚本的完整目录名称,无论它在何处被调用。

只要用于查找脚本的路径的最后一个组件不是符号链接(目录链接正常),它就会起作用。如果您还想解决脚本本身的任何链接,则需要一个多行解决方案:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

最后一个可以使用别名的任意组合,sourcebash -c,符号链接等。

注意:如果您在运行此代码段之前cd到另一个目录,结果可能不正确!

另外,如果用户巧妙地重写了cd以将输出重定向到stderr(包括转义序列,例如在Mac上调用update_terminal_cwd >&2时),请注意$CDPATH gotchas和stderr输出副作用。在>/dev/null 2>&1命令末尾添加cd将处理这两种可能性。

要了解它是如何工作的,请尝试运行这个更详细的表单:

#!/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" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

它将打印出如下内容:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

答案 1 :(得分:766)

使用dirname "$0"

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"
如果您没有从包含它的目录运行脚本,单独使用pwd

将不起作用。

[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

答案 2 :(得分:420)

dirname命令是最基本的,只需解析从$ 0(脚本名称)变量到文件名的路径:

dirname "$0"

但是,正如 matt b 指出的那样,返回的路径会有所不同,具体取决于脚本的调用方式。 pwd没有完成这项工作,因为它只告诉你当前目录是什么,而不是脚本所在的目录。另外,如果执行了一个脚本的符号链接,你将获得一个(可能是相对的)路径到链接所在的位置,而不是实际的脚本。

其他一些人提到了 readlink 命令,但最简单的是,您可以使用:

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

readlink将脚本路径解析为文件系统根目录的绝对路径。因此,任何包含单点或双点,波浪线和/或符号链接的路径都将被解析为完整路径。

这是一个演示其中每一个的脚本, whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

使用相对路径在我的主目录中运行此脚本:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

再次,但使用脚本的完整路径:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

现在更改目录:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

最后使用符号链接执行脚本:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

答案 3 :(得分:175)

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; 
  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

适用于所有版本,包括

  • 通过多深度软链接调用时,
  • 当文件
  • 当脚本被命令“source”称为.(点)运算符时。
  • 从调用者修改arg $0时。
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

或者,如果bash脚本本身是相对符号链接,则希望跟随它并返回链接到脚本的完整路径:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

SCRIPT_PATH无论如何被称为完整路径 只需确保在脚本开头找到它。

此评论和代码Copyleft,GPL2.0或更高版本或CC-SA 3.0(CreativeCommons Share Alike)或更高版本下的可选许可证。 (c)2008年。保留所有权利。没有任何形式的保证。你被警告了。
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656

答案 4 :(得分:101)

简短回答:

`dirname $0`

或(preferably):

$(dirname "$0")

答案 5 :(得分:97)

您可以使用$ BASH_SOURCE

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

请注意,您需要使用#!/ bin / bash而不是#!/ bin / sh,因为它是一个bash扩展名

答案 6 :(得分:62)

这应该这样做:

DIR=$(dirname "$(readlink -f "$0")")

使用路径中的符号链接和空格。 请参阅 dirname readlink 的手册页。

编辑:

从评论曲目看,它似乎无法与Mac OS一起使用。 我不知道为什么会这样。有什么建议吗?

答案 7 :(得分:55)

pwd可用于查找当前工作目录,dirname可查找特定文件的目录(已运行的命令为$0,因此{{1}应该给你当前脚本的目录。)

但是,dirname $0精确地给出了文件名的目录部分,它更可能是相对于当前工作目录的。如果您的脚本由于某种原因需要更改目录,则dirname的输出将变得毫无意义。

我建议如下:

dirname

这样,你得到一个绝对的,而不是相对的目录。

由于脚本将在单独的bash实例中运行,因此之后无需恢复工作目录,但如果由于某种原因确实想要更改脚本,则可以轻松分配{{{ 1}}更改目录之前的变量,以备将来使用。

虽然只是

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

解决问题中的具体情况,我发现通常有更多有用的绝对路径。

答案 8 :(得分:33)

我认为这并不像其他人那样容易实现。 pwd不起作用,因为当前目录不一定是脚本的目录。 $ 0并不总是有信息。请考虑以下三种调用脚本的方法。

./script

/usr/bin/script

script

在第一种和第三种方式中,$ 0没有完整的路径信息。在第二和第三,pwd不起作用。以第三种方式获取目录的唯一方法是遍历路径并找到具有正确匹配的文件。基本上代码必须重做操作系统的功能。

执行您要求的一种方法是仅对/ usr / share目录中的数据进行硬编码,并通过完整路径引用它。无论如何数据都不在/ usr / bin目录中,所以这可能是要做的事情。

答案 9 :(得分:32)

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )

答案 10 :(得分:28)

这将获取Mac OS X 10.6.6上的当前工作目录:

DIR=$(cd "$(dirname "$0")"; pwd)

答案 11 :(得分:27)

$(dirname "$(readlink -f "$BASH_SOURCE")")

答案 12 :(得分:26)

我厌倦了一次又一次地访问此页面,将单线复制粘贴到已接受的答案中。问题在于它不容易理解和记住。

这是一个易于记忆的脚本:

DIR=$(dirname "${BASH_SOURCE[0]}")  # get the directory name
DIR=$(realpath "${DIR}")    # resolve its full path if need be

答案 13 :(得分:26)

这是特定于Linux的,但您可以使用:

SELF=$(readlink /proc/$$/fd/255)

答案 14 :(得分:21)

这是符合POSIX标准的单行:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

答案 15 :(得分:16)

我尝试了其中的每一个,但没有一个有效。一个非常接近,但有一个小小的虫子,打破了它;他们忘了用引号包住路径。

也有很多人认为你是从shell运行脚本所以忘记当你打开一个新的脚本它默认你的家。

尝试使用此目录的大小:

/ var /没有人/思想/关于空间存在/在目录/名称/这里是你的文件.text

无论您如何或在何处运行它,都能正确使用它。

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

因此,为了使其真正有用,如何更改到正在运行的脚本的目录:

cd "`dirname "$0"`"

希望有所帮助

答案 16 :(得分:16)

这是一种简单,正确的方法:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

说明:

  • ${BASH_SOURCE[0]} - 脚本的完整路径。即使在获取脚本时,这个值也是正确的,例如source <(echo 'echo $0')打印 bash ,而将其替换为${BASH_SOURCE[0]}则会打印脚本的完整路径。 (当然,这假设您可以依赖Bash。)

  • readlink -f - 以递归方式解析指定路径中的所有符号链接。这是GNU扩展,在(例如)BSD系统上不可用。如果您正在运行Mac,则可以使用Homebrew来安装GNU coreutils并使用 greadlink -f 取代它。

  • 当然, dirname 会获取路径的父目录。

答案 17 :(得分:15)

我会用这样的东西:

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

答案 18 :(得分:14)

their answer

中指出对e-satisf和3bcdnlklvc04a解决方案的略微修改
SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}    

这应该适用于他们列出的所有情况。

编辑:在推送失败后阻止popd,感谢konsolebox

答案 19 :(得分:13)

#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

答案 20 :(得分:11)

我已经比较了许多给出的答案,并提出了一些更紧凑的解决方案。这些似乎可以解决您最喜欢的组合产生的所有疯狂边缘情况:

  • 绝对路径或相对路径
  • 文件和目录软链接
  • 调用scriptbash scriptbash -c scriptsource script. script
  • 目录和/或文件名中的空格,制表符,换行符,unicode等
  • 以连字符开头的文件名

如果您从Linux运行,似乎使用proc句柄是找到当前正在运行的脚本的完全解析源的最佳解决方案(在交互式会话中,链接指向相应的{ {1}}):

/dev/pts/X

这有一点点丑陋,但修复很紧凑,易于理解。我们不仅仅使用bash原语,但我很好,因为resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}" 大大简化了任务。 readlink在变量字符串的末尾添加echo X,以便文件名中的任何尾随空格都不会被吃掉,并且行尾的参数替换X得到摆脱${VAR%X}。因为X添加了自己的换行符(如果不是我们之前的诡计,通常会在命令替换中被吃掉),我们也必须摆脱它。这是使用readlink引用方案最容易实现的,它允许我们使用转义序列(如$'')来表示换行符(这也是您可以轻松制作狡猾的命名目录和文件)。

以上内容应该包括您在Linux上查找当前运行的脚本的需求,但如果您没有\n文件系统,或者您正试图找到某些完全解析的路径其他文件,那么也许你会发现下面的代码很有帮助。这只是对上述单线的略微修改。如果您正在使用奇怪的目录/文件名,那么使用procls检查输出会提供信息,因为readlink将输出“简化”路径,替换为ls对于像换行符这样的东西。

?

答案 21 :(得分:11)

$_值得一提,作为$ 0的替代品。如果您正在使用bash运行脚本,则可以将接受的答案缩短为:

DIR="$( dirname "$_" )"

请注意,这必须是您脚本中的第一个语句。

答案 22 :(得分:10)

对于具有GNU coreutils readlink的系统(例如linux):

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

BASH_SOURCE包含脚本文件名时,无需使用$0

答案 23 :(得分:10)

尝试使用:

real=$(realpath $(dirname $0))

答案 24 :(得分:8)

所以......我相信我有这个。晚到派对,但我想有些人会欣赏它在这里是他们遇到这个线程。评论应该解释。

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi

答案 25 :(得分:8)

尝试以下交叉兼容的解决方案:

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

由于realpathreadlink命令并非始终可用(取决于操作系统),${BASH_SOURCE[0]}仅在bash shell中可用。

或者你可以在bash中尝试以下功能:

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

此函数需要1个参数。如果参数已经是绝对路径,则按原样打印,否则打印$PWD变量+文件名参数(不带./前缀)。

相关:

答案 26 :(得分:7)

在许多情况下,您需要获取的只是您刚刚调用的脚本的完整路径。这可以很容易地完成,如下。请注意,realpath GNU coreutils 的一部分。如果尚未安装(Ubuntu上默认安装),则可以使用sudo apt update && sudo apt install coreutils进行安装。

realpath_test.sh

#!/bin/bash

PATH_TO_SCRIPT="$(realpath $0)"
echo "PATH_TO_SCRIPT = \"$PATH_TO_SCRIPT\""

示例输出:

$ ./realpath_test.sh
PATH_TO_SCRIPT =“ /home/gabriel/dev/linux_scripts/practice/realpath/realpath_test.sh”

请注意,realpath还会成功地沿符号链接移动,以确定并指向其目标,而不是指向符号链接。

参考文献:

  1. How to retrieve absolute path given relative

答案 27 :(得分:7)

嗯,如果在路径basename&amp; dirname只是不会削减它 走路是很难的(如果父母没有出口PATH怎么办!)。 但是,shell必须有一个打开它的脚本的句柄,并且在 手柄是#255。

SELF=`readlink /proc/$$/fd/255`

适合我。

答案 28 :(得分:6)

这适用于bash-3.2:

path="$( dirname "$( which "$0" )" )"

以下是其用法示例:

假设您有一个〜/ bin 目录,该目录位于 $ PATH 中。您在此目录中有脚本 A 来源 s脚本〜/ bin / lib / B 。您知道所包含的脚本相对于原始脚本(子目录 lib )的位置,而不是相对于用户当前目录的位置。

这可以通过以下方法解决(在 A 中):

source "$( dirname "$( which "$0" )" )/lib/B"

无论用户在哪里或者他如何调用脚本都无关紧要,这将始终有效。

答案 29 :(得分:6)

总结许多答案:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

使用的命令

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

输出:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

请参阅 https://pastebin.com/J8KjxrPF

答案 30 :(得分:5)

这些都不适用于Finder在OS X中启动的bash脚本 - 我最终使用了:

SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
sed s:.*/Volumes/:/Volumes/:`"

不漂亮,但它完成了工作。

答案 31 :(得分:5)

您只需将脚本名称($0)与realpath和/或dirname结合使用即可。它适用于Bash和Shell。

#!/usr/bin/env bash

RELATIVE_PATH="${0}"
RELATIVE_DIR_PATH="$(dirname "${0}")"
FULL_DIR_PATH="$(realpath "${0}" | xargs dirname)"
FULL_PATH="$(realpath "${0}")"

echo "RELATIVE_PATH->${RELATIVE_PATH}<-"
echo "RELATIVE_DIR_PATH->${RELATIVE_DIR_PATH}<-"
echo "FULL_DIR_PATH->${FULL_DIR_PATH}<-"
echo "FULL_PATH->${FULL_PATH}<-"

输出将是这样的:

# RELATIVE_PATH->./bin/startup.sh<-
# RELATIVE_DIR_PATH->./bin<-
# FULL_DIR_PATH->/opt/my_app/bin<-
# FULL_PATH->/opt/my_app/bin/startup.sh<-
  

$ 0是脚本本身的名称

https://www.tldp.org/LDP/abs/html/othertypesv.html

示例:https://gist.github.com/LozanoMatheus/da96b4e44b89b13ad4af10ac4602ad99

答案 32 :(得分:5)

当这里的其他答案没有时,这对我有用:

thisScriptPath=`realpath $0`
thisDirPath=`dirname $thisScriptPath`
echo $thisDirPath

答案 33 :(得分:5)

我认为最好的紧凑型解决方案是:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

除了Bash之外,没有任何依赖。 dirnamereadlinkbasename的使用最终会导致兼容性问题,因此如果可能的话,最好避免使用它们。

答案 34 :(得分:4)

如果目录名称末尾有任何换行符,当前的解决方案都不起作用 - 它们将被命令替换删除。要解决此问题,您可以在命令替换中附加一个非换行符,然后仅删除该字符:

dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd && echo x)"
dir="${dir%x}"

这可以防止两种非常常见的情况:事故和蓄意破坏。脚本不应该仅仅因为某人在某处做了一个 mkdir $'\n' 就以不可预测的方式失败。

答案 35 :(得分:4)

这是我发现可靠的唯一方法:

SCRIPT_DIR=$(dirname $(cd "$(dirname "$BASH_SOURCE")"; pwd))

答案 36 :(得分:3)

使用readlink的组合来规范化名称(如果它是符号链接,则会将其追溯到其源代码)和dirname以提取目录名称:

script="`readlink -f "${BASH_SOURCE[0]}"`"
dir="`dirname "$script"`"

答案 37 :(得分:3)

$0不是获取当前脚本路径的可靠方法。例如,这是我的.xprofile

#!/bin/bash
echo "$0 $1 $2"
echo "${BASH_SOURCE[0]}"
# $dir/my_script.sh &
  

cd / tmp&amp;&amp; 〜/ .xprofile&amp;&amp;来源〜/ .xprofile

/home/puchuu/.xprofile  
/home/puchuu/.xprofile
-bash  
/home/puchuu/.xprofile

所以请改用BASH_SOURCE

答案 38 :(得分:2)

我尝试了以下 3 种不同的执行。

echo $(realpath $_)

. application         # /correct/path/to/dir or /path/to/temporary_dir
bash application      # /path/to/bash
/PATH/TO/application  # /correct/path/to/dir

echo $(realpath $(dirname $0))

. application         # failed with `realpath: missing operand`
bash application      # /correct/path/to/dir
/PATH/TO/application  # /correct/path/to/dir

echo $(realpath $BASH_SOURCE)

$BASH_SOURCE${BASH_SOURCE[0]} 基本相同。

. application         # /correct/path/to/dir
bash application      # /correct/path/to/dir
/PATH/TO/application  # /correct/path/to/dir

似乎只有 $(realpath $BASH_SOURCE) 是可靠的。

答案 39 :(得分:1)

令人讨厌的是,当可执行脚本是符号链接时,这是我发现的唯一一种在 Linux 和 macOS 上都适用的单行代码:

SCRIPT_DIR=$(python -c "import os, sys; print(os.path.dirname(os.path.realpath('${BASH_SOURCE[0]}')))")

在 Linux 和 macOS 上进行了测试,并与此要点中的其他解决方案进行了比较:https://gist.github.com/ptc-mrucci/61772387878ed53a6c717d51a21d9371

答案 40 :(得分:1)

大多数答案要么不处理通过相对路径符号链接的文件,要么不是单行文件,要么不处理 BSD (Mac)。一个解决所有三个问题的方法是:

return(
  <Admin dataProvider={resolvedDataProvider}>
    <Resource name="nameOfGraphQLQuery" list={SomeList} />
  </Admin>
)

首先,cd 到 bash 脚本目录的概念。然后读取链接文件以查看它是否是符号链接(相对或其他),如果是,则 cd 到该目录。如果没有, cd 到当前目录(必须保持单行)。然后通过 HERE=$(cd "$(dirname "$BASH_SOURCE")"; cd -P "$(dirname "$(readlink "$BASH_SOURCE" || echo .)")"; pwd) 回显当前目录。

您可以在 cd 和 readlink 的参数中添加 pwd 以避免出现类似选项的目录问题,但我不会为大多数目的而烦恼。

您可以在此处查看带插图的完整说明:

https://www.binaryphile.com/bash/2020/01/12/determining-the-location-of-your-script-in-bash.html

答案 41 :(得分:1)

这种方法的一个优点是它不涉及 Bash 本身之外的任何东西,也不分叉任何子 shell。

首先,使用模式替换将任何不以 / 开头(即相对路径)的内容替换为 $PWD/。由于我们使用替换来匹配 $0 的第一个字符,因此我们还必须将其附加回来(替换中的 ${0:0:1})。

现在我们有了脚本的完整路径;我们可以通过删除最后一个 / 和后面的任何内容(即脚本名称)来获取目录。然后可以在 cd 中使用该目录或作为与您的脚本相关的其他路径的前缀。

#!/bin/bash

BIN=${0/#[!\/]/"$PWD/${0:0:1}"}
DIR=${BIN%/*}

cd "$DIR"

如果您的脚本可能是源代码而不是执行,您当然可以将 $0 替换为 ${BASH_SOURCE[0]},例如:

BIN=${BASH_SOURCE[0]/#[!\/]/"$PWD/${BASH_SOURCE[0]:0:1}"}

这也适用于可执行脚本。它更长,但更多元化。

答案 42 :(得分:1)

这是一个很老的问题,但是无论如何我都会添加答案。我通常使用

dirname $(which $BASH_SOURCE)

答案 43 :(得分:1)

如果你的BASH SCRIPT是一个SYMLINK,那么就是这样做的方法

#!/usr/bin/env bash

dirn="$(dirname "$0")"
rl="$(readlink "$0")";
exec_dir="$(dirname $(dirname "$rl"))";
my_path="$dirn/$exec_dir";
X="$(cd $(dirname ${my_path}) && pwd)/$(basename ${my_path})"

X是包含bash脚本的目录(原始文件,而不是符号链接)。我向上帝发誓这是有效的,也是我知道这样做的唯一方法。

答案 44 :(得分:0)

最简单,最优雅的方法是:

#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY

这将适用于所有平台,并且非常干净。

更多详细信息可以在这里找到:https://www.electrictoolbox.com/bash-script-directory/

答案 45 :(得分:0)

以下是我对shell script: check directory name and convert to lowercase的回答摘录,其中我不仅演示了如何使用非常基本的POSIX指定实用程序解决此问题,还解决了 如何非常简单地存储函数的问题导致返回的变量......

......好吧,正如您所看到的,通过一些帮助,我找到了一个非常简单且非常强大的解决方案:

我可以将函数传递给某种信使变量,并根据需要取消引用任意显式使用结果函数的参数$1eval,并在函数上使用例程的完成,我使用eval和一个反向引用技巧来为我的信使变量分配我想要的值,而不必知道它的名字。

在完整的披露中,...... [我找到了这个的信使变量部分]和Rich's sh tricks,我也在我自己的答案的摘录下摘录了他的页面的相关部分。

... 的摘录: ...

虽然还不是严格的POSIX,realpath 是自2012年以来的GNU核心应用。完全披露:在我info coreutils TOC注意到之前从未听说过它,并立即想到了[链接]问题,但是使用以下函数应该可靠,(很快就是POSIXLY?),我希望,有效率的 为其来电者提供绝对来源的$0

% _abs_0() { 
> o1="${1%%/*}"; ${o1:="${1}"}; ${o1:=`realpath -s "${1}"`}; eval "$1=\${o1}"; 
> }  
% _abs_0 ${abs0:="${0}"} ; printf %s\\n "${abs0}"
/no/more/dots/in/your/path2.sh

编辑:值得强调的是,此解决方案使用POSIX parameter expansion在尝试执行此操作之前首先检查路径是否实际需要扩展和解析。这应该通过信使变量 返回绝对来源的$0(但值得注意的例外是它会将symlinks保留为有效< / strong>我可以想象它可以完成路径是否已经绝对。 ...

次要编辑 :在文档中找到realpath之前,我至少减少了我的[以下版本]的版本依赖于时间字段[就像它在第一个ps命令中所做的那样],但是,公平警告,在测试之后我不太相信ps在命令路径扩展能力方面是完全可靠的

另一方面,你可以这样做:

ps ww -fp $$ | grep -Eo '/[^:]*'"${0#*/}"

eval "abs0=${`ps ww -fp $$ | grep -Eo ' /'`#?}"

... 来自Rich's sh tricks ...

从shell函数返回字符串

从上面的命令替换缺陷中可以看出,stdout不是shell函数将字符串返回给调用者的好途径,除非输出采用尾随换行无关紧要的格式。当然,这种做法对于处理任意字符串的函数是不可接受的。那么,可以做些什么呢?

试试这个:

func () {
body here
eval "$1=\${foo}"
}

当然${foo}可以替换为任何类型的替换。这里的关键技巧是评估线和转义的使用。当eval的参数由主命令解析器构造时,“$1”被扩展。但是“${foo}”在此阶段尚未展开,因为“$”已被引用。相反,当eval评估其参数时,它会被扩展。如果不清楚为什么这很重要,请考虑以下情况如何:

foo='hello ; rm -rf /'
dest=bar
eval "$dest=$foo"

但当然以下版本是绝对安全的:

foo='hello ; rm -rf /'
dest=bar
eval "$dest=\$foo"

请注意,在原始示例中,“$1”用于允许调用者将目标变量名称作为函数的参数传递。如果您的函数需要使用shift命令,例如将剩余的参数作为“$@”处理,那么将“$1”的值保存在函数开头的临时变量中可能很有用。

答案 46 :(得分:0)

我认为最简单的答案是原始变量的参数扩展:

#!/usr/bin/env bash                                                                

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"            
echo "opt1; original answer: $DIR"                                                 
echo ''                                                                            

echo "opt2; simple answer  : ${BASH_SOURCE[0]%/*}"                              

应产生如下输出:

$ /var/tmp/test.sh
opt1; original answer: /var/tmp

opt2; simple answer  : /var/tmp

变量/参数扩展${BASH_SOURCE[0]%/*}"似乎更易于维护。

答案 47 :(得分:0)

Python被提及了几次。这是JavaScript(即node)的替代方法:

baseDirRelative=$(dirname "$0")
baseDir=$(node -e "console.log(require('path').resolve('$baseDirRelative'))") # get absolute path using node

echo $baseDir

答案 48 :(得分:0)

免责声明-并非在所有情况下都能获得最佳答复...这就是发布此替代答案的原因。

由于我在使用“ sh my_script.sh”调用shell脚本时,在一些非常新的和还不太新鲜的已安装Ubuntu Xenial(16.04)系统上使用“ cd”方法的BASH_SOURCE遇到问题,我尝试了一些方法就我的目的而言,到现在为止似乎运行得很顺利。该方法在脚本中更紧凑,并且隐秘感更低。

这种替代方法使用coreutils软件包中的外部应用程序“ realpath”和“ dirname”。 (好吧,不是每个人都喜欢调用辅助进程的开销-但是,当看到用于解析真实对象的多行脚本时,将其以单个二进制形式解决就不会那么糟糕。)

因此,让我们来看一下上述查询特定文件的绝对路径的任务的替代解决方案的一个示例:

SCRIPT=`realpath -s $0`
SCRIPTPATH=`dirname $SCRIPT`

或者有机会使用带空格的路径(或其他特殊字符)时:

SCRIPT=`realpath -s "$0"`
SCRIPTPATH=`dirname "$SCRIPT"`

实际上,如果不需要SCRIPT变量的值,则可以将这两条直线合并为一行。但是为什么您真的要为此付出努力呢?

答案 49 :(得分:0)

以下内容将返回脚本的当前目录

  • 可以正常工作,无论它是来源还是没有来源
  • 可以在当前目录或其他目录中运行。
  • 如果使用相对目录,则可以使用。
  • 使用bash,不确定其他shell。
/tmp/a/b/c $ . ./test.sh
/tmp/a/b/c

/tmp/a/b/c $ . /tmp/a/b/c/test.sh
/tmp/a/b/c

/tmp/a/b/c $ ./test.sh
/tmp/a/b/c

/tmp/a/b/c $ /tmp/a/b/c/test.sh
/tmp/a/b/c

/tmp/a/b/c $ cd

~ $ . /tmp/a/b/c/test.sh
/tmp/a/b/c

~ $ . ../../tmp/a/b/c/test.sh
/tmp/a/b/c

~ $ /tmp/a/b/c/test.sh
/tmp/a/b/c

~ $ ../../tmp/a/b/c/test.sh
/tmp/a/b/c

test.sh

#!/usr/bin/env bash

# snagged from: https://stackoverflow.com/a/51264222/26510
function toAbsPath {
    local target
    target="$1"

    if [ "$target" == "." ]; then
        echo "$(pwd)"
    elif [ "$target" == ".." ]; then
        echo "$(dirname "$(pwd)")"
    else
        echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
    fi
}

function getScriptDir(){
  local SOURCED
  local RESULT
  (return 0 2>/dev/null) && SOURCED=1 || SOURCED=0

  if [ "$SOURCED" == "1" ]
  then
    RESULT=$(dirname "$1")
  else
    RESULT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
  fi
  toAbsPath "$RESULT"
}

SCRIPT_DIR=$(getScriptDir "$0")
echo "$SCRIPT_DIR"

答案 50 :(得分:0)

这是我多年来精心制作的用作bash脚本头文件的方法:

## BASE BRAIN - Get where you're from and who you are.
MYPID=$$
ORIGINAL_DIR="$(pwd)" # This is not a hot air balloon ride..
fa="$0" # First Assumption
ta= # Temporary Assumption
wa= # Weighed Assumption
while true; do
    [ "${fa:0:1}" = "/" ] && wa=$0 && break
    [ "${fa:0:2}" = "./" ] && ta="${ORIGINAL_DIR}/${fa:2}" && [ -e "$ta" ] && wa="$ta" && break
    ta="${ORIGINAL_DIR}/${fa}" && [ -e "$ta" ] && wa="$ta" && break
done
SW="$wa"
SWDIR="$(dirname "$wa")"
SWBIN="$(basename "$wa")"
unset ta fa wa
( [ ! -e "$SWDIR/$SWBIN" ] || [ -z "$SW" ] ) && echo "I could not find my way around :( possible bug in the TOP script" && exit 1

此时,您的变量SW SWDIR和SWBIN包含所需的内容。

答案 51 :(得分:0)

我想评论前面的答案(https://stackoverflow.com/a/201915/5010054),但没有足够的声誉来做到这一点。

两年前在Apple的文档站点https://developer.apple.com/library/archive/documentation/OpenSource/Conceptual/ShellScripting/AdvancedTechniques/AdvancedTechniques.html上找到了一种解决方案。然后,我坚持使用这种方法。它不能处理软链接,但对我来说效果很好。我在这里将其发布给任何需要它的人,以征询他们的意见。

#!/bin/sh

# Get an absolute path for the poem.txt file.
POEM="$PWD/../poem.txt"

# Get an absolute path for the script file.
SCRIPT="$(which $0)"
if [ "x$(echo $SCRIPT | grep '^\/')" = "x" ] ; then
    SCRIPT="$PWD/$SCRIPT"
fi

如代码所示,获取脚本的绝对路径后,可以使用dirname命令获取目录的路径。

答案 52 :(得分:0)

这是在bash或zsh下工作的命令,无论是独立执行还是源执行:

[ -n "$ZSH_VERSION" ] && this_dir=$(dirname "${(%):-%x}") \
    || this_dir=$(dirname "${BASH_SOURCE[0]:-$0}")

工作原理

zsh当前文件扩展名:${(%):-%x}

zsh中的

${(%):-%x}会扩展到当前正在执行的文件的路径。

后备替换运算符:-

您已经知道${...}会替换字符串中的变量。您可能不知道替换期间对变量进行某些操作(在bashzsh中都是可行的,例如后备扩展运算符:-

% x=ok
% echo "${x}"
ok

% echo "${x:-fallback}"
ok

% x=
% echo "${x:-fallback}"
fallback

% y=yvalue
% echo "${x:-$y}"
yvalue

%x提示转义代码

接下来,我们将介绍提示转义码,这是仅zsh的功能。在zsh中,%x会扩展到文件的路径,但是通常只有在对prompt strings进行扩展时才这样做。要在替换中启用这些代码,我们可以在变量名称之前添加一个(%)标志:

% cat apath/test.sh
fpath=%x
echo "${(%)fpath}"

% source apath/test.sh
apath/test.sh

% cd apath
% source test.sh
test.sh

不太可能的匹配:逃生百分比和回退

到目前为止,我们可以使用,但是避免创建额外的fpath变量会比较整洁。不用将%x放在fpath中,我们可以使用:-并将%x放在后备字符串中:

% cat test.sh
echo "${(%):-%x}"

% source test.sh
test.sh

请注意,我们通常会在(%):-之间放置一个变量名,但是将其留空。无法声明或设置名称为空的变量,因此总是触发后备。

完成:print -P %x怎么样?

现在,我们几乎有了脚本的目录。我们本可以使用print -P %x来获得相同的文件路径,但黑客攻击次数较少,但是在我们这种情况下,我们需要将其作为参数传递给dirname,这将需要启动文件的开销。新的子外壳:

% cat apath/test.sh
dirname "$(print -P %x)"  # $(...) runs a command in a new process
dirname "${(%):-%x}"

% source apath/test.sh
apath
apath

事实证明,这种破解方式既高效又简洁。

答案 53 :(得分:0)

没有100%可移植且可靠的方法来请求指向当前脚本目录的路径。尤其是在cygwin / mingw / msys / Linux之类的不同后端之间。多年来,这个问题一直没有在bash中得到适当彻底的解决。

例如,如果您想在source命令之后请求路径以嵌套包含另一个bash脚本,而该bash脚本又使用相同的source命令来包含另一个bash脚本,则无法解决此问题bash脚本等等。

在使用source命令的情况下,我建议将source命令替换为以下内容:

function include()
{
  if [[ -n "$CURRENT_SCRIPT_DIR" ]]; then
    local dir_path=... get directory from `CURRENT_SCRIPT_DIR/$1`, depends if $1 is absolute path or relative ...
    local include_file_path=...
  else
    local dir_path=... request the directory from the "$1" argument using one of answered here methods...
    local include_file_path=...
  fi
  ... push $CURRENT_SCRIPT_DIR in to stack ... 
  export CURRENT_SCRIPT_DIR=... export current script directory using $dir_path ...
  source "$include_file_path"
  ... pop $CURRENT_SCRIPT_DIR from stack ...
}

从现在开始,include(...)的使用基于脚本中的上一个CURRENT_SCRIPT_DIR

仅当您可以用source命令替换所有include命令时,此方法才有效。如果不能,那么您别无选择。至少要等到bash解释器的开发人员做出明确的命令,才能请求当前正在运行的脚本目录路径。

答案 54 :(得分:0)

以下将脚本的目录路径存储在dir变量中

(也尝试使用Cygwin支持在Windows php中执行)

最后运行my-sample-app可执行文件,并使用"$@"

将所有参数传递给该脚本。
#!/usr/bin/env sh

dir=$(cd "${0%[/\\]*}" > /dev/null && pwd)

if [ -d /proc/cygdrive ]; then
    case $(which php) in
        $(readlink -n /proc/cygdrive)/*)
            # We are in Cygwin using Windows php, so the path must be translated
            dir=$(cygpath -m "$dir");
            ;;
    esac
fi

# Runs the executable which is beside this script
"${dir}/my-sample-app" "$@"

答案 55 :(得分:0)

我通常会这样做:

LIBDIR=$(dirname "$(readlink -f "$(type -P $0 || echo $0)")")
source $LIBDIR/lib.sh

答案 56 :(得分:0)

这就是我在脚本上的工作方式: pathvar="$( cd "$( dirname $0 )" && pwd )" 这将告诉您正在执行Launcher(当前脚本)的目录。

答案 57 :(得分:-1)

补充所有其他出色答案的另一种选择

  

$(cd“ $(dirname” $ {BASH_SOURCE [0]}“)”; pwd)“

答案 58 :(得分:-1)

cur_dir=`old=\`pwd\`; cd \`dirname $0\`; echo \`pwd\`; cd $old;`

答案 59 :(得分:-1)

没有forks(除了子shell)并且可以处理“外星人”路径名表单,就像有些人会声称的那样有新行;

IFS= read -rd '' DIR < <([[ $BASH_SOURCE != */* ]] || cd "${BASH_SOURCE%/*}/" >&- && echo -n "$PWD")

答案 60 :(得分:-1)

此解决方案仅适用于bash。请注意,如果您尝试在函数中找到路径,则通常提供的答案${BASH_SOURCE[0]}将无效。

我发现此行始终有效,无论该文件是来源还是作为脚本运行。

dirname ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

如果你想遵循符号链接,请在你上面的路径上使用readlink,递归或非递归。

这是一个试用它并将其与其他提议的解决方案进行比较的脚本。将其调用为source test1/test2/test_script.shbash test1/test2/test_script.sh

#
# Location: test1/test2/test_script.sh
#
echo $0
echo $_
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_dir="$(dirname "${cur_file}")"
source "${cur_dir}/func_def.sh"

function test_within_func_inside {
    echo ${BASH_SOURCE}
    echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}

echo "Testing within function inside"
test_within_func_inside

echo "Testing within function outside"
test_within_func_outside

#
# Location: test1/test2/func_def.sh
#
function test_within_func_outside {
    echo ${BASH_SOURCE}
    echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}

使用BASH_SOURCE环境变量及其关联FUNCNAME来解释单行工作的原因。

  

BASH_SOURCE

     

一个数组变量,其成员是源文件名,其中定义了FUNCNAME数组变量中相应的shell函数名。 shell函数$ {FUNCNAME [$ i]}在文件$ {BASH_SOURCE [$ i]}中定义,并从$ {BASH_SOURCE [$ i + 1]}调用。

     

FUNCNAME

     

一个数组变量,包含当前在执行调用堆栈中的所有shell函数的名称。索引为0的元素是当前正在执行的shell函数的名称。最底部的元素(索引最高的元素)是&#34; main&#34;。此变量仅在执行shell函数时存在。对FUNCNAME的分配无效并返回错误状态。如果未设置FUNCNAME,则会丢失其特殊属性,即使它随后被重置。

     

此变量可与BASH_LINENO和BASH_SOURCE一起使用。 FUNCNAME的每个元素在BASH_LINENO和BASH_SOURCE中都有对应的元素来描述调用堆栈。例如,$ {FUNCNAME [$ i]}是从行号$ {BASH_LINENO [$ i]}的$ {BASH_SOURCE [$ i + 1]}调用的。内置调用者使用此信息显示当前调用堆栈。

[来源:Bash手册]

答案 61 :(得分:-1)

使用奇怪的目录名查看底部的测试。

要将工作目录更改为Bash脚本所在的目录,您应该尝试使用测试,并使用 shellcheck 解决方案进行验证:

#!/bin/bash --
cd "$(dirname "${0}")"/. || exit 2

测试:

$ ls 
application
$ mkdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47testdir" "")"
$ mv application *testdir
$ ln -s *testdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47symlink" "")"
$ ls -lb
total 4
lrwxrwxrwx 1 jay stacko   46 Mar 30 20:44 \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'symlink -> \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'testdir
drwxr-xr-x 2 jay stacko 4096 Mar 30 20:44 \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'testdir
$ *testdir/application && printf "SUCCESS\n" ""
SUCCESS
$ *symlink/application && printf "SUCCESS\n" ""
SUCCESS

答案 62 :(得分:-1)

我通常在脚本的顶部包含以下内容,这些脚本适用于大多数情况:

[ "$(dirname $0)" = '.' ] && SOURCE_DIR=$(pwd) || SOURCE_DIR=$(dirname $0);
ls -l $0 | grep -q ^l && SOURCE_DIR=$(ls -l $0 | awk '{print $NF}');

如果从当前路径运行,则第一行根据pwd的值分配源,如果从其他地方调用,则分配dirname。

第二行检查路径以查看它是否为符号链接,如果是,则将SOURCE_DIR更新为链接本身的位置。

可能有更好的解决方案,但这是我设法自己想出的最干净的。

答案 63 :(得分:-1)

讨论很晚,但尝试这样的事情:

function get_realpath() {

if [[ -f "$1" ]]
then
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else
        # file *must* be local
        local tmppwd="$PWD"
    fi
else
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

function get_dirname(){

local realpath="$(get_realpath "$1")"
if (( $? )) # true when non-zero.
then
    return $? # failure
fi
echo "${realpath%/*}"
return 0 # success

}

# Then from the top level:
get_dirname './script.sh'

# Or Within a script:
get_dirname "$0"

# Can even test the outcome!
if (( $? )) # true when non-zero.
then
    exit 1 # failure
fi

这些功能和相关工具是我们产品的一部分,免费提供给社区,可以在GitHub上找到realpath-lib。它简单,干净且记录良好(非常适合学习),纯粹的Bash并且没有依赖性。也适合跨平台使用。因此,对于上面的示例,在脚本中您可以简单地:

source '/path/to/realpath-lib'

get_dirname "$0"

if (( $? )) # true when non-zero.
then
    exit 1 # failure
fi

这就是全部!

答案 64 :(得分:-1)

function getScriptAbsoluteDir { # fold>>
    # @description used to get the script path
    # @param $1 the script $0 parameter
    local script_invoke_path="$1"
    local cwd=`pwd`

    # absolute path ? if so, the first character is a /
    if test "x${script_invoke_path:0:1}" = 'x/'
    then
        RESULT=`dirname "$script_invoke_path"`
    else
        RESULT=`dirname "$cwd/$script_invoke_path"`
    fi
} # <<fold

答案 65 :(得分:-1)

(注意:这个答案经历了很多修改,因为我对原文进行了改进。截至上一次修订时,还没有人评论或投票。)

我正在为自己的利益添加这个答案 - 记住它并收集评论 - 就像其他人一样。答案的关键部分是我正在减少问题的范围:我禁止通过路径间接执行脚本(如/ bin / sh [相对于路径组件的脚本路径])。这可以被检测到,因为$ 0将是一个相对路径,它不会解析为相对于当前文件夹的任何文件。我相信使用“#!”直接执行机制总是导致绝对$ 0,包括在路径上找到脚本的时间。我还要求路径名和符号链接链上的任何路径名只包含一个合理的字符子集,特别是'\ n','&gt;','*'或'?'。这是解析逻辑所必需的。还有一些隐含的期望我不会参与(参见之前的答案&lt; 1&gt;),而且我不会尝试处理故意破坏$ 0(因此请考虑任何安全隐患)。我希望这几乎可以在任何类似Unix的系统上使用Bourne-like / bin / sh。

欢迎提出意见和建议!

&LT 1为卤素; https://stackoverflow.com/a/4794711/213180

#!/bin/sh
(
    path="${0}"
    while test -n "${path}"; do
        # Make sure we have at least one slash and no leading dash.
        expr "${path}" : / > /dev/null || path="./${path}"
        # Filter out bad characters in the path name.
        expr "${path}" : ".*[*?<>\\]" > /dev/null && exit 1
        # Catch embedded new-lines and non-existing (or path-relative) files.
        # $0 should always be absolute when scripts are invoked through "#!".
        test "`ls -l -d "${path}" 2> /dev/null | wc -l`" -eq 1 || exit 1
        # Change to the folder containing the file to resolve relative links.
        folder=`expr "${path}" : "\(.*/\)[^/][^/]*/*$"` || exit 1
        path=`expr "x\`ls -l -d "${path}"\`" : "[^>]* -> \(.*\)"`
        cd "${folder}"
        # If the last path was not a link then we are in the target folder.
        test -n "${path}" || pwd
    done
)

答案 66 :(得分:-2)

ME=`type -p $0`
MDIR="${ME%/*}"
WORK_DIR=$(cd $MDIR && pwd)

答案 67 :(得分:-2)

根据this answer,我建议将SCRIPT_HOME作为当前正在运行的bash脚本的包含文件夹的澄清版本

s=${BASH_SOURCE[0]} ; s=`dirname $s` ; SCRIPT_HOME=`cd $s ; pwd`
echo $SCRIPT_HOME

答案 68 :(得分:-2)

我想确保脚本在其目录中运行。所以

cd $(dirname $(which $0) )

在此之后,如果您真的想知道自己的运行位置,请运行以下命令。

DIR=$(/usr/bin/pwd)

答案 69 :(得分:-3)

chosen answer工作得很好。我会将解决方案发布给任何正在寻找更短替代方案的人,这些替代方案仍然可以解决源代码,执行,完整路径,相对路径和符号链接。最后,如果不能假设GNU的coreutils版本的readlink可用,这将在MacOS上起作用。

难题在于它没有使用Bash,但易于在Bash脚本中使用。尽管OP对解决方案的语言没有任何限制,但最好还是让大多数人呆在Bash世界中。这只是一种选择,并且可能是一种不受欢迎的选择。

PHP默认情况下在MacOS上可用,并安装在许多其他平台上,尽管不一定默认情况下。我意识到这是一个缺点,但是无论如何,我都会把它留给所有来自搜索引擎的人。

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"

答案 70 :(得分:-3)

即使已使用bash -c <script> Windows 调用脚本,此单行也适用于CYGWIN:

set mydir="$(cygpath "$(dirname "$0")")"

答案 71 :(得分:-6)

FOLDERNAME=${PWD##*/}

这是我所知道的最快捷方式

答案 72 :(得分:-6)

保持简单。

int y = x.LastIndexOf(".");