実行中のスクリプトを含むディレクトリへのパスを確実に識別します。

実行中のスクリプトを含むディレクトリへのパスを確実に識別します。

SOには、この問題を解決する多くの答えがあります。

少し残ったエマルジョンUnixとLinuxで源泉スクリプト。

ただし、その一部はBashにのみ適用されます(Bashに依存しています$BASH_SOURCE)。それらの一部ディレクトリ名に改行文字が含まれている場合は機能しませんが、一部はシンボリックリンクが含まれていると正しく機能しません。

何ですか信頼できるすべてのデフォルトシェルで実行/呼び出しスクリプトを含むディレクトリへのパスを取得するには?各シェルタイプを確認するソリューションがあれば良いでしょう。

答え1

私はこれを初めて試してみます。他の人も改善されることを願っています。

スクリプトを実行する前に、シェルはファイルのファイル記述子を開きます。通常、fd 255に割り当てられます。とにかくオープンがあれば見つけることができますfdlsofだから私たちはlsof -p $$最高のファイル記述子のファイル名を使用して取得します。lsofすべてのUNIXバージョンに適用されるわけではありません。 BSD Wikiには同等のものがあることがわかりますfstat。 Darwin(Mac OS)にあるようです。 `-Fで

スクリプト例:

#!/bin/sh

this_script_path=`lsof -p $$  | awk '/\/'${0##*/}'$/' | cut -c 55-`

明らかに、クリッピングはlsofの特定の形式に大きく依存します。バージョン 2 では、この問題を軽減できます。注:私のバージョンでは、lsof印刷できない文字を変換してパス名のタブ文字も\t

バージョン2。醜いPerlコードについて事前に申し訳ありません。今回は-Fオプションを使って出力を制御します。私たちは-F fn次のような結果を得るでしょう:

p3834
fcwd
n/home/joe/test
frtd
n/
ftxt
n/bin/bash
fmem
n/lib64/ld-2.12.so
fmem
n/lib64/libdl-2.12.so
fmem
n/lib64/libc-2.12.so
fmem
n/lib64/libtinfo.so.5.7
fmem
n/usr/lib/locale/locale-archive
fmem
n/usr/lib64/gconv/gconv-modules.cache
f0
n/dev/pts/1
f1
n/dev/pts/1
f2
n/dev/pts/1
f255
n/home/joe/test/t.sh

私たちは、最高のファイル記述子(255と信じられないと仮定します)がスクリプト名になるように、この混乱した部分を変換する必要があります。 (これもdash当てはまるようです。)

this_script_path=`lsof -p $$ -F fn | 
  perl -lane '
        $fd=$1,next if /^f(\d+)/; 
        $p{$fd}=$1 if $fd and /^n(.*)/;
        $fd="";
  }END { 
        @x=sort {$a<=>$b} keys %p;
        print $p{$x[-1]}; 
  }{'`

Perlスクリプトは見苦しいことに同意します。これは一行で、明確にするために分類しました。行が始まると、ファイル記述子の番号がキャプチャされます。f有効なファイル記述子と有効なファイル名がある場合は、ファイル名をハッシュとしてキャプチャします。これらの条件のいずれも満たしていない場合は、キャンセルしてください$fd。すべての行が処理されたら、ハッシュ(ファイル記述子)のキーを数字でソートし、結果を配列に保存し、配列の最後の要素(最も大きい値)でインデックス付けされたxファイル名hashの内容を出力します。px

唯一の質問は、lsofがすべてのシステムにインストールされているかどうか、およびこの出力形式がどれほど安定しているかです。

答え2

役に立ついくつかの経験的な方法がありますが、完全に信頼できる方法はありません。

Otheusはファイル記述子の使い方を示しています。。これはほとんどの場合に機能する良い経験的な方法です。ただし、失敗して失敗を検出できない極端なケースがあります。

例:次のスクリプトを使用してください。

#!/bin/sh
set
lsof -p$$ | sed 's/[0-9][0-9]*//'

このスクリプトのコピーを2つ作成します。 1つは名前が付けられ、fooもう1つはbar。それでは少し強調してみましょう。

$ env -i PATH=/bin:/usr/bin perl -MPOSIX -e 'dup2(4, 11) or die $!; exec "dash", "foo", "bar"' 3<foo 4<bar </dev/null >foo.out
$ env -i PATH=/bin:/usr/bin perl -MPOSIX -e 'dup2(3, 10) or die $!; exec "dash", "bar", "foo"' 3<foo 4<bar </dev/null >bar.out
$ diff foo.out bar.out

17c17 < dash gils 1w reg 0,24 99 10130024 /tmp/202954/foo.out ---

ダッシュザイルズ 1w REG 0,24 99 10130022 /tmp/202954/bar.out

ここで唯一の違いは、出力を記録するファイルです。

この経験的な方法が失敗する別のケースでは、シェルが標準入力から呼び出されるか-c

別のアプローチは、シェルのコマンドラインを解析することです。 Linuxでは/proc引数がnullで区切られているため、ポータブルシェルツールで解析するのは困難ですが(最近のGNUツールを使用すると簡単になります)。移植可能なパラメータにアクセスするには呼び出す必要があり、ps明示的な出力形式はありません。ps -o argsパラメータはスペースで連結され、切り捨てることができます。

Linuxでもここで発生する問題は、スクリプトが知らないオプションでシェルを呼び出すことができ、そのオプションの1つが引数を取ることができることです。たとえば、名前の付いたスクリプト1と名前の異なるスクリプトがあるとします2

mksh -T 1 2

その後、mkshが呼び出され、/dev/tty1スクリプトが実行されます2

zsh -T 1 2

オプションを使用してzshを呼び出し、cprecedences引数を使用してスクリプトを実行します。12

各シェルを区別するには、各シェルを知る必要があります。このアプローチを使用すると、極端なケースを検出できます。標準以外のオプションが表示された場合は、終了してください。

関連情報