it-swarm-id.com

menentukan jalur ke skrip Shell bersumber

Apakah ada cara untuk skrip Shell bersumber untuk mencari tahu jalannya sendiri? Saya terutama khawatir dengan bash, meskipun saya memiliki beberapa rekan kerja yang menggunakan tcsh.

Saya kira saya mungkin tidak memiliki banyak keberuntungan di sini, karena sumber menyebabkan perintah dieksekusi di Shell saat ini, jadi $0 masih merupakan permintaan Shell saat ini, bukan skrip bersumber. Pemikiran terbaik saya saat ini adalah melakukan source $script $script, sehingga parameter posisi pertama berisi informasi yang diperlukan. Adakah yang punya cara yang lebih baik?

Untuk menjadi jelas, saya sumber skrip, tidak menjalankannya:

source foo.bash
86
Cascabel

Dalam tcsh, $_ di awal skrip akan berisi lokasi jika file tersebut bersumber dan $0 berisi jika dijalankan.

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

Di Bash:

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

Saya pikir Anda bisa menggunakan $BASH_SOURCE variabel. Ini mengembalikan jalur yang dieksekusi:

[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

Jadi pada langkah selanjutnya kita harus memeriksa apakah path relatif atau tidak. Jika tidak relatif semuanya baik-baik saja. Jika ya, kita bisa mengecek path dengan pwd, concatenate dengan / dan $BASH_SOURCE.

32
pbm

Solusi ini hanya berlaku untuk bash dan bukan tcsh. Perhatikan bahwa jawaban yang umum diberikan ${BASH_SOURCE[0]} tidak akan berfungsi jika Anda mencoba menemukan jalur dari dalam suatu fungsi.

Saya menemukan baris ini selalu berfungsi, terlepas dari apakah file tersebut bersumber atau dijalankan sebagai skrip.

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

Jika Anda ingin mengikuti symlinks gunakan readlink pada jalur yang Anda dapatkan di atas, secara rekursif atau non-rekursif.

Berikut ini skrip untuk mencobanya dan membandingkannya dengan solusi yang diusulkan lainnya. Gunakan sebagai source test1/test2/test_script.sh atau 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]}
}

Alasan mengapa one-liner berfungsi dijelaskan oleh penggunaan BASH_SOURCE variabel lingkungan dan asosiasinya FUNCNAME.

BASH_SOURCE

Variabel array yang anggotanya adalah nama file sumber tempat nama fungsi Shell yang sesuai dalam variabel array FUNCNAME didefinisikan. Fungsi Shell $ {FUNCNAME [$ i]} didefinisikan dalam file $ {BASH_SOURCE [$ i]} dan dipanggil dari $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

Variabel array yang berisi nama-nama semua fungsi Shell saat ini dalam tumpukan panggilan eksekusi. Elemen dengan indeks 0 adalah nama dari setiap fungsi Shell yang sedang dijalankan. Elemen terbawah (yang memiliki indeks tertinggi) adalah "utama". Variabel ini hanya ada ketika fungsi Shell mengeksekusi. Tugas untuk FUNCNAME tidak berpengaruh dan mengembalikan status kesalahan. Jika FUNCNAME tidak disetel, FUNCNAME akan kehilangan properti khususnya, meskipun kemudian disetel ulang.

Variabel ini dapat digunakan dengan BASH_LINENO dan BASH_SOURCE. Setiap elemen FUNCNAME memiliki elemen yang sesuai di BASH_LINENO dan BASH_SOURCE untuk menjelaskan tumpukan panggilan. Misalnya, $ {FUNCNAME [$ i]} dipanggil dari file $ {BASH_SOURCE [$ i + 1]} di nomor baris $ {BASH_LINENO [$ i]}. Penelepon bawaan menampilkan tumpukan panggilan saat ini menggunakan informasi ini.

[Sumber: Bash manual]

21
gkb0986

Demi ketelitian dan demi pencarian, inilah yang dilakukan ... Ini adalah wiki komunitas, jadi silakan tambahkan setara Shell lainnya (jelas, $ BASH_SOURCE akan berbeda).

test.sh:

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

test2.sh:

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

Pesta:

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

Berlari

$./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

Ini bekerja untuk saya di bash, dash, ksh, dan 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

Output untuk cangkang ini:

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

Saya mencoba membuatnya bekerja untuk csh/tcsh, tetapi terlalu sulit; Saya berpegang teguh pada POSIX.

16
Paul Brannan

Saya agak bingung dengan jawaban wiki komunitas (dari Shawn J. Goff), jadi saya menulis naskah untuk menyelesaikan masalah. Tentang $_, Saya menemukan ini: Penggunaan _ sebagai variabel lingkungan yang diteruskan ke perintah . Ini adalah variabel lingkungan sehingga mudah untuk menguji nilainya secara salah.

Di bawah ini adalah skrip, lalu hasilnya. Mereka juga ada di Intisari ini .

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

Output dari ./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 : 

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

Apa yang kami pelajari?

$BASH_SOURCE

  • $BASH_SOURCE bekerja dalam bash dan hanya dalam bash.
  • Satu-satunya perbedaan dengan $0 adalah ketika file saat ini bersumber dari file lain. Dalam hal itu, $BASH_PROFILE berisi nama file sumber, bukan nama file sumber.

$0

  • Dalam zsh, $0 memiliki nilai yang sama dengan $BASH_SOURCE dalam bash.

$_

  • $_ dibiarkan tidak tersentuh oleh tanda hubung dan ksh.
  • Dalam bash dan zsh, $_ meluruh ke argumen terakhir dari panggilan terakhir.
  • bash menginisialisasi $_ hingga "bash".
  • zsh pergi $_ tidak tersentuh. (Saat sumber, itu hanya hasil dari aturan "argumen terakhir").

Symlinks

  • Ketika sebuah skrip dipanggil melalui symlink, tidak ada variabel yang berisi referensi ke tujuan tautan, hanya namanya.

ksh

  • Mengenai tes-tes itu, ksh berperilaku seperti tanda hubung.

sH

  • Ketika bash atau zsh dipanggil melalui symlink bernama sh, mengenai tes-tes tersebut, ia berperilaku seperti tanda hubung.
2
Mathieu CAROFF

jawaban ini menjelaskan bagaimana lsof dan sedikit grep magic adalah satu-satunya hal yang tampaknya memiliki peluang bekerja untuk file bersarang bersarang di bawah tcsh:

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

tl; dr script=$(readlink -e -- "${BASH_SOURCE}") (untuk bash jelas)


$BASH_SOURCE kasus uji

file yang diberikan /tmp/source1.sh

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

source file dengan cara yang berbeda

source dari /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 dari /

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

source dari jalur relatif yang berbeda /tmp/a dan /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)

tentang $0

dalam semua kasus, jika skrip memiliki perintah yang ditambahkan

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

lalu source skrip selalu dicetak

$0 (bash)

namun, jika skrip dijalankan dijalankan , mis.

$> bash /tmp/source1.sh

kemudian $0 akan menjadi nilai string /tmp/source1.sh.

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

Untuk Shell bash, saya menemukan @ jawaban Dennis Williamson paling membantu, tetapi tidak berhasil dalam kasus Sudo. Ini tidak:

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

Untuk membuat skrip Anda kompatibel dengan bash dan zsh alih-alih menggunakan pernyataan if, Anda cukup menulis ${BASH_SOURCE[0]:-${(%):-%x}}. Nilai yang dihasilkan akan diambil dari BASH_SOURCE[0] Ketika sudah ditentukan, dan ${(%):-%x}} ketika BASH_SOURCE [0] tidak ditentukan.

0
dols3m

Bagian tersulit adalah menemukan file yang saat ini bersumber untuk dash Shell yang digunakan sebagai pengganti sh di Ubuntu. Cuplikan kode berikut dapat digunakan dalam skrip yang bersumber untuk menentukan jalur absolutnya. Diuji dalam bash, zsh dan dash dipanggil sebagai dash dan sh.

NB: tergantung pada modern realpath (1) utilitas dari GNU paket coreutils

NB: lsof (1) opsi harus diverifikasi juga karena saran serupa baik dari ini dan halaman lain tidak bekerja untuk saya di Ubuntu 18 dan 19 sehingga saya harus menemukan kembali ini.

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