แยกความแตกต่างระหว่างการรันและการซอร์สในสคริปต์เชลล์ bash หรือไม่?


22

ไม่ว่าฉันจะขออะไรจากที่นี่เป็นเรื่องแปลกแหกคอก / ไม่เป็นทางการ / มีความเสี่ยงหรือทักษะ Google-fu ของฉันไม่ได้ขึ้นอยู่กับว่า ...

ในbashเชลล์สคริปต์มีวิธีใดที่จะบอกได้ง่ายว่าเชลล์สคริปต์นั้นมาจากแหล่งอื่นหรือไม่? กล่าวอีกนัยหนึ่งเป็นไปได้ไหมที่จะแยกแยะระหว่างพฤติกรรมสองอย่างต่อไปนี้?

# from another shell script
source myScript.sh

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

สิ่งที่ฉันกำลังคิดที่จะทำคือการสร้างเชลล์สคริปต์แบบยูทิลิตี้ที่มีbashฟังก์ชั่นที่สามารถให้บริการได้เมื่อมีที่มา แม้ว่าสคริปต์นี้จะถูกเรียกใช้ด้วยตัวเองฉันก็อยากให้มันทำงานบางอย่างตามหน้าที่ที่กำหนดไว้เช่นกัน มีตัวแปรสภาพแวดล้อมบางชนิดที่เชลล์สคริปต์นี้สามารถรับได้เช่น

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

โดยเฉพาะอย่างยิ่งฉันกำลังมองหาโซลูชันที่ไม่ต้องการสคริปต์ผู้โทรเพื่อตั้งค่าตัวแปรสถานะใด ๆ

แก้ไข : ฉันทราบความแตกต่างระหว่างการจัดหาและการเรียกใช้สคริปต์สิ่งที่ฉันพยายามค้นหาที่นี่หากเป็นไปได้ที่จะบอกความแตกต่างในสคริปต์ที่ใช้งานอยู่ (ทั้งสองวิธี)



@conglm แก้ไขคำถามของฉันฉันรู้ว่าความแตกต่างระหว่างทั้งสอง แต่ฉันสงสัยว่าฉันสามารถเขียนโปรแกรมเชลล์สคริปต์ให้บอกความแตกต่างได้หรือไม่
hjk

4
@cuonglm: ไม่ซ้ำกัน เขาไม่ได้ถามเกี่ยวกับ.คำสั่งเลย แต่เกี่ยวกับการตรวจสอบว่าสคริปต์นั้นมีแหล่งที่มาหรือเรียกใช้ตามปกติ (เช่นใน subshell)
Jander

คำตอบที่ดีมากสำหรับคำถามเดียวกันที่ stack overflow: stackoverflow.com/a/28776166/96944
Jannie Theunissen

คำตอบ:


19

ใช่ - ตัวแปร $ 0 ให้ชื่อของสคริปต์เหมือนเดิม:

$ 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

ซึ่งทำงานเหมือน:

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

ที่ไม่ตอบสนองสำหรับการเป็นแหล่งจากเปลือกโต้ตอบ แต่คุณได้รับความคิดนี้ (ฉันหวังว่า)

อัปเดตเพื่อรวม BASH_SOURCE - ขอบคุณ hjk


ใกล้พอ :) อุปสรรคเล็ก ๆ ที่ฉันจะต้องระบุชื่อของสคริปต์อย่างน้อย แต่ฉันจะตอบคำถามนี้หากไม่มีวิธีแก้ไขปัญหาอื่น ๆ ที่ทำงานได้ ...
hjk

7

การรวมคำตอบของ @ DarkHeart เข้ากับตัวแปรสภาพแวดล้อมBASH_SOURCEดูเหมือนจะเป็นการหลอกลวง:

$ 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

แก้ไขดูเหมือนว่าจะเป็นวิธีที่ง่ายกว่าถ้าฉันต้องนับจำนวนองค์ประกอบในBASH_SOURCEอาร์เรย์ของ:

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

1
ดูเหมือนว่าเราพบตัวแปร 'bash_source' ในเวลาเดียวกัน :)
DarkHeart

@DarkHeart ฉันได้เพิ่มคำตอบลงในการใช้งานการนับขนาดอาร์เรย์ด้วย ... ขอบคุณที่บอกให้ฉันรู้เส้นทางนี้! : D
hjk

1

ฉันเพิ่งสร้างไลบรารีสคริปต์ชนิดเดียวกับที่ทำงานมากเช่น BusyBox ในนั้นฉันใช้ฟังก์ชั่นต่อไปนี้เพื่อทดสอบว่ามีการจัดหา ...

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

อาร์เรย์ FUNCNAME ที่ได้รับการดูแลโดย Bash นั้นเป็นฟังก์ชันการเรียกฟังก์ชัน $FUNCNAME(หรือ${FUNCNAME[0]}) เป็นชื่อของฟังก์ชั่นการดำเนินการในปัจจุบัน ${FUNCNAME[1]}เป็นชื่อของฟังก์ชั่นที่เรียกมันและอื่น ๆ

รายการสูงสุดเป็นค่าพิเศษสำหรับสคริปต์ มันจะมี ...

  • คำว่า "แหล่งที่มา" ถ้าสคริปต์นั้นมีที่มา
  • คำว่า "main" ถ้าสคริปต์ถูกเรียกใช้งานและการทดสอบกำลังทำจากภายในฟังก์ชัน
  • "" (null) หากสคริปต์ถูกดำเนินการและการทดสอบกำลังดำเนินการนอกฟังก์ชั่นใด ๆ นั่นคือ ... ที่ระดับของสคริปต์เอง

ฟังก์ชั่นด้านบนใช้งานได้จริงเมื่อเรียกที่ระดับสคริปต์เท่านั้น (ซึ่งเป็นสิ่งที่ฉันต้องการ) มันจะล้มเหลวถ้าเรียกจากภายในฟังก์ชันอื่นเนื่องจากหมายเลขรายการอาร์เรย์จะผิด เพื่อให้ทำงานได้ทุกที่ต้องค้นหาด้านบนสุดของสแต็กและทดสอบค่าซึ่งมีความซับซ้อนมากขึ้น

หากคุณต้องการคุณจะได้รับหมายเลขรายการอาเรย์ของ "top of the stack" ด้วย ...

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

${#FUNCNAME[@]}คือจำนวนรายการในอาร์เรย์ ในฐานะที่เป็นอาร์เรย์แบบ zero-based เราลบ 1 เพื่อรับรายการสุดท้าย #

ฟังก์ชั่นทั้งสามนี้ใช้ร่วมกันเพื่อสร้างฟังก์ชั่นการติดตามสแต็กคล้ายกับ Python และพวกเขาอาจให้ความคิดที่ดีกว่าว่ามันทำงานอย่างไร ...

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 และ BASH_LINENO เป็น 3 อาร์เรย์ที่ดูแลโดย bash ราวกับว่ามันเป็นหนึ่งในสามมิติ


0

เพียงแค่ต้องการที่จะเพิ่มที่นับอาร์เรย์ที่ดูเหมือนจะไม่น่าเชื่อถือและเป็นหนึ่งอาจจะไม่ถือว่าsourceถูกนำมาใช้ตั้งแต่ใช้จุด ( .) เป็นเรื่องธรรมดามากเช่นกัน (และถือกำเนิดsourceคำหลัก)

ตัวอย่างเช่นสำหรับsourced.shสคริปต์ที่มีเพียงecho $0:


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

โซลูชันการเปรียบเทียบที่แนะนำนั้นทำงานได้ดีกว่า


0

วิธีหนึ่งที่ใช้งานได้เช่นกันเมื่อจัดหาจากเชลล์แบบโต้ตอบ :

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

BASH_LINENOตัวแปรยังเป็นอาร์เรย์ที่มีเส้นทุกฟังก์ชั่นโทรถูกประหารชีวิตที่ มันจะเป็นศูนย์ถ้าคุณเรียกใช้สคริปต์โดยตรงหรือจำนวนเต็มที่สอดคล้องกับหมายเลขบรรทัด

ตัวแปร BASH_ *

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.