ソースのシェルスクリプトの場所を見つける


8

ソースシェルスクリプトがその場所を知っている可能性はありますか?私が読んだソースとシェルスクリプトのパスを決定するが、答えはに焦点を当てるbashtcshPOSIXシェルが使用されている場合は失敗と。$0も解決策ではなく、間違った結果をもたらします

ソリューションは100%信頼できる必要はありません。パスにハードリンクまたはシンボリックリンクが含まれていることはほとんどありません。

# sourcing the script should yield the absolute path to the script
. somedir/thescript

# within “thescript”
-> /tmp/foo/bar/somedir

背景:このスクリプトはbin、ソースとなるスクリプトに対して既知の場所(アーキテクチャごとに異なります)のディレクトリにある数十のバイナリを含む既存のアプリケーションの一部です。アプリケーションを使用するには、binディレクトリの先頭にを付けるスクリプトをユーザーがに提供するPATHため、アプリケーションのバイナリを現在のシェル(シェルのいずれか)で簡単に呼び出すことができます。


このリンクが、paste.ubuntu.com / 6242655
Rahul Patil

@RahulPatilこれはbashでのみ機能し、非常に移植性がありません。
Marco

1
私は答えがノーだとかなり確信しています。移植可能なメソッドがないため、シェル固有のメソッドのみが見つかりました。これは何のために必要ですか?または. /path/to/scriptなどとは異なる何かをするようにスクリプトのユーザーに指示できますMARCO_DIR=/path/to; . $MARCO_DIR/scripteval "$(/path/to/script)"?@RahulPatil BASH_SOURCEは明らかにbashに固有です。
Gilles「SO-悪をやめる」

@ギレス私が言ったように、それは非常に堅牢である必要はありません。しかし、POSIXシェルで最も単純なケースでもリモートで機能する方法は見つかりませんでした。(したがって、当面の解決策は、このアプリケーションを使用するには、サポートされているシェルの1つが必要であることです。)答えが「いいえ…」の場合、答えとして自由に投稿してください。
Marco

@Gillesアプリケーションの設定方法を変更することはできません。それはインストールを壊すでしょう。そのため、スクリプトの呼び出し方法を変更せずにスクリプトをより堅牢にしたかったのです。最終的にはPATHを設定するだけなので、スクリプトが失敗した場合の代替は、バイナリを見つけて環境へのパスを追加することです。
Marco

回答:


9

POSIX仕様の拡張機能を提供するシェルを使用していない限り、ソーススクリプトの場所は利用できません。これは、次のスニペットでテストできます。

env -i PATH=/usr/bin:/bin sh -c '. ./included.sh' | grep included

どこincluded.sh

echo "$0"
set

bashでは、ソーススクリプトの名前はにあり$BASH_SOURCEます。zsh(shまたはksh互換モードではなく、zsh互換モード)では、それが含まれます$0(関数で$0は、代わりに関数名です)。pdkshおよびdashでは使用できません。ksh93では、このメソッドは解決策を明らかにしませんが、含まれているスクリプトへの絶対パスはとして利用できます${.sh.file}

bashまたはksh93またはzshが必要な場合は、このスニペットを使用できます。

if [ -n "$BASH_SOURCE" ]; then
  this_script=$BASH_SOURCE
elif [ -n "$ZSH_VERSION" ]; then
  setopt function_argzero
  this_script=$0
elif eval '[[ -n ${.sh.file} ]]' 2>/dev/null; then
  eval 'this_script=${.sh.file}'
else
  echo 1>&2 "Unsupported shell. Please use bash, ksh93 or zsh."
  exit 2
fi

シェルが開いているファイルを調べることにより、スクリプトの場所を推測することができます。実験的に、これはダッシュとpdkshでは機能するようですが、bashまたはksh93では機能しないようです。少なくとも短いスクリプトでは、スクリプトファイルを実行するまでにスクリプトファイルを閉じています。

  open_file=$(lsof -F n -p $$ | sed -n '$s/^n//p')
  if [ -n "$open_file" ]; then
    # best guess: $open_file is this script
  fi

リダイレクトを使用して実行されている複雑なスクリプト内でスクリプトが提供されている場合、スクリプトは記述子の番号が最も大きいファイルではない可能性があります。開いているファイルをループすることができます。とにかくこれが機能することは保証されていません。ソーススクリプトを特定する唯一の信頼できる方法は、bash、ksh93、またはzshを使用することです。


インターフェースを変更できる場合は、スクリプトを調達する代わりevalに、呼び出し側で渡されるシェルスニペットをスクリプトに出力させます。これは、環境変数を設定するスクリプトが通常行うことです。それはあなたのスクリプトが呼び出し側のシェルとシェル構成の気まぐれに関係なく書かれることを可能にします。

#!/bin/sh
FOO_DIR=$(dirname -- "$0")
cat <<EOF
FOO_DIR='$(printf %s "$FOO_DIR" | sed "s/'/'\\''/g")'
PATH="\$PATH:$FOO_DIR/bin";
export FOO_DIR PATH
EOF

呼び出し元で: eval "`/path/to/setenv`"


0

Gillesの回答に加えて、をif $0 = ash介してシェルスクリプト()を取得できる場合があります/proc/$$ interface。これがどれほど正確かはわかりませんが、/proc/$$/fd/11常にBusyBoxの灰でthis_scriptを指しているようです。

これを、このページに出くわした人々への潜在的な回答として提供します(私もそうでした)。

最後の答えは削除されました-したがって、この動作に対する私の主張は4つの異なるHWプラットフォーム(x86、ARM、XLP、およびpowerpc)でテストされています。すべてはfd / 11の同じ答えを与えます。からのreadlink /proc/$$/fd/11は私のスクリプトを生成します。

アンディ

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.