スクリプト自体からBashスクリプトのソースディレクトリを取得する方法


4951

どのように私はされているディレクトリのパスを取得んバッシュ、スクリプトが配置されているが内部でそのスクリプト?

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

$ ./application

69
ディレクトリ名の最後に改行がある場合、現在の解決策はどれも機能しません-それらはコマンド置換によって削除されます。これを回避するには、コマンド置換内に改行以外の文字を追加し、コマンド置換DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"なしで削除しDIR="${DIR%x}"ます。
l0b0 2012

80
@ jpmc26 2つの非常に一般的な状況があります:事故と妨害。誰かがどこかでを実行したからといって、スクリプトが予期しない方法で失敗することはありませんmkdir $'\n'
l0b0 2013年

24
この方法でシステムを妨害することを許可する人は、そのような問題を検出するためにbashに任せるべきではありません。私たちは、このような汚染のチェックなどのperlや慣行のようなものを持っている理由、これは(私はおそらくこと:)言って燃え上がっされている....この種のものは、どこにでも起こる見bashのを、使用しての25年間で、持っていたことがありません
osirisgothra

3
@ l0b0で同じ保護が必要でありdirname、ディレクトリが-(たとえば--help)で始まる可能性があることを考慮してください。DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}。おそらくこれはやり過ぎですか?
Score_Under、2015

56
この件については、このBash FAQを読むことをお勧めします。
Rany Albeg Wein、2016

回答:


6571
#!/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で呼び出すときなどのエスケープシーケンスを含む)、$CDPATHgotchasとstderr出力の副作用に注意してくださいupdate_terminal_cwd >&2。コマンド>/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'

27
このアプローチとuser25866の回答を融合してsource <script>、and で動作するソリューションを得ることができbash <script>ますDIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
Dan Molding

21
時々cdSTDOUTに何かを印刷します!たとえば、あなた$CDPATHが持って.いる場合。このケースをカバーするには、DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
user716468

182
この受け入れられた答えは大丈夫ではありません、それはシンボリックリンクでは機能せず、過度に複雑です。dirname $(readlink -f $0)正しいコマンドです。テストケースについてはgist.github.com/tvlooy/cbfbdb111a4ebad8b93eをご覧ください
tvlooy

167
@tvlooy IMOパスにスペースがあると失敗するため、答えもそのままではまったく問題ありません。改行文字とは対照的に、これはありそうにない、または珍しいことではありません。dirname "$(readlink -f "$0")"複雑さを増すことはなく、最小限のトラブルに対してより堅牢な公平な手段です。
エイドリアン・ギュンター

10
@tvlooyあなたのコメントはmacOS(またはおそらくBSD一般)互換ではありませんが、受け入れられた答えは互換性があります。readlink -f $0与えreadlink: illegal option -- fます。
Alexander Ljungberg、2017年

875

使用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

25
bashを超えた移植性のために、$ 0では必ずしも十分ではない場合があります。コマンドがパスで見つかった場合、これを機能させるために「type -p $ 0」を置き換える必要がある場合があります。
ダロン

10
@Darron:type -pスクリプトが実行可能である場合にのみ使用できます。また、スクリプトを使用bash test2.shして実行され、同じ名前の別のスクリプトがどこか別の場所で実行可能である場合にも、微妙な穴が開く可能性があります。
D.Shawley、2010

90
@Darron:しかし、質問にはタグが付けられてbashおり、ハッシュバングの行は明示的に言及/bin/bashしているので、バシズムに依存することはかなり安全だと私は思います。
Joachim Sauer

34
+1ですが、使用に関する問題dirname $0は、ディレクトリが現在のディレクトリである場合、を取得すること.です。スクリプトのディレクトリを変更して、dirname $0絶対パスであるかのように取得したパスを使用することを期待しているのでない限り、これは問題ありません。絶対パスを取得するpushd `dirname $0` > /dev/nullSCRIPTPATH=`pwd`popd > /dev/null、、pastie.org/1489386(しかし、確かにそのパスを拡張するためのより良い方法はありますか?)
TJ Crowder

9
@TJ Crowder dirname $0変数に割り当て、それを使用して次のようなスクリプトを起動すると問題が発生するかどうかはわかりません$dir/script.sh。これは、このタイプの事柄の90%の場合のユースケースだと思います。./script.shうまくいきます。
matt b

515

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

13
readlinkデフォルトのインストールでは、一部のプラットフォームでは使用できません。可能な場合は使用しないようにしてください
TL

43
回避の空白の問題にすべてを引用するように注意してください:export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
Catskul

13
OSXでは、Yosemite 10.10.1はの-fオプションとして認識されませんreadlinkstat -f代わりに使用するのが仕事です。ありがとう
cucu8 14年

11
OSXにはがありgreadlink、これは基本的にreadlink私たちがよく知っているものです。ここではプラットフォームに依存しないバージョンがある:dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`
ロバート・

6
@robertさん、こんにちは。FYI、greadlink簡単に自作を介してインストールすることができますbrew install coreutils
phatblat

184
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それがどのように呼び出されても、フルパスで与えられます。
これは、スクリプトの先頭にあることを確認してください。

このコメントとコードのコピーレフト、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
いいね!"pushd [...] popd / dev / null"をSCRIPT_PATH =に置き換えて、短くすることができますreadlink -f $(dirname "${VIRTUAL_ENV}")
e-satis

5
そして、pushdを使用する代わりに...; $(cd dirname "${SCRIPT_PATH}"&& pwd)を使用する方が良いでしょうか?しかし、とにかく素晴らしいスクリプトです!
ovane 2010

6
後で再び元に戻すことをcd期待して、スクリプトが現在のディレクトリの外に出るのは危険です。スクリプトにcdは、呼び出されたときに現在だったディレクトリにディレクトリを戻す権限がない場合があります。(pushd / popdも同様)
Adrian Pronk

7
readlink -fGNU固有です。BSDにreadlinkはそのオプションはありません。
Kara Brightwell、2014年

3
すべての不要なサブシェルとは何ですか?([ ... ])はに比べて効率が悪く[ ... ]、そのパフォーマンスに見合った分離が提供される利点はありません。
Charles Duffy

110

簡潔な答え:

`dirname $0`

または(できれば):

$(dirname "$0")

17
スクリプトを入手した場合は機能しません。"source my / script.sh"
Arunprasad Rajkumar

私はものを自動化し、同じディレクトリ内の他のスクリプトを頻繁に呼び出すbashスクリプトでこれを常に使用しています。私はsourceこれらを使用したことがなくcd $(dirname $0)、覚えやすいです。
kqw 2017年

16
@vidstige:${BASH_SOURCE[0]}代わりに$0動作するsource my/script.sh
Timothy Jones

@TimothyJonesは、bash以外のシェルから供給された場合に100%失敗します。${BASH_SOURCE[0]}まったく満足できません。${BASH_SOURCE:-0}はるかに優れています。
Mathieu CAROFF 2018年

106

使用できます$BASH_SOURCE

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

あなたが使用する必要があることを注意#!/bin/bashしていない#!/bin/sh、それはbashの延長だから。


14
私がするとき./foo/script、それ$(dirname $BASH_SOURCE)はそう./fooです。
2010年

1
@Till、この場合、realpathコマンドを使用して./foo/scriptの完全パスを取得できます。だから、dirname $(realpath ./foo/script) スクリプトのパスを提供します。
プルショタマンpoovai

73

これはそれを行うはずです:

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

これは、パス内のシンボリックリンクとスペースで機能します。

dirnameおよびのマニュアルページを参照してくださいreadlink

コメントトラックから、Mac OSでは動作しないようです。なぜかはわかりません。助言がありますか?


6
あなたのソリューションで、のようなスクリプトを起動./script.shショー.の代わりに、完全なディレクトリ・パス
ブルーノNegrãoZica

5
MacOSのreadlinkには-fオプションはありません。stat代わりに使用してください。しかし、それでも、.「this」ディレクトリにいるかどうかが表示されます。
Denis The Menace

2
Linuxではなく、* BSDであるため、coreutilsHomebrew からインストールしてMacOSでオプションgreadlinkを取得する必要があり-fます。
dragon788

あなたは、すべての右側を囲む二重引用符を追加する必要があります:DIR="$(dirname "$(readlink -f "$0")")"
hagello

60

pwd現在の作業ディレクトリdirnameを見つけたり、特定のファイルのディレクトリを見つけたりするために使用できます(実行されたコマンドはis $0なのでdirname $0、現在のスクリプトのディレクトリがわかるはずです)。

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

私は以下を提案します:

#!/bin/bash

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

echo "Directory is $directory"

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

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

ただ

cd `dirname $0`

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


9
次のように1行ですべて実行できますdirname $0
。DIRECTORY

スクリプトが別のスクリプトのソースであり、後者の名前を知りたい場合、これは機能しません。
reinierpost 2014年

52

このページに何度も何度もアクセスして、受け入れられた回答にワンライナーをコピーして貼り付けるのにうんざりしています。それに関する問題は、理解して覚えることが容易でないことです。

以下に覚えやすいスクリプトを示します。

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

2
それとも、もっと漠然と、1行に: DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
AGC

なぜこれは受け入れられた答えではないのですか?のrealpathループで「手動で」解決することと使用して何か違いはありますreadlinkか?でも、readlinkmanページは述べていますNote realpath(1) is the preferred command to use for canonicalization functionality.
User9123

1
ちなみに、後ではなく、realpathdirnameに適用すべきではありませんか?スクリプトファイル自体がシンボリックリンクである場合は、次のようになりDIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"ます。実際、サイモンが提案した答えに非常に近い。
User9123

@ User9123私が受け入れるのは、すべての人気のあるシェル/ディストリビューションと互換性があるようにすることです。さらに、何をしようとしているのかに応じて、ほとんどの場合、実際のソースのディレクトリではなく、シンボリックリンクが配置されているディレクトリを取得する必要があります。

37

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

./script

/usr/bin/script

script

最初と3番目の方法で$0は、完全なパス情報がありません。2番目と3番目でpwdは、動作しません。3番目の方法でディレクトリを取得する唯一の方法は、パスを実行して、一致するファイルを見つけることです。基本的に、コードはOSの処理をやり直す必要があります。

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


9
彼のコメントを反証するつもりであれば、スクリプトがコード例とともに格納されている場所にスクリプトがアクセスできることを証明してください。
Richard Duerr、2015年

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

これは選択した回答よりもはるかに短いです。そして、同様に動作するように見えます。これは、人々が見落とさないように、1000票に値します。
Patrick

2
上記の回答の多くが詳細に説明しているように、スクリプトの呼び出し方法によっては、正しい情報が得られること$0pwd、保証されることもありません。
IMSoP 2013

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

私は、好む$BASH_SOURCE超える$0それもbashのに精通していない読者のために、明示的なので、。 $(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
blobmaster

32

これにより、Mac OS X 10.6.6の現在の作業ディレクトリが取得されます。

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

27

これはLinux固有ですが、以下を使用できます。

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

1
また、bash固有ですが、bashの動作が変更された可能性がありますか?/proc/fd/$$/255ディレクトリではなく、ttyを指しているようです。たとえば、現在のログインシェルでは、ファイル記述子0、1、2、255はすべてを参照し/dev/pts/4ます。いずれにせよ、bashマニュアルではfd 255について言及されていないため、この動作に依存することはおそらく賢明ではありません。\
Keith Thompson

2
対話型シェル!=スクリプト。とにかくrealpath ${BASH_SOURCE[0]};行くのが最善の方法のようです。
スティーブベイカー

23

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

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

# test
echo $SCRIPT_PATH

4
スクリプトを単独で実行したり、sudoを使用したりすると、これで成功しましたが、source ./script.shを呼び出すと失敗しました
Michael R

また、がcd新しいパス名を出力するように構成されている場合は失敗します。
アーロンディグラ2013年

18

私はこれらすべてを試しましたが、どれもうまくいきませんでした。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"`"

4
スクリプトが別のスクリプトから供給されている場合は機能しません。
reinierpost 2014年

$ 0の最後の部分が別のディレクトリのエントリを指すシンボリックリンクである場合、これは機能しません(ln -s ../bin64/foo /usr/bin/foo)。
ハゲロ

17

シンプルで正しい方法は次のとおりです。

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

説明:

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

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

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


1
greadlink -fsourceMacでスクリプトを実行すると、残念ながら効果的に機能しません:(
Gabe Kopley

17

これを行う最も短くて最もエレガントな方法は次のとおりです。

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

これはすべてのプラットフォームで機能し、非常にクリーンです。

詳細については、「bashスクリプトが格納されているディレクトリ参照してください。


素晴らしいクリーンなソリューションですが、ファイルがシンボリックリンクされている場合、これは機能しません。
-ruuter、

16

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

# 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

これが本物!シンプルでshも動作します!単純なdirname "$0"ベースのソリューションの問題:スクリプトが内にあり$PATH、パスなしで呼び出された場合、誤った結果が返されます。
Notinlist 2014年

@Notinlistそうではありません。スクリプトが経て発見された場合PATH$0絶対ファイル名が含まれます。スクリプトがを含む相対または絶対ファイル名で呼び出された場合は/、それ$0が含まれます。
Neil Mayhew、2016

ソーススクリプトでは機能しません。
アミットナイドゥ

16

これは、ソリューションe-satisのわずかな改訂であり、3bcdnlklvc04a が回答で指摘しまし

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

これは、リストされているすべてのケースで機能します。

これはkonsoleboxのおかげでpopd、失敗した後pushdを防ぎます。


これは、シンボリックリンクの名前だけでなく、「実際の」dirnameを取得するために完全に機能します。ありがとうございました!
ジェイテイラー

1
改善SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
konsolebox 2014

@konsolebox、あなたは何を防御しようとしていますか?私は通常、論理条件をインライン化するのが好きですが、pushdで見られた具体的なエラーは何ですか?空のSCRIPT_DIRを返すのではなく、直接処理する方法を見つけたいと思います。
Fuwjax、2015年

@Fuwjax Naturalプラクティスpopdpushdは、失敗するケース(まれな場合でも)を回避します。そしてpushd失敗した場合、あなたは何の価値があると思いますSCRIPT_DIRか?アクションは、論理的に見えるものやユーザーが好むものによって異なりますが、間違いpopdは間違っています。
konsolebox 2015年

これらのpushd popd危険性はすべて、単にそれらを削除し、代わりにコマンド置換で囲まれたcd+ を使用することで回避できますpwdSCRIPT_DIR=$(...)
アミットナイドゥ

16

GNU coreutils readlink(例:linux)を備えたシステムの場合:

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

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


3
スクリプトのソースがでない限り。または 'source'の場合は、それをソースとするスクリプトのままです。または、コマンドラインからの場合は、 '-bash'(ttyログイン)または 'bash'( 'bash -l'を介して呼び出されます)または '/ bin / bash '(インタラクティブな非ログインシェルとして呼び出されます)
osirisgothra

dirname通話の前後に2つ目の引用符を追加しました。ディレクトリパスにスペースが含まれている場合に必要です。
user1338062

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

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

scriptdir=`dirname "$PRG"`

さまざまなシステムでテストしていません。しかし、私にとって、このソリューションは少なくともUbuntuですぐに機能するものです!
Natus Drew

$0ソーススクリプトでは機能しません
アミットナイドゥ

13

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

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

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


4
あなたsource.スクリプトなら壊れます。これらの状況で$_は、の前に実行した最後のコマンドの最後のパラメーターが含まれます.$BASH_SOURCE毎回動作します。
2014年

11

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

  • 絶対パスまたは相対パス
  • ファイルとディレクトリのソフトリンク
  • 呼び出しとしてscriptbash scriptbash -c scriptsource script、または. script
  • ディレクトリおよび/またはファイル名のスペース、タブ、改行、ユニコードなど
  • ハイフンで始まるファイル名

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

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

これには少し醜い点がありますが、修正はコンパクトで理解しやすいものです。私たちはbashプリミティブのみを使用しているわけではありませんがreadlink、タスクを大幅に簡略化するので大丈夫です。echo X追加Xファイル名に任意の末尾の空白は食わないし、パラメータ置換ように可変文字列の末尾に${VAR%X}行の終わりには、取り除きますXreadlink独自の改行が追加されるため(以前のトリックではない場合、コマンド置換で通常使用されます)、それも取り除く必要があります。これは$''、次のようなエスケープシーケンスを使用できる引用スキームを使用して最も簡単に実行できます。\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"

私が手/dev/pts/30のUbuntu 14.10のデスクトップ上のbashと。
Dan Dascalescu

@DanDascalescuワンライナーを使用していますか?または下部にある完全なコードスニペット?そして、あなたはそれにトリッキーなパス名を与えましたか?
billyjmc 2015

1つのラインプラスに別の行はecho $resolved、私はとしてそれを保存しdchmod +x d./d
Dan Dascalescu、2015

@DanDascalescuスクリプトの最初の行は、次のようにする必要があります#!/bin/bash
billyjmc

10

使ってみてください:

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

1
私が知りたいのは、なぜこの方法が良くないのですか?それは私にとって悪くないと正しいように見えました。なぜ反対票が投じられたのか誰か説明できますか?
Shou Ya

7
realpathは標準のユーティリティではありません。
スティーブベネット

2
Linuxでは、realpathは標準のユーティリティ(GNU coreutilsパッケージの一部)ですが、bashビルトイン(つまり、bash自体によって提供される関数)ではありません。Linuxを実行している場合、このメソッドはおそらく機能しますが、関数を含め、どこでもこのメソッドが機能するように$0forを置き換え${BASH_SOURCE[0]}ます。
Doug Richardson

3
この回答の操作の順序は間違っています。あなたはする必要がまず、解決シンボリックリンクを、次に行うdirnameの最後の部分ので、$0シンボリックリンク自体と同じディレクトリにないファイルを指していることをシンボリックリンクかもしれません。この回答で説明されている解決策は、ターゲットのディレクトリではなく、シンボリックリンクが保存されているディレクトリのパスを取得するだけです。さらに、このソリューションには引用がありません。パスに特殊文字が含まれている場合は機能しません。
hagello 2015

3
dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
Kostiantyn Ponomarenko

9

次の相互互換性のあるソリューションを試してください。

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

このようなコマンドとして、realpathまたはreadlink利用できないかもしれない(オペレーティングシステムに応じて)。

注:Bashでは、${BASH_SOURCE[0]}ではなくを使用することをお勧めします$0。そうしないと、ファイル(source/ .)をソースするときにパスが壊れる可能性があります。

または、bashで次の関数を試すこともできます。

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

この関数は1つの引数を取ります。引数がすでに絶対パスを持っている場合はそのまま出力し、そうでない場合は$PWD変数+ファイル名の引数(./プレフィックスなし)を出力します。

関連:


realpath関数について詳しく説明してください。
Chris

1
@Chris realpath関数は1つの引数を取ります。引数にすでに絶対パスがある場合はそのまま出力し、そうでない場合は$PWD+ファイル名(./接頭辞なし)を出力します。
ケノーブ2015年

スクリプトがシンボリックリンクされている場合、互換性のあるソリューションは機能しません。
Jakub Jirutka

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

8

これらは、スクリプト情報を取得するための短い方法です。

フォルダーとファイル:

    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



シンプルな実用版を見つけるのは難しいので、私の答えは大丈夫だと思います。ここでは、たとえばcd + pwd、dirname + realpathまたはdirname + readlinkなど、好きなコードを使用できます。以前はすべての部分が存在していて、ほとんどの回答が複雑で過負荷であるかどうかはわかりません。ここで、使用したいコードを確認できます。私は将来的に必要があるとして、少なくともそれを削除しないでください:D
User8461

8

これはbash-3.2で機能します。

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

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

これは次のようにして解決されます(内部A)。

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

ユーザーの場所やスクリプトの呼び出し方は関係ありません。これは常に機能します。


3
ポイントwhichは非常に議論の余地があります。typehashおよびその他の組み込みコマンドはbashで、より良い同じことを行います。which移植性は少し高いですが、whichtcshのような他のシェルで使用されているものとは異なり、組み込みです。
モニカを回復する

"常に"?どういたしまして。which外部ツールであるため、親シェルと同じように動作すると信じる理由はありません。
Charles Duffy

7

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

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

Bash以外には依存していません。使用dirnamereadlinkおよびbasenameすべての可能性であれば、彼らが最高の回避されているので、最終的には、互換性の問題につながります。


2
おそらくそれにスラッシュを追加する必要があります:"$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )"。そうしないと、ルートディレクトリに問題が発生します。また、なぜエコーを使用する必要があるのですか?
konsolebox 2014

dirnameおよびbasenamePOSIXはなぜ避け、それらを使用して、標準化されていますか?リンク:dirnamebasename
myrdd 2018年

2つの余分なプロセスフォークを回避し、シェルの組み込みに固執することが、1つの理由である可能性があります。
アミットナイドゥ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.