5658

Bashスクリプトが配置されているディレクトリのパスを、そのスクリプトで取得するにはどうすればよいですか?

別のアプリケーションのランチャーとしてBashスクリプトを使用したいと思います。作業ディレクトリをBashスクリプトが配置されているディレクトリに変更したいので、次のようにそのディレクトリ内のファイルを操作できます。

$ ./application
4

72 に答える 72

7469
#!/usr/bin/env bash

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

どこから呼び出されても、スクリプトの完全なディレクトリ名を提供する便利なワンライナーです。

スクリプトを見つけるために使用されるパスの最後のコンポーネントがシンボリック リンクでない限り、機能します (ディレクトリ リンクは問題ありません)。スクリプト自体へのリンクも解決したい場合は、複数行のソリューションが必要です。

#!/usr/bin/env 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 )

sourceこの最後のものは、エイリアスbash -c、、、、シンボリックリンクなどの任意の組み合わせで機能します。

注意:このスニペットを実行cdする前に別のディレクトリに移動すると、結果が正しくない可能性があります。

また、ユーザーが出力を stderr にリダイレクトするように cd をスマートにオーバーライドした場合 ( Mac で呼び出す場合などのエスケープ シーケンスを含む)、$CDPATHgotchasと stderr 出力の副作用に注意してください。コマンドの最後にupdate_terminal_cwd >&2追加すると、両方の可能性が処理されます。>/dev/null 2>&1cd

それがどのように機能するかを理解するには、次のより詳細なフォームを実行してみてください。

#!/usr/bin/env 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'
于 2008-10-29T08:36:45.740 に答える
1067

使用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
于 2008-09-12T20:49:28.600 に答える
603

このdirnameコマンドは最も基本的なもので、$0(スクリプト名) 変数からファイル名までのパスを解析するだけです。

dirname "$0"

しかし、matt bが指摘したように、返されるパスは、スクリプトの呼び出し方法によって異なります。pwdスクリプトが存在するディレクトリではなく、現在のディレクトリが何であるかのみを示すため、ジョブを実行しません。さらに、スクリプトへのシンボリックリンクが実行された場合、への(おそらく相対)パスを取得します実際のスクリプトではなく、リンクが存在する場所。

他の人はコマンドについて言及していreadlinkますが、最も簡単には、次を使用できます。

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

readlinkスクリプト パスをファイル システムのルートからの絶対パスに解決します。そのため、1 つまたは 2 つのドット、チルダ、および/またはシンボリック リンクを含むパスは、フル パスに解決されます。

これらのそれぞれを示すスクリプトを次に示します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
于 2009-09-26T20:38:02.093 に答える
195
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どのように呼び出されても、フルパスで指定されます。

スクリプトの開始時にこれを見つけてください。

于 2008-10-07T16:12:14.120 に答える
125

使用できます$BASH_SOURCE

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

#!/bin/bashand は#!/bin/shBash 拡張機能であるため、使用する必要があることに注意してください。

于 2008-09-12T20:50:55.930 に答える
123

簡潔な答え:

`dirname $0`

または(できれば):

$(dirname "$0")
于 2008-12-03T12:57:40.373 に答える
73

pwd現在の作業ディレクトリdirnameを検索したり、特定のファイルのディレクトリを検索したりするために使用できます (実行されたコマンドは$0であるためdirname $0、現在のスクリプトのディレクトリが表示されます)。

ただし、dirnameファイル名のディレクトリ部分を正確に示します。これは、現在の作業ディレクトリに関連している可能性が高いです。何らかの理由でスクリプトがディレクトリを変更する必要がある場合、からの出力dirnameは無意味になります。

次のことをお勧めします。

#!/bin/bash

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

echo "Directory is $directory"

このようにして、相対ディレクトリではなく絶対ディレクトリを取得します。

スクリプトは別の Bash インスタンスで実行されるため、後で作業ディレクトリを復元する必要はありませんが、何らかの理由でスクリプトを元に戻したい場合はpwd、変数に値を簡単に割り当てることができます。将来の使用のために、ディレクトリを変更する前に。

ただ

cd `dirname $0`

質問の特定のシナリオを解決します。一般的に、より便利な方法への絶対パスがあることがわかりました。

于 2008-09-15T19:55:06.053 に答える
42
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
于 2010-10-07T17:34:23.570 に答える
42

これは、他の人が成功させたほど簡単ではないと思います。 pwd現在のディレクトリは必ずしもスクリプトのあるディレクトリではないため、機能しません。 $0常に情報を持っているわけではありません。スクリプトを呼び出すには、次の 3 つの方法を検討してください。

./script

/usr/bin/script

script

1 番目と 3 番目の方法で$0は、フル パス情報がありません。2番目と3番目では、pwd機能しません。3 番目の方法でディレクトリを取得する唯一の方法は、パスを実行して、正しい一致を持つファイルを見つけることです。基本的に、コードは OS が行うことをやり直す必要があります。

あなたが求めていることを行う1つの方法は、/usr/shareディレクトリ内のデータをハードコーディングし、フルパスで参照することです。とにかく、データは/usr/binディレクトリにあるべきではないので、これはおそらく行うべきことです。

于 2008-09-13T01:07:11.867 に答える
39
$(dirname "$(readlink -f "$BASH_SOURCE")")
于 2011-07-27T07:40:31.753 に答える
38

これは、 Mac OS X v10.6.6 (Snow Leopard)の現在の作業ディレクトリを取得します。

DIR=$(cd "$(dirname "$0")"; pwd)
于 2010-12-30T07:20:59.243 に答える
29

これは Linux 固有のものですが、次のものを使用できます。

SELF=$(readlink /proc/$$/fd/255)
于 2008-09-16T19:55:33.953 に答える
25

以下は POSIX 準拠のワンライナーです。

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

# test
echo $SCRIPT_PATH
于 2013-03-06T19:07:19.720 に答える
20
#!/bin/sh
PRG="$0"

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

scriptdir=`dirname "$PRG"`
于 2008-09-13T01:28:47.010 に答える
19

私はこれらすべてを試しましたが、どれもうまくいきませんでした。1つは非常に近いものでしたが、小さなバグがあり、ひどく壊れていました。パスを引用符で囲むのを忘れていました。

また、多くの人は、スクリプトをシェルから実行していると想定しているため、新しいスクリプトを開いたときに、デフォルトで自宅に設定されていることを忘れています。

サイズについては、このディレクトリを試してください。

/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text

これにより、実行方法や場所に関係なく正しくなります。

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

したがって、実際に役立つように、実行中のスクリプトのディレクトリに変更する方法は次のとおりです。

cd "`dirname "$0"`"
于 2010-09-09T07:19:22.913 に答える
19

簡単で正しい方法は次のとおりです。

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

説明:

  • ${BASH_SOURCE[0]}- スクリプトへのフル パス。この値は、スクリプトがソースされている場合でも正しくなります。たとえば、bashsource <(echo 'echo $0')が出力されますが、これを に置き換えると、スクリプトのフル パスが出力されます。(もちろん、これはあなたが Bash に依存しても問題ないことを前提としています。)${BASH_SOURCE[0]}

  • readlink -f- 指定されたパス内のシンボリック リンクを再帰的に解決します。これは GNU 拡張機能であり、(たとえば) BSD システムでは使用できません。Mac を実行している場合は、Homebrew を使用して GNU をインストールcoreutilsし、これをgreadlink -f.

  • もちろんdirname、パスの親ディレクトリを取得します。

于 2016-02-19T20:30:08.713 に答える
18

これは、ソリューション e-satis と 3bcdnlklvc04aが回答で指摘したものを少し修正したものです。

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}

これは、リストされているすべてのケースで引き続き機能するはずです。

popdこれにより、失敗した後に防止されpushdます。konsolebox に感謝します。

于 2010-04-13T22:12:31.037 に答える
17

GNU coreutils を備えたシステムreadlink(Linux など) の場合:

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

whenにスクリプト ファイル名が含まれているBASH_SOURCE場合は、使用する必要はありません。$0

于 2014-05-28T07:16:09.843 に答える
17

私は次のようなものを使用します:

# 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
于 2012-11-21T03:57:55.027 に答える
16

使用してみてください:

real=$(realpath "$(dirname "$0")")
于 2012-02-06T10:43:18.737 に答える
14

$_の代替として言及する価値があり$0ます。Bash からスクリプトを実行している場合、受け入れられた回答は次のように短縮できます。

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

これは、スクリプトの最初のステートメントでなければならないことに注意してください。

于 2011-09-16T19:05:51.450 に答える
13

これは Bash 3.2 で機能します。

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

~/binにディレクトリがある場合は、このディレクトリ内$PATHにあります A。スクリプトのソース~/bin/lib/Bです。含まれているスクリプトがサブディレクトリ内の元のスクリプトに対して相対的な場所を知っていますがlib、ユーザーの現在のディレクトリに対して相対的な場所はわかりません。

これは、次の方法で解決されます ( 内A)。

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

ユーザーがどこにいても、どのようにスクリプトを呼び出してもかまいません。これは常に機能します。

于 2008-10-14T16:39:03.097 に答える
10

与えられた多くの回答を比較し、よりコンパクトなソリューションをいくつか考え出しました。これらは、次のお気に入りの組み合わせから生じるクレイジーなエッジケースをすべて処理しているようです。

  • 絶対パスまたは相対パス
  • ファイルとディレクトリのソフト リンク
  • 、、、、またはscript_ bash script_ bash -c script_source script. script
  • ディレクトリやファイル名のスペース、タブ、改行、Unicode など
  • ハイフンで始まるファイル名

Linux から実行している場合proc、現在実行中のスクリプトの完全に解決されたソースを見つけるには、ハンドルを使用するのが最善の解決策のようです (対話型セッションでは、リンクはそれぞれの を指します/dev/pts/X)。

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

これには少し醜い部分がありますが、修正はコンパクトで理解しやすいものです。私たちは bash プリミティブだけを使用しているわけではありませんがreadlink、タスクが大幅に簡素化されるため、それで問題ありません。変数文字列の末尾に をecho X追加しXて、ファイル名の末尾の空白が食べられないようにし${VAR%X}、行末のパラメーター置換によって . が削除されXます。独自の改行を追加するためreadlink(以前の策略がなければ、通常はコマンド置換で使用されます)、それも削除する必要があります。$''これは、次のようなエスケープ シーケンスを使用できる引用スキームを使用して最も簡単に実現できます。\n改行を表す (これは、不正な名前のディレクトリとファイルを簡単に作成する方法でもあります)。

上記は、Linux で現在実行中のスクリプトを見つけるためのニーズをカバーするはずですが、procファイルシステムを自由に使用できない場合、または他のファイルの完全に解決されたパスを見つけようとしている場合は、おそらく以下のコードが役に立ちます。上記のワンライナーからわずかに変更しただけです。奇妙なディレクトリ/ファイル名で遊んでいる場合は、改行などの代わりに「簡略化された」パスを出力するため、 と の両方lsで出力を確認するreadlinkと有益です。ls?

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"
于 2013-04-21T13:40:41.247 に答える
9

私はこれを持っていると信じています。私はパーティーに遅れましたが、このスレッドに出くわした場合、ここにいることに感謝する人もいると思います. コメントは次のように説明する必要があります。

#!/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
于 2013-12-21T17:47:37.047 に答える
8

うーん、パス内にbasenameあり、dirnameそれをカットするつもりがなく、パスを歩くのが難しい場合 (親が PATH をエクスポートしていなかったら?!)。

ただし、シェルにはスクリプトへのオープン ハンドルが必要であり、Bash ではハンドルは #255 です。

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

私のために働きます。

于 2009-01-24T17:01:27.353 に答える
7

私の見解では、最適なコンパクトなソリューションは次のとおりです。

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

Bash 以外に依存するものはありません。dirnamereadlinkおよびの使用basenameは最終的に互換性の問題につながるため、可能な限り避けるのが最善です。

于 2013-10-08T14:24:13.443 に答える
5

OS XでFinderによって起動された Bash スクリプトでは、これらの他の回答はどれも機能しませんでした。私は最終的に使用しました:

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

それはきれいではありませんが、仕事を成し遂げます。

于 2010-09-22T23:22:34.757 に答える
5

readlinkを組み合わせて名前を正規化し (シンボリックリンクの場合はソースに戻るというボーナス付き) 、ディレクトリ名を抽出するにはdirnameを使用します。

script="`readlink -f "${BASH_SOURCE[0]}"`"
dir="`dirname "$script"`"
于 2010-10-13T07:42:58.977 に答える
4

厄介なことに、実行スクリプトがシンボリックリンクの場合、Linux と macOS の両方で動作する唯一のワンライナーです。

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

または、同様に、python3 pathlib モジュールを使用します。

SCRIPT_DIR=$(python3 -c "from pathlib import Path; print(Path('${BASH_SOURCE[0]}').resolve().parent)")

Linux および macOS でテストされ、この要旨の他のソリューションと比較されました: https://gist.github.com/ptc-mrucci/61772387878ed53a6c717d51a21d9371

于 2021-06-20T13:11:28.417 に答える
2

以下は、スクリプトの現在のディレクトリを返します

  • 供給されているかどうかに関係なく機能します
  • 現在のディレクトリまたは他のディレクトリで実行すると機能します。
  • 相対ディレクトリが使用されている場合に機能します。
  • bash で動作しますが、他のシェルについては不明です。
/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"
于 2019-10-08T12:37:16.200 に答える
2

Most answers either don't handle files which are symlinked via a relative path, aren't one-liners or don't handle BSD (Mac). A solution which does all three is:

HERE=$(cd "$(dirname "$BASH_SOURCE")"; cd -P "$(dirname "$(readlink "$BASH_SOURCE" || echo .)")"; pwd)

First, cd to bash's conception of the script's directory. Then readlink the file to see if it is a symlink (relative or otherwise), and if so, cd to that directory. If not, cd to the current directory (necessary to keep things a one-liner). Then echo the current directory via pwd.

You could add -- to the arguments of cd and readlink to avoid issues of directories named like options, but I don't bother for most purposes.

You can see the full explanation with illustrations here:

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

于 2021-04-18T13:32:21.303 に答える
2

このソリューションは Bash にのみ適用されます。${BASH_SOURCE[0]}関数内からパスを見つけようとすると、一般的に提供される回答は機能しないことに注意してください。

ファイルがソースであるか、スクリプトとして実行されているかに関係なく、この行は常に機能することがわかりました。

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

シンボリックリンクをたどりたい場合はreadlink、上記のパスを再帰的または非再帰的に使用します。

これを試して、他の提案されたソリューションと比較するためのスクリプトを次に示します。source test1/test2/test_script.shまたはとして呼び出しbash 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 配列変数内の対応するシェル関数名が定義されているソース ファイル名をメンバーとする配列変数。シェル関数 ${FUNCNAME[$i]} はファイル ${BASH_SOURCE[$i]} で定義され、${BASH_SOURCE[$i+1]} から呼び出されます。

機能名

現在実行コール スタックにあるすべてのシェル関数の名前を含む配列変数。インデックス 0 の要素は、現在実行中のシェル関数の名前です。一番下の要素 (インデックスが最も高い要素) は "main" です。この変数は、シェル関数が実行されている場合にのみ存在します。FUNCNAME への代入は効果がなく、エラー ステータスが返されます。FUNCNAME が設定されていない場合、後でリセットされても、その特別なプロパティは失われます。

この変数は、BASH_LINENO および BASH_SOURCE で使用できます。FUNCNAME の各要素には、BASH_LINENO と BASH_SOURCE に対応する要素があり、コール スタックを記述します。たとえば、${FUNCNAME[$i]} は、ファイル ${BASH_SOURCE[$i+1]} の行番号 ${BASH_LINENO[$i]} から呼び出されました。呼び出し元のビルトインは、この情報を使用して現在の呼び出しスタックを表示します。

[出典: Bash マニュアル]

于 2014-08-31T22:10:46.977 に答える
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)信頼できるように見えるだけです。

于 2021-01-20T03:20:25.963 に答える
2

この方法の利点の 1 つは、Bash 自体の外部に何も関与せず、サブシェルも fork しないことです。

最初に、パターン置換を使用して、で始まらないもの/(つまり、相対パス) をに置き換え$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}"}

これは、実行可能なスクリプトでも機能します。より長いですが、より多価です。

于 2020-12-23T20:55:00.727 に答える
0

私は通常、次のことを行います。

LIBDIR=$(dirname "$(readlink -f "$(type -P $0 || echo $0)")")
source $LIBDIR/lib.sh
于 2009-01-24T16:32:59.127 に答える
0
于 2013-11-26T21:10:31.463 に答える
0

これが純粋なBashソリューションです

$ cat a.sh
BASENAME=${BASH_SOURCE/*\/}
DIRNAME=${BASH_SOURCE%$BASENAME}.
echo $DIRNAME

$ a.sh
/usr/local/bin/.

$ ./a.sh
./.

$ . a.sh
/usr/local/bin/.

$ /usr/local/bin/a.sh
/usr/local/bin/.
于 2013-02-14T18:14:23.373 に答える
-1

重要な部分は、問題の範囲を縮小していることです。パスを介したスクリプトの間接的な実行を禁止しています (のように/bin/sh [script path relative to path component])。

$0これは、現在のフォルダーに関連するファイルに解決されない相対パスになるため、検出できます。このメカニズムを使用した直接実行は、スクリプトがパス上で見つかった場合を含め、#!常に絶対値になると思います。$0

また、パス名とシンボリック リンクのチェーンに沿ったすべてのパス名には、適切な文字のサブセットのみが含まれている必要があります。特に\n、、、または. これは、解析ロジックに必要です。>*?

私が踏み込まないいくつかの暗黙の期待があり (この回答を見てください)、意図的な妨害行為を処理しようとはしません$0(したがって、セキュリティへの影響を考慮してください)。Bourne のような/bin/sh.

#!/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
)
于 2015-10-09T08:16:09.547 に答える
-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を割り当てます。

2 行目では、パスがシンボリック リンクであるかどうかを確認し、シンボリック リンクである場合は、SOURCE_DIR をリンク自体の場所に更新します。

おそらくもっと良い解決策があるでしょうが、これは私が自分で思いついた最もクリーンなものです。

于 2013-06-09T15:35:27.027 に答える
-1
cur_dir=`old=\`pwd\`; cd \`dirname $0\`; echo \`pwd\`; cd $old;`
于 2014-05-24T15:47:25.460 に答える
-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
于 2016-03-31T00:49:14.143 に答える
-1

フォーク (サブシェル以外) はなく、次のように改行を含むような「エイリアン」パス名形式を処理できます。

IFS= read -rd '' DIR < <([[ $BASH_SOURCE != */* ]] || cd "${BASH_SOURCE%/*}/" >&- && echo -n "$PWD")
于 2014-07-03T04:58:44.697 に答える
-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
于 2009-11-29T11:44:13.250 に答える
-3

スクリプトがそのディレクトリで実行されていることを確認したいと思います。それで

cd $(dirname $(which $0) )

この後、実行している場所を本当に知りたい場合は、以下のコマンドを実行します。

DIR=$(/usr/bin/pwd)
于 2009-02-12T15:06:33.450 に答える
-3

現在のスクリプト ディレクトリへのパスを要求する、100% 移植可能で信頼できる方法はありません。特に、 CygwinMinGWMSYS 、 Linux などの異なるバックエンド間では、この問題は Bash では適切かつ完全に解決されていませんでした。

たとえば、sourceコマンドの後にパスを要求して、別の Bash スクリプトをネストしてインクルードし、同じsourceコマンドを使用して別の 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 ...
}

これからは、 is の使用はスクリプト内のinclude(...)previous に基づいていCURRENT_SCRIPT_DIRます。

sourceこれは、すべてのコマンドをコマンドで置き換えることができる場合にのみ機能しますinclude。できないなら仕方がない。少なくとも、Bash インタープリターの開発者が、現在実行中のスクリプト ディレクトリ パスを要求する明示的なコマンドを作成するまでは。

これに最も近い私自身の実装: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/bash/tacklelib/bash_tacklelib
https://github.com/andry81/tacklelib/tree/trunk/bash/タックルライブラリ/bash_tacklelib

(関数を検索tkl_include

于 2019-05-07T22:06:25.190 に答える
-6

複雑にしないでおく。

#!/usr/bin/env bash
sourceDir=`pwd`
echo $sourceDir
于 2020-02-24T17:58:05.090 に答える