it-swarm-vi.com

xác định đường dẫn đến tập lệnh Shell có nguồn gốc

Có cách nào để một nguồn gốc Shell script tìm ra đường dẫn đến chính nó không? Tôi chủ yếu quan tâm đến bash, mặc dù tôi có một số đồng nghiệp sử dụng tcsh.

Tôi đoán rằng tôi có thể không có nhiều may mắn ở đây, vì việc tìm nguồn cung ứng khiến các lệnh được thực thi trong Shell hiện tại, vì vậy $0 vẫn là lời gọi của Shell hiện tại, không phải là tập lệnh có nguồn gốc. Suy nghĩ tốt nhất của tôi hiện tại là làm source $script $script, để tham số vị trí đầu tiên chứa thông tin cần thiết. Bất cứ ai có một cách tốt hơn?

Để rõ ràng, tôi tìm nguồn cung ứng tập lệnh, không chạy nó:

source foo.bash
86
Cascabel

Trong tcsh, $_ ở đầu tập lệnh sẽ chứa vị trí nếu tệp được lấy nguồn và $0 chứa nó nếu nó được chạy.

#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
    echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
    echo "run $0"
endif

Trong Bash:

#!/bin/bash
[[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"

Tôi nghĩ rằng bạn có thể sử dụng $BASH_SOURCE Biến đổi. Nó trả về đường dẫn đã được thực thi:

[email protected] ~ $ /home/pbm/a.sh 
/home/pbm/a.sh
[email protected] ~ $ ./a.sh
./a.sh
[email protected] ~ $ source /home/pbm/a.sh 
/home/pbm/a.sh
[email protected] ~ $ source ./a.sh
./a.sh

Vì vậy, trong bước tiếp theo chúng ta nên kiểm tra xem đường dẫn có tương đối hay không. Nếu nó không tương đối thì mọi thứ đều ổn. Nếu đó là chúng ta có thể kiểm tra đường dẫn với pwd, nối với /$BASH_SOURCE.

32
pbm

Giải pháp này chỉ áp dụng cho bash và không tcsh. Lưu ý rằng câu trả lời thường được cung cấp ${BASH_SOURCE[0]} sẽ không hoạt động nếu bạn cố gắng tìm đường dẫn từ trong một hàm.

Tôi đã tìm thấy dòng này luôn hoạt động, bất kể tập tin đang được lấy nguồn hay chạy dưới dạng tập lệnh.

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

Nếu bạn muốn theo liên kết tượng trưng, ​​hãy sử dụng readlink trên đường dẫn bạn đi ở trên, đệ quy hoặc không đệ quy.

Đây là một kịch bản để dùng thử và so sánh nó với các giải pháp được đề xuất khác. Gọi nó là source test1/test2/test_script.sh hoặc là 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]}
}

Lý do hoạt động của một lớp lót được giải thích bằng việc sử dụng BASH_SOURCE biến môi trường và liên kết của nó FUNCNAME.

BASH_SOURCE

Một biến mảng có thành viên là tên tệp nguồn trong đó tên hàm Shell tương ứng trong biến mảng FUNCNAME được xác định. Hàm Shell $ {FUNCNAME [$ i]} được xác định trong tệp $ {BASH_SOURCE [$ i]} và được gọi từ $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

Một biến mảng chứa tên của tất cả các hàm Shell hiện có trong ngăn xếp lệnh thực thi. Phần tử có chỉ số 0 là tên của bất kỳ hàm Shell nào đang thực thi. Phần tử dưới cùng nhất (phần tử có chỉ số cao nhất) là "chính". Biến này chỉ tồn tại khi một hàm Shell đang thực thi. Việc gán cho FUNCNAME không có hiệu lực và trả về trạng thái lỗi. Nếu FUNCNAME không được đặt, nó sẽ mất các thuộc tính đặc biệt, ngay cả khi sau đó được đặt lại.

Biến này có thể được sử dụng với BASH_LINENO và BASH_SOURCE. Mỗi phần tử của FUNCNAME có các phần tử tương ứng trong BASH_LINENO và BASH_SOURCE để mô tả ngăn xếp cuộc gọi. Chẳng hạn, $ {FUNCNAME [$ i]} đã được gọi từ tệp $ {BASH_SOURCE [$ i + 1]} tại số dòng $ {BASH_LINENO [$ i]}. Trình dựng cuộc gọi hiển thị ngăn xếp cuộc gọi hiện tại bằng cách sử dụng thông tin này.

[Nguồn: hướng dẫn Bash]

21
gkb0986

Vì sự thấu đáo và vì lợi ích của người tìm kiếm, đây là những gì họ làm ... Đây là một wiki cộng đồng, vì vậy hãy thoải mái thêm các tương đương khác của Shell (rõ ràng, $ BASH_SOURCE sẽ khác).

kiểm tra

#! /bin/sh
called=$_
echo $called
echo $_
echo $0
echo $BASH_SOURCE

kiểm tra2.sh:

#! /bin/sh
source ./test.sh

Bash:

$./test2.sh
./test2.sh
./test2.sh
./test2.sh
./test.sh
$ sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
./test.sh

Dấu gạch ngang

$./test2.sh
./test2.sh
./test2.sh
./test2.sh

$/bin/sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh

$

Zsh

$ ./test2.sh
./test.sh
./test.sh
./test.sh

$ zsh test.sh

echo
test.sh

$
18
Shawn J. Goff

Điều này làm việc cho tôi trong bash, dash, ksh và zsh:

if test -n "$BASH" ; then script=$BASH_SOURCE
Elif test -n "$TMOUT"; then script=${.sh.file}
Elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
Elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
else script=$0
fi

echo $script

Đầu ra cho các shell này:

BASH source: ./myscript
ZSH source: ./myscript
KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
BASH: ./myscript
ZSH: ./myscript
KSH: /home/pbrannan/git/theme/src/theme/web/myscript
DASH: ./myscript

Tôi đã cố gắng làm cho nó hoạt động cho csh/tcsh, nhưng nó quá khó; Tôi đang gắn bó với POSIX.

16
Paul Brannan

Tôi đã có một chút bối rối bởi câu trả lời wiki cộng đồng (từ Shawn J. Goff), vì vậy tôi đã viết một kịch bản để sắp xếp mọi thứ. Trong khoảng $_, Tôi tìm thấy cái này: Cách sử dụng _ như một biến môi trường được truyền cho một lệnh . Đây là một biến môi trường để dễ dàng kiểm tra giá trị của nó không chính xác.

Dưới đây là kịch bản, sau đó là đầu ra. Họ cũng đang ở Gist này .

test-Shell-default-variables.sh

#!/bin/bash

# test-Shell-default-variables.sh

# Usage examples (you might want to `Sudo apt install zsh ksh`):
#
#  ./test-Shell-default-variables.sh dash bash
#  ./test-Shell-default-variables.sh dash bash zsh ksh
#  ./test-Shell-default-variables.sh dash bash zsh ksh | less -R

# `-R` in `less -R` to have less pass escape sequences directly to the terminal
# so we have colors.


# The "invoking with name `sh`" tests are commented because for every Shell I
# tested (dash, bash, zsh and ksh), the output was the same as that of dash.

# The `test_expression` function also work with expansion changes. You can try
# lines like `test_expression '{BASH_SOURCE:-$0}'`.

echolor() {
    echo -e "\e[1;[email protected]\e[0m"
}

tell_file() {
    echo File \`"$1"\` is:
    echo \`\`\`
    cat "$1"
    echo \`\`\`
    echo
}

Shell_ARRAY=("[email protected]")

test_command() {
    for Shell in "${Shell_ARRAY[@]}"
    do
        prepare "$Shell"
        cmd="$(eval echo $1)"
        # echo "cmd: $cmd"
        printf '%-4s: ' "$Shell"
        { env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
        teardown
    done
    echo
}

prepare () {
    Shell="$1"
    PATH="$PWD/$Shell/sh:$PATH"
}

teardown() {
    PATH="${PATH#*:}"
}


###
### prepare
###
for Shell in "${Shell_ARRAY[@]}"
do
    mkdir "$Shell"
    ln -sT "/bin/$Shell" "$Shell/sh"
done

echo > printer.sh
echo '. ./printer.sh' > sourcer.sh
rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"

tell_file sourcer.sh

###
### run
###
test_expression() {
    local expr="$1"

    # prepare
    echo "echo $expr" > printer.sh
    tell_file printer.sh

    # run
    cmd='$Shell ./printer.sh'
    echolor "\`$cmd\` (simple invocation) ($expr):"
    test_command "$cmd"

    # cmd='sh ./printer.sh'
    # echolor "\`$cmd\` (when executable name is \`sh\`) ($expr):"
    # test_command "$cmd"

    cmd='$Shell ./sourcer.sh'
    echolor "\`$cmd\` (via sourcing) ($expr):"
    test_command "$cmd"

    # cmd='sh ./sourcer.sh'
    # echolor "\`$cmd\` (via sourcing, when name is \`sh\`) ($expr):"
    # test_command "$cmd"

    cmd='$Shell ./linked.sh'
    echolor "\`$cmd\` (via symlink) ($expr):"
    test_command "$cmd"

    # cmd='sh ./linked.sh'
    # echolor "\`$cmd\` (via symlink, when name is \`sh\`) ($expr):"
    # test_command "$cmd"

    echolor "------------------------------------------"
    echo
}

test_expression '$BASH_SOURCE'
test_expression '$0'
test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
test_expression '$_'

###
### teardown
###
for Shell in "${Shell_ARRAY[@]}"
do
    rm "$Shell/sh"
    rm -d "$Shell"
done

rm sourcer.sh
rm linked.sh
rm printer.sh

Đầu ra của ./test-Shell-default-variables.sh {da,ba,z,k}sh

File `sourcer.sh` is:
```
. ./printer.sh
```

File `printer.sh` is:
```
echo $BASH_SOURCE
```

`$Shell ./printer.sh` (simple invocation) ($BASH_SOURCE):
dash: 
bash: ./printer.sh
zsh : 
ksh : 

`$Shell ./sourcer.sh` (via sourcing) ($BASH_SOURCE):
dash: 
bash: ./printer.sh
zsh : 
ksh : 

`$Shell ./linked.sh` (via symlink) ($BASH_SOURCE):
dash: 
bash: ./linked.sh
zsh : 
ksh : 

------------------------------------------

File `printer.sh` is:
```
echo $0
```

`$Shell ./printer.sh` (simple invocation) ($0):
dash: ./printer.sh
bash: ./printer.sh
zsh : ./printer.sh
ksh : ./printer.sh

`$Shell ./sourcer.sh` (via sourcing) ($0):
dash: ./sourcer.sh
bash: ./sourcer.sh
zsh : ./printer.sh
ksh : ./sourcer.sh

`$Shell ./linked.sh` (via symlink) ($0):
dash: ./linked.sh
bash: ./linked.sh
zsh : ./linked.sh
ksh : ./linked.sh

------------------------------------------

File `printer.sh` is:
```
echo $(/bin/true x y; true a b c; echo $_)
```

`$Shell ./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
dash: 
bash: c
zsh : c
ksh : 

`$Shell ./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
dash: 
bash: c
zsh : c
ksh : 

`$Shell ./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
dash: 
bash: c
zsh : c
ksh : 

------------------------------------------

File `printer.sh` is:
```
echo $_
```

`$Shell ./printer.sh` (simple invocation) ($_):
dash: 
bash: bash
zsh : 
ksh : 

`$Shell ./sourcer.sh` (via sourcing) ($_):
dash: 
bash: bash
zsh : ./printer.sh
ksh : 

`$Shell ./linked.sh` (via symlink) ($_):
dash: 
bash: bash
zsh : 
ksh : 

------------------------------------------

Chúng ta đã học được gì?

$BASH_SOURCE

  • $BASH_SOURCE hoạt động trong bash và chỉ trong bash.
  • Sự khác biệt duy nhất với $0 là khi tệp hiện tại được lấy nguồn bởi một tệp khác. Trong trường hợp đó, $BASH_PROFILE chứa tên của tệp có nguồn gốc, thay vì tên của tệp chua.

$0

  • Trong zsh, $0 có cùng giá trị với $BASH_SOURCE trong bash.

$_

  • $_ không bị ảnh hưởng bởi dấu gạch ngang và ksh.
  • Trong bash và zsh, $_ phân rã đến đối số cuối cùng của cuộc gọi cuối cùng.
  • bash khởi tạo $_ cú đánh".
  • lá zsh $_ không bị ảnh hưởng. (khi tìm nguồn cung ứng, đó chỉ là kết quả của quy tắc "đối số cuối cùng").

Liên kết

  • Khi một tập lệnh được gọi thông qua một liên kết tượng trưng, ​​không có biến nào chứa bất kỳ tham chiếu nào đến đích của liên kết, chỉ có tên của nó.

ksh

  • Về những thử nghiệm đó, ksh hành xử như dấu gạch ngang.

sh

  • Khi bash hoặc zsh được gọi thông qua một liên kết tượng trưng có tên sh, liên quan đến các thử nghiệm đó, nó hoạt động giống như dấu gạch ngang.
2
Mathieu CAROFF

câu trả lời này mô tả cách lsof và một chút ma thuật grep là điều duy nhất dường như có cơ hội làm việc cho các tệp có nguồn gốc lồng nhau trong tcsh:

/usr/sbin/lsof +p $$ | grep -oE /.\*source_me.tcsh
0
Patrick Maupin

tl; dr script=$(readlink -e -- "${BASH_SOURCE}") (for bash rõ ràng)


$BASH_SOURCE trường hợp thử nghiệm

tập tin đã cho /tmp/source1.sh

echo '$BASH_SOURCE '"(${BASH_SOURCE})"
echo 'readlink -e $BASH_SOURCE'\
     "($(readlink -e -- "${BASH_SOURCE}"))"

source tệp theo cách khác nhau

source từ /tmp

$> cd /tmp

$> source source1.sh
$BASH_SOURCE (source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$> source ./source1.sh
$BASH_SOURCE (./source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$> source /tmp/source1.sh
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

source từ /

cd /
$> source /tmp/source1.sh
$0 (bash)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

source từ các đường dẫn tương đối khác nhau /tmp/a/var

$> cd /tmp/a

$> source ../source1.sh
$BASH_SOURCE (../source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$> cd /var

$> source ../tmp/source1.sh
$BASH_SOURCE (../tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

về $0

trong mọi trường hợp, nếu tập lệnh có lệnh được thêm vào

echo '$0 '"(${0})"

sau đó source tập lệnh luôn được in

$0 (bash)

tuy nhiên, nếu tập lệnh là chạy , ví dụ:.

$> bash /tmp/source1.sh

sau đó $0 sẽ là giá trị chuỗi /tmp/source1.sh.

$0 (/tmp/source1.sh)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
0
JamesThomasMoon1979

Đối với bash Shell, tôi đã tìm thấy câu trả lời của @ Dennis Williamson hữu ích nhất, nhưng nó không hoạt động trong trường hợp Sudo. Cái này không

if ( [[ $_ != $0 ]] && [[ $_ != $Shell ]] ); then
    echo "I'm being sourced!"
    exit 1
fi
0
Matt

Để làm cho tập lệnh của bạn tương thích cả bash- và zsh thay vì sử dụng các câu lệnh if, bạn chỉ cần viết ${BASH_SOURCE[0]:-${(%):-%x}}. Giá trị kết quả sẽ được lấy từ BASH_SOURCE[0] Khi được xác định và ${(%):-%x}} khi BASH_SOURCE [0] không được xác định.

0
dols3m

Phần khó nhất là tìm tệp hiện có nguồn gốc là cho dash Shell được sử dụng làm thay thế sh trong Ubuntu. Đoạn mã sau có thể được sử dụng trong tập lệnh được lấy nguồn để xác định đường dẫn tuyệt đối của nó. Đã thử nghiệm trong bash, zsh và dash gọi cả dưới dạng dash và sh.

NB: phụ thuộc vào hiện đại realpath (1) tiện ích từ GNU coreutils

NB: Các tùy chọn lsof (1) cũng nên được xác minh vì các lời khuyên tương tự cả từ trang này và các trang khác không hoạt động với tôi trên Ubuntu 18 và 19 do đó tôi phải phát minh lại.

getShellName() {
    [ -n "$BASH" ] && echo ${BASH##/*/} && return
    [ -n "$ZSH_NAME" ] && echo $ZSH_NAME && return
    echo ${0##/*/}
}

getCurrentScript() {
    local result
    case "$(getShellName)" in
        bash )  result=${BASH_SOURCE[0]}
                ;;
        zsh )   emulate -L zsh
                result=${funcfiletrace[1]%:*}
                ;;
        dash | sh )
                result=$(
                    lsof -p $$ -Fn  \
                    | tail --lines=1  \
                    | xargs --max-args=2  \
                    | cut --delimiter=' ' --fields=2
                )
                result=${result#n}
                ;;
        * )     result=$0
                ;;
    esac
    echo $(realpath $result)
}
0
maoizm