Bir bash shell betiğinde koşma ile kaynaklanma arasında ayrım yapmak?


22

Burada sorduğum şey son derece alışılmadık / sıradışı / riskli veya Google-fu becerilerim sadece rahatsız edici değil ...

Bir bashkabuk betiğinde, başka bir kabuk betiği tarafından kaynaklanıp kaynaklanmadığını veya kendi başına mı çalıştırıldığını söylemenin kolay bir yolu var mı? Başka bir deyişle, aşağıdaki iki davranış arasında ayrım yapmak mümkün müdür?

# from another shell script
source myScript.sh

# from command prompt, or another shell script
./myScript.sh

Yapmayı düşündüğüm, bashkaynak verildiğinde kullanılabilecek işlevleri içeren bir yardımcı program benzeri kabuk betiği oluşturmak . Bu komut dosyası kendi başına çalıştırıldığında, tanımlanmış işlevlere dayalı olarak belirli işlemleri gerçekleştirmesini de isterim. Bu kabuk betiğinin alabileceği bir ortam değişkeni var mı, örneğin;

some_function() {
    # ...
}
if [ -z "$IS_SOURCED" ]; then
    some_function;
fi

Tercihen, arayan betiğinin herhangi bir bayrak değişkenini ayarlamasını gerektirmeyen bir çözüm arıyorum.

düzenleme : betiğin kaynak koduyla çalıştırılması arasındaki farkı, burada kullanılan betiğin (her iki şekilde de) farkını söylemek mümkün olup olmadığını öğrenmeye çalışıyorum arasındaki farkı biliyorum .



@cuonglm sorumu düzenledi, ikisi arasındaki farkları biliyorum ama programatik olarak kabuk betiğinin de farkı söyleyip söylemediğini merak ediyorum.
hjk

4
@ cunonglm: Bir kopya değil. .Komut hakkında hiç bir şey sormuyor , bir betiğin normal olarak mı kaynaklandığını veya normal bir şekilde çalıştırılıp çalıştırılmadığını (örneğin bir deniz kabuğu altında) soruyor .
Jander,

Yığın taşma de aynı soruda çok iyi cevaplar: stackoverflow.com/a/28776166/96944
Jannie Theunissen

Yanıtlar:


19

Evet - $ 0 değişkeni, kodun çalıştırıldığı adı verir:

$ cat example.sh
#!/bin/bash
script_name=$( basename ${0#-} ) #- needed if sourced no path
this_script=$( basename ${BASH_SOURCE} )
if [[ ${script_name} = ${this_script} ]] ; then
    echo "running me directly"
else
    echo "sourced from ${script_name}"
fi 

$ cat example2.sh
#!/bin/bash
. ./example.sh

Hangi gibi çalışır:

$ ./example.sh
running me directly
$ ./example2.sh
example.sh sourced from example2.sh

Bu etkileşimli bir kabuktan kaynaklanmak için uygun değildir, ancak bu fikre sahip olursunuz (umarım).

BASH_SOURCE içerecek şekilde güncellendi - teşekkürler hjk


Yeterince yakın. :) En azından komut dosyasının adını belirtmem gerekecek küçük engel, ancak başka bir uygulanabilir çözüm yoksa bu cevabı alırım ...
hjk

7

@ DarkHeart'ın çevre değişkeni ile cevabını birleştiren numarayıBASH_SOURCE görünüyor:

$ head example*.sh
==> example2.sh <==
#!/bin/bash
. ./example.sh

==> example.sh <==
#!/bin/bash
if [ "$(basename $0)" = "$(basename $BASH_SOURCE)" ]; then
    echo "running directly"
else
    echo "sourced from $0"
fi
$ ./example2.sh
sourced from ./example2.sh
$ ./example.sh
running directly

düzenleme Sadece BASH_SOURCEdizideki öğelerin sayısını saymam gerekirse, hala daha basit bir çözüm gibi görünüyor :

if [ ${#BASH_SOURCE[@]} -eq 1 ]; then echo "running directly"; else echo "sourced from $0"; fi

1
Görünüşe göre 'bash_source' değişkeni aynı anda bulundu. :)
DarkHeart

@DarkHeart Cevabımı dizi boyutu sayma kullanımını da ekledik ... Beni bu yolda ipucu için teşekkürler! : D
hjk

1

BusyBox gibi çalışan bir kütüphane komut dosyası kullandım. İçinde kaynak olup olmadığını test etmek için aşağıdaki işlevi kullanıyorum ...

function isSourced () {
  [[ "${FUNCNAME[1]}" == "source" ]]  && return 0
  return 1
}

Bash tarafından tutulan FUNCNAME dizisi aslında bir işlev çağrısı yığınıdır. $FUNCNAME(veya ${FUNCNAME[0]}) şu anda yürütülen işlevin adıdır. ${FUNCNAME[1]}onu çağıran işlevin adı vb.

En üstteki öğe betiğin kendisi için özel bir değerdir. İçecek ...

  • komut dosyası kaynaklanıyorsa "kaynak" sözcüğü
  • Eğer kod çalıştırılıyorsa "main" sözcüğü VE test bir fonksiyon içinden gerçekleştiriliyor
  • "" (null) komut dosyası çalıştırılıyorsa VE test, herhangi bir işlevin dışında gerçekleştiriliyorsa, yani ... komut dosyası düzeyinde.

Yukarıdaki işlev aslında yalnızca komut dosyası düzeyinde çağrıldığında çalışır (ihtiyacım olan tek şey budur). Dizi öğesi numarası yanlış olacağı için başka bir işlevin içinden çağrılırsa başarısız olur. Herhangi bir yerde çalışmasını sağlamak için yığının tepesini bulmak ve daha karmaşık olan bu değeri test etmek gerekir.

İhtiyacınız olursa, "yığının en üstündeki" dizi öğesinin numarasını ...

  local _top_of_stack=$(( ${#FUNCNAME[@]} - 1 ))

${#FUNCNAME[@]}dizideki öğelerin sayısı. Sıfır tabanlı bir dizi olarak, son öğeyi almak için 1'i çıkardık.

Bu üç işlev, Python'unkine benzer bir işlev yığını izi üretmek için birlikte kullanılır ve size tüm bunların nasıl çalıştığı hakkında daha iyi bir fikir verebilirler ...

function inspFnStack () {
  local T+="  "
  local _at=
  local _text="\n"
  local _top=$(inspFnStackTop)
  local _fn=${FUNCNAME[1]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local i=_top; ((--i))
  #
  _text+="$i item function call stack for $_fn ...\n"
  _text+="| L   BASH_SOURCE{BASH_LINENO called from}.FUNCNAME  \n"
  _text+="| ---------------------------------------------------\n"
  while (( $i > 0 ))
  do
    _text+="| $i ${T}$(inspFnStackItem $i)\n"
    T+="  "
    ((--i))
  done
  #
  printf "$_text\n"
  #
  return 0
}

function inspFnStackItem ()  {
  local _i=$1
  local _fn=${FUNCNAME[$_i]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local _at="${BASH_LINENO[$_i-1]}"; [[ $_at == 1 ]]  && _at="trap"
  local _item="${BASH_SOURCE[$_i]}{${_at}}.$_fn"
  #
  printf "%s" "$_item"
  return 0
}

function inspFnStackTop () {
  # top stack item is 1 less than length of FUNCNAME array stack
  printf "%d\n" $(( ${#FUNCNAME[@]} - 1 ))
  #
  return 0
}

FUNCNAME, BASH_SOURCE ve BASH_LINENO öğelerinin bir üç boyutlu dizimiş gibi bash tarafından tutulan 3 dizi olduğunu unutmayın.


0

Sadece dizinin sayılmasının güvenilmez göründüğünü ve sourcebir nokta ( .) kullanmanın çok yaygın olduğu (ve sourceanahtar kelimenin önüne geçildiği için ) birinin kullanılmayacağını varsayması gerektiğini eklemek istiyorum .

Örneğin, sourced.shyalnızca içeren bir komut dosyası için echo $0:


$ . sourced.sh 
bash
$ source sourced.sh 
bash
$ chmod +x sourced.sh 
$ ./sourced.sh 
./sourced.sh
$ cat ./sourced.sh 
echo $0

Karşılaştırma çözümleri daha iyi iş önerdi.


0

Etkileşimli bir kabuktan tedarik ederken de çalışan bir yol :

if [ $BASH_LINENO -ne 0 ]; then
    some_function;
fi

BASH_LINENODeğişkeni de çağrı işlevi de çalıştırıldı tüm hatları ile bir dizidir. Komut dosyasını doğrudan veya bir satır numarasına karşılık gelen bir tamsayıyı çağırırsanız, sıfır olacaktır.

BASH_ * değişkenli dokümanlar

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.