Bash

可靠地辨識包含執行腳本的目錄的路徑

  • May 13, 2015

SO中有很多答案可以解決這個問題:

對於原始碼腳本,Unix 和 Linux 中也有一些類似的問題。

不過,其中一些是特定於 Bash 的(它們依賴於$BASH_SOURCE)。如果目錄名稱包含換行符,其中一些不起作用,如果涉及符號連結,其中一些不起作用。

在每個主要 shell 中獲取包含執行/呼叫腳本的目錄的路徑的可靠方法是什麼?有一個檢查每種外殼類型的解決方案很好。

有一些啟發式方法可以幫助您,但沒有完全可靠的方法。

Otheus 展示瞭如何使用文件描述符。這是一個很好的啟發式方法,在大多數情況下都有效。但是,在某些極端情況下它會失敗,並且無法檢測到失敗。

範例:採用以下腳本。

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

製作兩份腳本副本,一份名為foo,一份名為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 gilles 1w REG 0,24 99 10130024 /tmp/202954/foo.out —

dash gilles 1w REG 0,24 99 10130022 /tmp/202954/bar.out

這裡唯一的區別是我記錄輸出的文件。

這種啟發式方法會失敗的另一種情況是,如果 shell 是在標準輸入上呼叫的,或者使用-c.

另一種方法是解析 shell 的命令行。在 Linux 上,您可以通過/proc. 參數是空分隔的,很難用可移植的 shell 工具解析(最近的 GNU 工具使它更容易),但可以做到。可移植地,您需要呼叫ps來訪問參數,並且沒有明確的輸出格式:ps -o args將參數與空格連接起來,並且可能會被截斷。

即使在 Linux 上,您在這裡遇到的問題是 shell 可能已使用您的腳本不知道的選項呼叫,並且其中一個選項可能帶有參數。例如,假設您有一個名為的腳本1和另一個名為2.

mksh -T 1 2

這將呼叫 mksh on/dev/tty1並執行腳本2

zsh -T 1 2

這使用選項呼叫 zsh 並使用參數cprecedences執行腳本。1``2

您需要了解各個外殼的知識才能將它們區分開來。使用此方法,您可以檢測邊緣情況:如果您看到非標準選項,請退出。

我將對此進行第一階段的嘗試。希望其他人會有所改善。

在執行腳本之前,shell 將打開文件的文件描述符。通常這是在 fd 255 分配的。無論如何,如果有一個 open fd,那麼lsof可以找到它。所以我們使用lsof -p $$並獲得最高文件描述符的文件名。lsof不適用於所有類型的 Unix。BSD 的 wiki 說有fstat. 它似乎在達爾文(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&lt;=&gt;$b} keys %p;
       print $p{$x[-1]}; 
 }{'`

perl 腳本很醜,我同意。為了清楚起見,我把它分開了。如果行以 開頭f,我們擷取文件描述符的編號,如果我們有有效的文件描述符和有效的文件名,我們將文件名擷取到散列中。以防萬一,如果這些條件都不滿足,我們清除$fd. 在處理完所有行之後,我們對雜湊的鍵(我們的文件描述符)進行數字排序,將結果儲存到數組x中並輸出p文件名雜湊的內容,由數組中的最後一個元素(最大值)索引x

唯一的問題是:是否會在所有系統上安裝 lsof 以及這種輸出格式的穩定性如何。

引用自:https://unix.stackexchange.com/questions/202954