it-swarm-eu.dev

určující cestu ke zdrojovému skriptu Shell

Existuje způsob, jak skript sourced Shell najít cestu k sobě? Hlavně mě zajímá bash, i když mám několik spolupracovníků, kteří používají tcsh.

Hádám, že tady nebudu mít spoustu štěstí, protože sourcing způsobuje provádění příkazů v aktuální Shell, takže $0 je stále aktuální vyvolání Shell, nikoli zdrojový skript. Moje nejlepší myšlenka v současné době je udělat source $script $script, takže první polohový parametr obsahuje potřebné informace. Někdo má lepší způsob?

Jasně řečeno, jsem sourcing skript, nespouštím jej:

source foo.bash
86
Cascabel

V tcsh, $_ na začátku skriptu bude obsahovat umístění, pokud byl soubor získán, a $0 obsahuje, pokud bylo spuštěno.

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

V Bash:

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

Myslím, že byste mohli použít $BASH_SOURCE proměnná. Vrací cestu, která byla provedena:

[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

Takže v dalším kroku bychom měli zkontrolovat, zda je cesta relativní nebo ne. Pokud to není relativní, vše je v pořádku. Pokud je to, můžeme zkontrolovat cestu pomocí pwd, zřetězit s / a $BASH_SOURCE.

32
pbm

Toto řešení platí pouze pro bash a ne tcsh. Všimněte si, že běžně dodávaná odpověď ${BASH_SOURCE[0]} nebude fungovat, pokud se pokusíte najít cestu uvnitř funkce.

Zjistil jsem, že tento řádek vždy funguje, bez ohledu na to, zda je soubor získán nebo spuštěn jako skript.

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

Pokud chcete sledovat odkazy, použijte readlink na cestě, kterou dostanete výše, rekurzivně nebo nerekurzivně.

Zde je skript, který si to vyzkouší a porovná s jinými navrhovanými řešeními. Vyvolejte to jako source test1/test2/test_script.sh nebo 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]}
}

Důvod, proč funguje jednoplášťová doprava, je vysvětlen pomocí BASH_SOURCE proměnná prostředí a její přidružený FUNCNAME.

BASH_SOURCE

Proměnná matice, jejíž členové jsou zdrojovými názvy souborů, kde jsou definovány odpovídající názvy funkcí Shell v proměnné matice FUNCNAME. Funkce Shell $ {FUNCNAME [$ i]} je definována v souboru $ {BASH_SOURCE [$ i]} a vyvolává se z $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

Proměnná pole obsahující názvy všech funkcí prostředí, které jsou aktuálně v zásobníku volání provádění. Prvek s indexem 0 je název jakékoli aktuálně prováděné funkce Shell. Nejspodnější prvek (ten s nejvyšším indexem) je „hlavní“. Tato proměnná existuje, pouze pokud se provádí funkce Shell. Přiřazení k FUNCNAME nemá žádný účinek a vrací chybový stav. Pokud FUNCNAME není nastaveno, ztratí své speciální vlastnosti, i když je následně resetováno.

Tuto proměnnou lze použít s BASH_LINENO a BASH_SOURCE. Každý prvek FUNCNAME má odpovídající prvky v BASH_LINENO a BASH_SOURCE k popisu zásobníku volání. Například $ {FUNCNAME [$ i]} byl vyvolán ze souboru $ {BASH_SOURCE [$ i + 1]} na číslo řádku $ {BASH_LINENO [$ i]}. Vestavěný volající zobrazuje aktuální zásobník hovorů pomocí těchto informací.

[Zdroj: Bash manual]

21
gkb0986

Pro důkladnost a pro pátrače je zde to, co tito dělají ... Je to komunitní wiki, takže neváhejte přidat další ekvivalenty Shell (samozřejmě bude $ BASH_SOURCE odlišný).

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

Pomlčka

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

Fungovalo to pro mě v bash, dash, ksh a 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

Výstup pro tyto náboje:

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

Snažil jsem se, aby to fungovalo pro csh/tcsh, ale je to příliš těžké; Držím se POSIXu.

16
Paul Brannan

Trochu jsem byl zmaten odpovědí na komunitní wiki (od Shawn J. Goffa), takže jsem napsal skript, abych věci vyřešil. O $_, Našel jsem toto: Použití _ jako proměnná prostředí předaná příkaz . Je to proměnná prostředí, takže je snadné otestovat jeho hodnotu nesprávně.

Níže je skript, pak je to výstup. Jsou také v tento 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

Výstup ./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 : 

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

Co jsme se naučili?

$BASH_SOURCE

  • $BASH_SOURCE pracuje v bash a pouze v bash.
  • Jediný rozdíl s $0 je, když byl aktuální soubor získán jiným souborem. V tom případě, $BASH_PROFILE obsahuje název zdrojového souboru, nikoli název souboru souring.

$0

  • V zsh, $0 má stejnou hodnotu jako $BASH_SOURCE v bash.

$_

  • $_ zůstane nedotčeno pomlčkou a ksh.
  • In bash and zsh, $_ se rozpadne na poslední argument posledního hovoru.
  • bash inicializuje $_ na „bash“.
  • zsh listy $_ nedotčeno. (při sourcingu je to pouze výsledek pravidla „posledního argumentu“).

Symlinks

  • Když je skript vyvolán prostřednictvím odkazu, žádná proměnná neobsahuje žádný odkaz na cíl odkazu, pouze jeho název.

ksh

  • Pokud jde o tyto testy, ksh se chová jako pomlčka.

sh

  • Když je bash nebo zsh volán přes symbolický odkaz s názvem sh, pokud jde o tyto testy, chová se jako pomlčka.
2
Mathieu CAROFF

tato odpověď popisuje, jak lsof a trochu grep magie je jediná věc, která, jak se zdá, má šanci pracovat pro vnořené soubory v tcsh:

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

tl; dr script=$(readlink -e -- "${BASH_SOURCE}") (pro bash samozřejmě)


$BASH_SOURCE testovací případy

daný soubor /tmp/source1.sh

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

source soubor různými způsoby

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

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

source z různých relativních cest /tmp/a 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)

týkající se $0

ve všech případech, pokud měl skript přidaný příkaz

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

pak source skript vždy vytištěn

$0 (bash)

nicméně, pokud byl skript spuštěn , např.

$> bash /tmp/source1.sh

pak $0 by byla hodnota řetězce /tmp/source1.sh.

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

Pro bash Shell jsem našel @ Dennis Williamsonova odpověď nejužitečnější, ale nefungovalo to v případě Sudo. To dělá:

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

Chcete-li, aby byl váš skript kompatibilní s bash i zsh místo použití příkazů if, můžete jednoduše napsat ${BASH_SOURCE[0]:-${(%):-%x}}. Výsledná hodnota bude převzata od BASH_SOURCE[0], Když je definována, a ${(%):-%x}}, když není definována BASH_SOURCE [0].

0
dols3m

Nejkomplikovanější částí je nalezení aktuálně získaného souboru pro pomlčku Shell používanou jako náhrada sh v Ubuntu. Následující fragment kódu lze použít ve zdroji skriptu k určení jeho absolutní cesty. Testováno v bash, zsh a dash vyvoláno jak dash, tak sh.

Pozn .: Závisí na moderním realpath (1) utilitě z GNU coreutils balíček)

Pozn .: Volby lsof (1) by měly být také ověřeny, protože podobná doporučení jak z této, tak z jiných stránek pro mě nefungovala na Ubuntu 18 a 19, a proto jsem to musel znovu objevit.

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