it-swarm-tr.com

kaynaklı Shell komut dosyasına giden yolu belirleme

Bir kaynaklı Shell betiğinin kendisine giden yolu bulmasının bir yolu var mı? Tcsh kullanan bazı arkadaşlarım olsa da, esas olarak bash ile ilgileniyorum.

Ben kaynak burada mevcut Kabuk yürütülmesine neden olur, çünkü burada bir ton şans olmayabilir tahmin ediyorum, bu yüzden $0 hala geçerli Kabuk'un çağrılmasıdır, kaynaklı komut dosyası değildir. Şu anda en iyi düşüncem source $script $script, böylece ilk konum parametresi gerekli bilgileri içerir. Daha iyi bir yolu var mı?

Açıkça söylemek gerekirse, ben kaynak betik, onu çalıştırmayan:

source foo.bash
86
Cascabel

tcsh, $_ betiğin başında dosya kaynaklanıyorsa konumu içerir ve $0 çalıştırıldıysa içerir.

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

Bash'ta:

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

Sanırım $BASH_SOURCE değişkeni. Yürütülen yolu döndürür:

[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

Bu yüzden bir sonraki adımda yolun göreli olup olmadığını kontrol etmeliyiz. Göreli değilse, her şey yolunda. Eğer öyleyse, yolu pwd ile kontrol edebilirsek, / ve $BASH_SOURCE.

32
pbm

Bu çözüm yalnızca bash için geçerlidir, tcsh için geçerli değildir. Sıkça verilen cevap ${BASH_SOURCE[0]} bir işlev içindeki yolu bulmaya çalışırsanız çalışmaz.

Ben dosya kaynak olup olmadığını veya bir komut dosyası olarak çalıştırmak olsun, her zaman çalışmak için bu satırı buldum.

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

Symlinks'i takip etmek istiyorsanız, yukarıda gördüğünüz yolda yinelenen veya yinelemesiz olarak readlink kullanın.

İşte denemek ve önerilen diğer çözümlerle karşılaştırmak için bir komut dosyası. source test1/test2/test_script.sh veya 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]}
}

Tek astarın çalışmasının nedeni BASH_SOURCE ortam değişkeni ve ortağı FUNCNAME.

BASH_SOURCE

Üyeleri FUNCNAME dizi değişkenindeki karşılık gelen Shell işlev adlarının tanımlandığı kaynak dosya adları olan bir dizi değişkeni. $ {FUNCNAME [$ i]} kabuk işlevi $ {BASH_SOURCE [$ i]} dosyasında tanımlanmış ve $ {BASH_SOURCE [$ i + 1]} dosyasından çağrılmıştır.

FUNCNAME

Yürütme çağrısı yığınında bulunan tüm Shell işlevlerinin adlarını içeren bir dizi değişkeni. 0 dizinine sahip öğe, şu anda yürütülen Shell işlevlerinin adıdır. En alttaki eleman (en yüksek endekse sahip olan eleman) "ana" dır. Bu değişken yalnızca bir Kabuk işlevi yürütüldüğünde mevcuttur. FUNCNAME öğesine yapılan atamaların hiçbir etkisi yoktur ve hata durumu döndürür. FUNCNAME ayarlanmamışsa, daha sonra sıfırlansa bile özel özelliklerini kaybeder.

Bu değişken BASH_LINENO ve BASH_SOURCE ile kullanılabilir. FUNCNAME öğesinin her öğesinde, çağrı yığınını açıklamak için BASH_LINENO ve BASH_SOURCE öğelerinde karşılık gelen öğeler bulunur. Örneğin $ {FUNCNAME [$ i]}, $ {BASH_SOURCE [$ i + 1]} dosyasından $ {BASH_LINENO [$ i]} satır numarasından çağrıldı. Arayan yerleşik, bu bilgiyi kullanarak geçerli çağrı yığınını görüntüler.

\. [Kaynak: Bash kılavuzu]

21
gkb0986

Kapsamlılık ve araştırmacılar uğruna, işte bunlar nelerdir ... Bir topluluk wiki'sidir, bu yüzden diğer Shell'in eşdeğerlerini eklemekten çekinmeyin (açıkçası, $ BASH_SOURCE farklı olacaktır).

test.sh:

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

test2.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

Tire

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

Bu benim için bash, dash, ksh ve zsh'de çalıştı:

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

Bu kabuklar için çıktı:

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

Ben csh/tcsh için çalışmayı denedim, ama çok zor; POSIX'e bağlıyorum.

16
Paul Brannan

Topluluk wiki cevabı (Shawn J. Goff'dan) biraz kafam karıştı, bu yüzden işleri çözmek için bir senaryo yazdım. $_ Hakkında şunu buldum: _ Komutunun bir komuta iletilen ortam değişkeni olarak kullanımı . Bu bir ortam değişkenidir, bu nedenle değerini yanlış test etmek kolaydır.

Aşağıda senaryo, sonra çıktı. Onlar da bu Gist .

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

Çıktı ./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 : 

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

Ne öğrendik?

$BASH_SOURCE

  • $BASH_SOURCE Bash ve yalnızca bash'da çalışıyor.
  • $0 İle tek fark, geçerli dosyanın başka bir dosya tarafından kaynaklanmasıdır. Bu durumda, $BASH_PROFILE, Kaynak dosyası yerine kaynak dosyasının adını içerir.

$0

  • Zsh'de, $0, Bash içindeki $BASH_SOURCE İle aynı değere sahiptir.

$_

  • $_, Tire ve ksh tarafından dokunulmadan bırakıldı.
  • Bash ve zsh'de $_ Son çağrının son argümanına bozulur.
  • bash $_ değerini "bash" olarak başlatır.
  • zsh $_ 'dan dokunulmaz. (kaynak yaparken, bu sadece "son argüman" kuralının sonucudur).

Symlinks

  • Bir komut dosyası bir sembolik bağla çağrıldığında, hiçbir değişken bağlantının hedefine herhangi bir başvuru içermez, yalnızca adıdır.

ksh

  • Bu testlerle ilgili olarak, ksh çizgi gibi davranır.

sh

  • Bash veya zsh, sh adlı bir sembolik bağlantıyla çağrıldığında, bu testlerle ilgili olarak tire işareti gibi davranır.
2
Mathieu CAROFF

bu cevap , tcsh altında iç içe kaynaklı dosyalar için çalışma şansı gibi görünen tek şey lsof ve biraz grep büyüsünün nasıl olduğunu açıklar:

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

tl; drscript=$(readlink -e -- "${BASH_SOURCE}") ( bash açıkçası)


$BASH_SOURCE test senaryoları

verilen dosya /tmp/source1.sh

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

source dosya farklı şekillerde

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

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

source farklı göreli yollardan /tmp/a ve /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)

ilgili $0

her durumda komut dosyasında komut eklenmişse

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

sonra source komut dosyası her zaman yazdırılır

$0 (bash)

ancak, komut dosyası çalıştır ise, ör.

$> bash /tmp/source1.sh

sonra $0, dize değeri /tmp/source1.sh.

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

Shell Shell için @ Dennis Williamson'ın cevabı en yararlı buldum, ancak Sudo durumunda işe yaramadı. Bu yapar:

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

Komut dosyanızı if ifadelerini kullanmak yerine bash ve zsh uyumlu yapmak için ${BASH_SOURCE[0]:-${(%):-%x}} yazmanız yeterlidir. Ortaya çıkan değer tanımlandığında BASH_SOURCE[0] 'Dan ve BASH_SOURCE [0] tanımlanmadığında ${(%):-%x}}' den alınacaktır.

0
dols3m

En zor bölüm şu anda kaynaklı dosya Ubuntu sh değiştirme olarak kullanılan çizgi Shell içindir. Aşağıdaki kod snippet'i, kaynak kodda mutlak yolunu belirlemek için kullanılabilir. Bas, zsh ve tire ile test edilmiş, hem tire hem de sh olarak çağrılmıştır.

Not: GNU coreutils paketinden modern realpath (1) yardımcı programına bağlıdır

Not: lsof (1) seçenekleri de doğrulanmalıdır çünkü Ubuntu 18 ve 19'da hem bu hem de diğer sayfalardaki benzer tavsiyeler benim için çalışmadı, bu yüzden bunu yeniden keşfetmeliydim.

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