วิธีที่ดีที่สุดที่จะรวมสคริปต์อื่น ๆ


353

วิธีที่คุณจะรวมสคริปต์ไว้กับ "แหล่ง"

เช่น:

main.sh:

#!/bin/bash

source incl.sh

echo "The main script"

incl.sh:

echo "The included script"

ผลลัพธ์ของการดำเนินการ "./main.sh" คือ:

The included script
The main script

... ตอนนี้ถ้าคุณพยายามเรียกใช้งานเชลล์สคริปต์จากตำแหน่งอื่นจะไม่สามารถหาการรวมได้เว้นแต่จะอยู่ในเส้นทางของคุณ

เป็นวิธีที่ดีในการตรวจสอบให้แน่ใจว่าสคริปต์ของคุณสามารถค้นหาสคริปต์รวมโดยเฉพาะอย่างยิ่งถ้าเช่นสคริปต์จะต้องพกพา?


2
ดูสิ่งนี้: stackoverflow.com/questions/59895/…
Lluís

1
คำถามของคุณดีมากและให้ข้อมูลว่าคำถามของฉันได้รับคำตอบก่อนที่คุณจะถามคำถามของคุณ! งานที่ดี!
Gabriel Staples

คำตอบ:


229

ฉันมักจะทำให้สคริปต์ของฉันทั้งหมดสัมพันธ์กับคนอื่น ด้วยวิธีนี้ฉันสามารถใช้ dirname:

#!/bin/sh

my_dir="$(dirname "$0")"

"$my_dir/other_script.sh"

5
สิ่งนี้จะไม่ทำงานหากสคริปต์ถูกดำเนินการผ่าน $ PATH แล้วwhich $0จะเป็นประโยชน์
ฮิวโก้

41
ไม่มีวิธีที่เชื่อถือได้ในการกำหนดตำแหน่งของสคริปต์เชลล์ดูmywiki.wooledge.org/BashFAQ/028
Philipp

12
@Phippipp ผู้เขียนรายการนั้นถูกต้องมีความซับซ้อนและมี gotchas แต่มันหายไปบางประเด็นสำคัญแรกผู้เขียนสันนิษฐานสิ่งต่าง ๆ มากมายเกี่ยวกับสิ่งที่คุณจะทำกับสคริปต์ทุบตีของคุณ ฉันไม่คาดหวังว่าสคริปต์ python จะทำงานหากไม่มีการขึ้นต่อกัน Bash เป็นภาษากาวที่ให้คุณทำสิ่งต่าง ๆ ได้อย่างรวดเร็ว เมื่อคุณต้องการให้ระบบสร้างของคุณทำงานได้ประโยชน์ (และคำเตือนที่ดีเกี่ยวกับสคริปต์ที่ไม่สามารถค้นหาการพึ่งพา) ชนะ
Aaron H.

11
เพิ่งเรียนรู้เกี่ยวกับBASH_SOURCEอาเรย์และองค์ประกอบแรกในอาเรย์จะชี้ไปยังแหล่งที่มาในปัจจุบันได้อย่างไร
haridsv

6
สิ่งนี้จะไม่ทำงานหากสคริปต์ต่างกัน เช่น /home/me/main.sh โทร /home/me/test/inc.sh เนื่องจาก dirname จะส่งคืน / home / me sacii คำตอบโดยใช้ BASH_SOURCE เป็นทางออกที่ดีกว่าstackoverflow.com/a/12694189/1000011
opticyclic

187

ฉันรู้ว่าฉันมางานปาร์ตี้ช้า แต่สิ่งนี้ควรจะทำงานไม่ว่าคุณจะเริ่มต้นสคริปต์อย่างไรและใช้งานบิวอินโดยเฉพาะ:

DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
. "$DIR/incl.sh"
. "$DIR/main.sh"

.(dot) คำสั่งเป็นนามแฝงไปsource, $PWDเป็นเส้นทางสำหรับไดเรกทอรีการทำงาน, BASH_SOURCEเป็นตัวแปรอาร์เรย์ที่มีสมาชิกเป็นชื่อไฟล์แหล่งที่มาของ${string%substring}แถบการแข่งขันที่สั้นที่สุดของ $ substring จากด้านหลังของ $ สตริง


7
นี่เป็นคำตอบเดียวในกระทู้ที่ทำงานให้ฉันอย่างสม่ำเสมอ
Justin

3
@sacii ฉันจะรู้ได้เมื่อไหร่ว่าต้องใช้สายif [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi? ฉันสามารถค้นหาความต้องการได้หากคำสั่งถูกวางใน bash prompt เพื่อรัน อย่างไรก็ตามหากทำงานในบริบทไฟล์สคริปต์ฉันไม่สามารถเห็นความต้องการมันได้ ...
Johnny Wong

2
นอกจากนี้ยังเป็นที่น่าสังเกตว่ามันใช้งานได้ตามที่คาดไว้ในหลาย ๆsources (ฉันหมายถึงถ้าคุณsourceเป็นสคริปต์ที่sourceอยู่ในไดเรกทอรีอื่นและอื่น ๆ มันก็ยังใช้งานได้)
Ciro Costa

4
สิ่งนี้จะไม่เป็น${BASH_SOURCE[0]}อย่างที่คุณต้องการเพียงเรียกชื่อล่าสุดหรือไม่ นอกจากนี้การใช้DIR=$(dirname ${BASH_SOURCE[0]})จะช่วยให้คุณกำจัด if-condition
kshenoy

1
คุณยินดีที่จะทำให้ข้อมูลโค้ดนี้มีอยู่ภายใต้ใบอนุญาตที่ไม่จำเป็นต้องมีแอตทริบิวต์เช่นCC0หรือเพียงแค่ปล่อยมันเป็นสาธารณสมบัติ ฉันต้องการที่จะใช้คำต่อคำนี้ แต่มันน่ารำคาญที่จะใส่ที่มาที่ด้านบนของสคริปต์ทุกเดียว!
BeeOnRope

52

ทางเลือก:

scriptPath=$(dirname $0)

คือ:

scriptPath=${0%/*}

.. ข้อได้เปรียบที่ไม่มีการพึ่งพา dirname ซึ่งไม่ใช่คำสั่งในตัว (และไม่สามารถใช้ได้ในโปรแกรมจำลองเสมอ)


2
basePath=$(dirname $0)ทำให้ฉันมีค่าว่างเมื่อไฟล์สคริปต์ที่มีแหล่งที่มา
prayagupd

41

หากอยู่ในไดเรกทอรีเดียวกันคุณสามารถใช้dirname $0:

#!/bin/bash

source $(dirname $0)/incl.sh

echo "The main script"

2
สองข้อผิดพลาด: 1) $0เป็น./t.shและผลตอบแทน dirname .; 2) หลังจากcd binการคืนสินค้า.ไม่ถูกต้อง $BASH_SOURCEไม่ดีกว่า
18446744073709551615

ข้อผิดพลาดอื่น: ลองใช้ช่องว่างในชื่อไดเรกทอรี
Hubert Grzeskowiak

source "$(dirname $0)/incl.sh" ทำงานได้กับกรณีเหล่านั้น
dsm

27

ฉันคิดว่าวิธีที่ดีที่สุดในการทำเช่นนี้คือใช้วิธีของ Chris Boran แต่คุณควรคำนวณ MY_DIR ด้วยวิธีนี้:

#!/bin/sh
MY_DIR=$(dirname $(readlink -f $0))
$MY_DIR/other_script.sh

ในการอ้างถึง man pages สำหรับ readlink:

readlink - display value of a symbolic link

...

  -f, --canonicalize
        canonicalize  by following every symlink in every component of the given 
        name recursively; all but the last component must exist

ฉันไม่เคยพบกรณีใช้งานที่MY_DIRคำนวณไม่ถูกต้อง หากคุณเข้าถึงสคริปต์ของคุณผ่าน symlink ในการ$PATHทำงานของคุณ


โซลูชันที่ดีและเรียบง่ายและทำงานอย่างมีชื่อเสียงสำหรับฉันในรูปแบบที่หลากหลายของการเรียกใช้สคริปต์ที่ฉันสามารถนึกถึง ขอบคุณ
Brian Cline

นอกเหนือจากปัญหาของคำพูดที่หายไปมีกรณีการใช้งานจริงที่คุณต้องการแก้ไขลิงก์สัญลักษณ์แทนที่จะใช้$0โดยตรงหรือไม่?
l0b0

1
@ l0b0: จินตนาการว่าสคริปต์ของคุณคือ/home/you/script.shคุณสามารถcd /homeและเรียกใช้สคริปต์ของคุณได้จากที่นั่น./you/script.shในกรณีนี้dirname $0จะกลับมา./youและรวมถึงสคริปต์อื่น ๆ จะล้มเหลว
dr.scre

1
อย่างไรก็ตามข้อเสนอแนะที่ดีฉันต้องทำสิ่งต่อไปนี้เพื่อให้สามารถอ่านตัวแปรของฉันใน ` MY_DIR=$(dirname $(readlink -f $0)); source $MY_DIR/incl.sh
Frederick Ollinger

21

การรวมกันของคำตอบสำหรับคำถามนี้ให้ทางออกที่แข็งแกร่งที่สุด

มันทำงานให้เราในสคริปต์ระดับการผลิตด้วยการสนับสนุนที่ดีของการพึ่งพาและโครงสร้างไดเรกทอรี:

#! / bin / ทุบตี

# เส้นทางแบบเต็มของสคริปต์ปัจจุบัน
นี่ = `readlink -f" $ {BASH_SOURCE [0]} "2> / dev / null || echo $ 0`

# ไดเรกทอรีที่สคริปต์ปัจจุบันตั้งอยู่
DIR = `dirname" $ ​​{This} "`

# 'Dot' หมายถึง 'แหล่งที่มา' เช่น 'รวม':
. "$ DIR / compile.sh"

วิธีการสนับสนุนสิ่งเหล่านี้ทั้งหมด:

  • ช่องว่างในเส้นทาง
  • ลิงค์ (ผ่านreadlink)
  • ${BASH_SOURCE[0]} แข็งแกร่งกว่า $0

1
นี่ = readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0 ถ้า readlink ของคุณคือ BusyBox v1.01
Alexx Roche

@AlexxRoche ขอบคุณ! มันจะใช้ได้กับ Linuxes ทั้งหมดหรือไม่
Brian Haak

1
ฉันคาดหวังเช่นนั้น ดูเหมือนว่าจะทำงานบน Debian Sid 3.16 และ armv5tel ของ QNAP 3.4.6 Linux
Alexx Roche

2
ฉันใส่มันลงในหนึ่งบรรทัด:DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0)) # https://stackoverflow.com/a/34208365/
คิวเมนตัส

20
SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/incl.sh"

1
ฉันสงสัยว่าคุณได้รับการโหวตให้เป็น "cd ... " เมื่อ dirname "$ 0" ควรทำสิ่งเดียวกัน ...
Aaron H.

7
รหัสนี้จะส่งคืนพา ธ สัมบูรณ์แม้ว่าจะถูกเรียกใช้งานสคริปต์จากไดเรกทอรีปัจจุบัน $ (dirname "$ 0") เพียงอย่างเดียวจะกลับมาเพียง "."
Max

1
และ./incl.shแก้ไขไปยังเส้นทางเดียวกับ+cd pwdดังนั้นข้อดีของการเปลี่ยนไดเรกทอรีคืออะไร?
l0b0

บางครั้งคุณต้องการเส้นทางที่สมบูรณ์ของสคริปต์เช่นเมื่อคุณต้องเปลี่ยนไดเรกทอรีไปมา
Laryx Decidua

15

สิ่งนี้ใช้ได้แม้ว่าสคริปต์จะมีแหล่งที่มา:

source "$( dirname "${BASH_SOURCE[0]}" )/incl.sh"

คุณช่วยอธิบายจุดประสงค์ของ "[0]" ได้อย่างไร?
เรย์

1
@Ray BASH_SOURCEเป็นอาร์เรย์ของเส้นทางที่สอดคล้องกับ call-stack องค์ประกอบแรกสอดคล้องกับสคริปต์ล่าสุดในสแต็กซึ่งเป็นสคริปต์ที่ดำเนินการในปัจจุบัน ที่จริงแล้ว$BASH_SOURCEเรียกว่าเป็นตัวแปรขยายองค์ประกอบแรกโดยค่าเริ่มต้นจึง[0]ไม่จำเป็นที่นี่ ดูลิงค์นี้สำหรับรายละเอียด
Jonathan H

10

1. Neatest

ฉันสำรวจเกือบทุกข้อเสนอแนะและนี่คือสิ่งที่ประณีตที่สุดที่เหมาะกับฉัน:

script_root=$(dirname $(readlink -f $0))

มันทำงานได้แม้ในขณะที่สคริปต์เชื่อมโยงกับ$PATHไดเรกทอรี

ดูการทำงานที่นี่: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent

2. ที่เด็ดสุด

# Copyright https://stackoverflow.com/a/13222994/257479
script_root=$(ls -l /proc/$$/fd | grep "255 ->" | sed -e 's/^.\+-> //')

อันนี้มาจากคำตอบอื่นในหน้านี้ แต่ฉันก็เพิ่มเข้าไปในคำตอบด้วย!

2. ที่น่าเชื่อถือที่สุด

อีกทางหนึ่งในกรณีที่ไม่ค่อยเกิดขึ้นซึ่งสิ่งเหล่านี้ไม่ได้ผลนี่เป็นวิธีการพิสูจน์ด้วยกระสุน:

# Copyright http://stackoverflow.com/a/7400673/257479
myreadlink() { [ ! -h "$1" ] && echo "$1" || (local link="$(expr "$(command ls -ld -- "$1")" : '.*-> \(.*\)$')"; cd $(dirname $1); myreadlink "$link" | sed "s|^\([^/].*\)\$|$(dirname $1)/\1|"); }
whereis() { echo $1 | sed "s|^\([^/].*/.*\)|$(pwd)/\1|;s|^\([^/]*\)$|$(which -- $1)|;s|^$|$1|"; } 
whereis_realpath() { local SCRIPT_PATH=$(whereis $1); myreadlink ${SCRIPT_PATH} | sed "s|^\([^/].*\)\$|$(dirname ${SCRIPT_PATH})/\1|"; } 

script_root=$(dirname $(whereis_realpath "$0"))

คุณสามารถดูได้ในtaskrunnerแหล่งที่มา: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunner

หวังว่านี่จะช่วยให้ใครบางคนที่นั่น :)

นอกจากนี้โปรดทิ้งไว้เป็นความคิดเห็นหากคุณไม่ได้ทำงานและพูดถึงระบบปฏิบัติการและโปรแกรมจำลองของคุณ ขอบคุณ!


7

คุณต้องระบุตำแหน่งของสคริปต์อื่น ๆ ไม่มีวิธีอื่นในการใช้งาน ฉันขอแนะนำตัวแปรที่กำหนดค่าได้ที่ด้านบนสุดของสคริปต์ของคุณ:

#!/bin/bash
installpath=/where/your/scripts/are

. $installpath/incl.sh

echo "The main script"

อีกทางหนึ่งคุณสามารถยืนยันได้ว่าผู้ใช้รักษาตัวแปรสภาพแวดล้อมที่ระบุตำแหน่งที่บ้านของคุณอยู่ที่โปรแกรมเช่น PROG_HOME หรือบางส่วน สิ่งนี้สามารถให้สำหรับผู้ใช้โดยอัตโนมัติโดยการสร้างสคริปต์ด้วยข้อมูลนั้นใน /etc/profile.d/ ซึ่งจะได้รับแหล่งที่มาทุกครั้งที่ผู้ใช้เข้าสู่ระบบ


1
ฉันขอขอบคุณความปรารถนาที่เฉพาะเจาะจง แต่ฉันไม่เห็นว่าทำไมต้องใช้เส้นทางแบบเต็มยกเว้นว่าสคริปต์รวมเป็นส่วนหนึ่งของแพ็คเกจอื่น ฉันไม่เห็นความแตกต่างด้านความปลอดภัยโหลดจากเส้นทางสัมพัทธ์เฉพาะ (เช่น dir เดียวกับที่สคริปต์กำลังดำเนินการ) เทียบกับพา ธ เต็ม ทำไมคุณถึงบอกว่าไม่มีทางรอบ ๆ มัน?
Aaron H.

4
เนื่องจากไดเรกทอรีที่สคริปต์ของคุณกำลังทำงานอยู่นั้นไม่จำเป็นต้องเป็นที่ที่สคริปต์ที่คุณต้องการรวมไว้ในสคริปต์ของคุณอยู่ คุณต้องการโหลดสคริปต์ที่ติดตั้งไว้ที่และไม่มีวิธีที่เชื่อถือได้ในการบอกตำแหน่งที่อยู่ ณ รันไทม์ การไม่ใช้ตำแหน่งคงที่ยังเป็นวิธีที่ดีในการรวมสคริปต์ที่ไม่ถูกต้อง (เช่นแฮ็กเกอร์ที่ให้มา) และเรียกใช้งาน
Steve Baker

6

ฉันขอแนะนำให้คุณสร้างสคริปต์ setenv โดยมีวัตถุประสงค์เพียงประการเดียวคือจัดเตรียมสถานที่สำหรับส่วนประกอบต่างๆทั่วทั้งระบบของคุณ

สคริปต์อื่นทั้งหมดจะส่งสคริปต์นี้เพื่อที่ตั้งทั้งหมดจะใช้ร่วมกันกับสคริปต์ทั้งหมดโดยใช้สคริปต์ setenv

สิ่งนี้มีประโยชน์มากเมื่อใช้ cronjobs คุณจะได้รับสภาพแวดล้อมที่น้อยที่สุดเมื่อใช้ cron แต่ถ้าคุณสร้างสคริปต์ cron ทั้งหมดก่อนรวมถึงสคริปต์ setenv คุณจะสามารถควบคุมและประสานสภาพแวดล้อมที่คุณต้องการให้ cronjobs ทำงานได้

เราใช้เทคนิคดังกล่าวในการสร้างลิงของเราซึ่งใช้สำหรับการรวมอย่างต่อเนื่องในโครงการประมาณ 2,000 kSLOC


3

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

จากนั้นสคริปต์ทั้งหมดจะมาสคริปต์นั้นและควรติดตั้งเปลี่ยนพา ธ คุณจะต้องเปลี่ยนมันในที่เดียว ทำให้สิ่งต่าง ๆ มากขึ้นเอ่ออนาคต พระเจ้าฉันเกลียดคำนั้น! (-:

BTW คุณควรอ้างถึงตัวแปรโดยใช้ $ {installpath} เมื่อใช้ในวิธีที่แสดงในตัวอย่างของคุณ:

. ${installpath}/incl.sh

หากวงเล็บปีกกาถูกทิ้งไว้เปลือกบางส่วนจะพยายามและขยายตัวแปร "installpath / incl.sh"!


3

Shell Script Loaderเป็นทางออกของฉันในเรื่องนี้

มันมีฟังก์ชั่นชื่อรวม () ที่สามารถเรียกได้หลายครั้งในหลาย ๆ สคริปต์เพื่ออ้างถึงสคริปต์เดียว แต่จะโหลดสคริปต์เพียงครั้งเดียว ฟังก์ชันสามารถยอมรับเส้นทางที่สมบูรณ์หรือเส้นทางบางส่วน (สคริปต์ถูกค้นหาในเส้นทางการค้นหา) นอกจากนี้ยังมีฟังก์ชั่นที่คล้ายกันชื่อว่า load () ที่จะโหลดสคริปต์โดยไม่มีเงื่อนไข

มันใช้งานได้กับbash , ksh , pd kshและzsh พร้อมกับสคริปต์ที่เหมาะสมที่สุดสำหรับแต่ละอัน และเชลล์อื่น ๆ ที่เข้ากันได้กับ sh ดั้งเดิมเช่นash , dash , heirloom sh , ฯลฯ ผ่านสคริปต์สากลที่ปรับฟังก์ชันการทำงานให้เหมาะสมโดยอัตโนมัติขึ้นอยู่กับคุณสมบัติที่เชลล์สามารถให้ได้

[ตัวอย่างที่ไม่ดี]

start.sh

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

#!/bin/sh

# load loader.sh
. loader.sh

# include directories to search path
loader_addpath /usr/lib/sh deps source

# load main script
load main.sh

main.sh

include a.sh
include b.sh

echo '---- main.sh ----'

# remove loader from shellspace since
# we no longer need it
loader_finish

# main procedures go from here

# ...

เถ้า

include main.sh
include a.sh
include b.sh

echo '---- a.sh ----'

b.sh

include main.sh
include a.sh
include b.sh

echo '---- b.sh ----'

เอาท์พุท:

---- b.sh ----
---- a.sh ----
---- main.sh ----

สิ่งที่ดีที่สุดคือสคริปต์ที่อ้างอิงตามนั้นอาจถูกรวบรวมเพื่อสร้างสคริปต์เดียวกับคอมไพเลอร์ที่มีอยู่

นี่คือโครงการที่ใช้: http://sourceforge.net/p/playshell/code/ci/master/tree/ http://sourceforge.net/p/playshell/code/ci/master/tree/มันสามารถรันได้แบบพกพาโดยมีหรือไม่มีการรวบรวมสคริปต์ การคอมไพล์เพื่อสร้างสคริปต์เดียวสามารถเกิดขึ้นได้และมีประโยชน์ในระหว่างการติดตั้ง

ฉันยังสร้างต้นแบบที่เรียบง่ายขึ้นสำหรับกลุ่มอนุรักษ์นิยมที่อาจต้องการทราบข้อมูลสั้น ๆ เกี่ยวกับวิธีการใช้งานสคริปต์: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype .bash มันมีขนาดเล็กและทุกคนก็สามารถรวมรหัสในสคริปต์หลักของพวกเขาหากพวกเขาต้องการที่จะถ้ารหัสของพวกเขามีจุดมุ่งหมายในการทำงานด้วยทุบตี 4.0 evalหรือใหม่กว่าและยังไม่ได้ใช้


3
สคริปต์ Bash 12 กิโลไบต์ที่มีevalโค้ด ed มากกว่า 100 บรรทัดเพื่อโหลดการขึ้นต่อกัน Ouch
l0b0

1
หนึ่งในสามevalช่วงตึกใกล้กับด้านล่างนั้นจะทำงานอยู่เสมอ ดังนั้นไม่ว่าจะจำเป็นหรือไม่evalก็ตาม
l0b0

3
การโทรแบบ Eval นั้นปลอดภัยและไม่ได้ใช้ถ้าคุณมี Bash 4.0+ ฉันเห็นว่าคุณเป็นหนึ่งในคนเก็บขยะเก่าที่คิดว่าevalเป็นความชั่วร้ายที่บริสุทธิ์และไม่รู้ว่าจะใช้ประโยชน์จากมันได้อย่างไร
konsolebox

1
ฉันไม่รู้ว่าคุณหมายถึงอะไรโดย "ไม่ใช้" แต่มันถูกเรียกใช้ และหลังจากสคริปต์เชลล์เป็นเวลาหลายปีซึ่งเป็นส่วนหนึ่งของงานของฉันใช่ฉันมั่นใจมากขึ้นกว่าเดิมนั่นevalคือความชั่วร้าย
l0b0

1
วิธีการที่ง่ายและสะดวกในการติดธงทำเครื่องหมายไฟล์ให้นึกถึงทันที: โดยอ้างอิงถึงหมายเลขไอโหนดหรือโดยการใส่พา ธ ที่คั่นด้วย NUL ลงในไฟล์
l0b0

2

วางไลบรารี่ทั้งหมดไว้ในlibโฟลเดอร์และใช้importฟังก์ชั่นในการโหลด

โครงสร้างโฟลเดอร์

ป้อนคำอธิบายรูปภาพที่นี่

script.sh เนื้อหา

# Imports '.sh' files from 'lib' directory
function import()
{
  local file="./lib/$1.sh"
  local error="\e[31mError: \e[0mCannot find \e[1m$1\e[0m library at: \e[2m$file\e[0m"
  if [ -f "$file" ]; then
     source "$file"
    if [ -z $IMPORTED ]; then
      echo -e $error
      exit 1
    fi
  else
    echo -e $error
    exit 1
  fi
}

โปรดทราบว่าฟังก์ชั่นนำเข้านี้ควรอยู่ที่จุดเริ่มต้นของสคริปต์ของคุณจากนั้นคุณสามารถนำเข้าไลบรารีของคุณเช่นนี้:

import "utils"
import "requirements"

เพิ่มบรรทัดเดียวที่ด้านบนของแต่ละไลบรารี (เช่น utils.sh):

IMPORTED="$BASH_SOURCE"

ตอนนี้คุณสามารถเข้าถึงฟังก์ชั่นภายในutils.shและrequirements.shจากscript.sh

สิ่งที่ต้องทำ: เขียนลิงเกอร์เพื่อสร้างshไฟล์เดียว


สิ่งนี้ยังช่วยแก้ปัญหาในการเรียกใช้สคริปต์นอกไดเร็กสคริปต์ที่มันมีอยู่หรือไม่?
Aaron H.

@AaronH ไม่มันเป็นวิธีที่มีโครงสร้างสำหรับการรวมการพึ่งพาในโครงการขนาดใหญ่
Xaqron

1

ใช้แหล่งที่มาหรือ $ 0 จะไม่ให้เส้นทางที่แท้จริงของสคริปต์ของคุณ คุณสามารถใช้รหัสกระบวนการของสคริปต์เพื่อดึงข้อมูลเส้นทางที่แท้จริง

ls -l       /proc/$$/fd           | 
grep        "255 ->"            |
sed -e      's/^.\+-> //'

ฉันกำลังใช้สคริปต์นี้และมันให้บริการฉันดีอยู่เสมอ :)


1

ฉันใส่สคริปต์เริ่มต้นทั้งหมดลงในไดเรกทอรี. bashrc.d นี่เป็นเทคนิคทั่วไปในสถานที่เช่น /etc/profile.d ฯลฯ

while read file; do source "${file}"; done <<HERE
$(find ${HOME}/.bashrc.d -type f)
HERE

ปัญหากับการแก้ปัญหาโดยใช้ globbing ...

for file in ${HOME}/.bashrc.d/*.sh; do source ${file};done

... คุณอาจมีรายการไฟล์ที่ "ยาวเกินไป" วิธีการเช่น ...

find ${HOME}/.bashrc.d -type f | while read file; do source ${file}; done

... ทำงาน แต่ไม่เปลี่ยนสภาพแวดล้อมตามที่ต้องการ


1

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

scriptdir=`dirname "$BASH_SOURCE"`
source $scriptdir/incl.sh

echo "The main script"

ดังนั้นนี่อาจเป็นวิธีที่ "ดีที่สุด" ในการรวมสคริปต์อื่น ๆ นี่เป็นพื้นฐานของคำตอบ "ดีที่สุด" อีกอันที่บอกสคริปต์ทุบตีที่เก็บไว้


1

สิ่งนี้จะทำงานได้อย่างน่าเชื่อถือ:

source_relative() {
 local dir="${BASH_SOURCE%/*}"
 [[ -z "$dir" ]] && dir="$PWD"
 source "$dir/$1"
}

source_relative incl.sh

0

เราแค่ต้องค้นหาโฟลเดอร์ที่เก็บ incl.sh และ main.sh ของเรา เพียงเปลี่ยน main.sh ของคุณด้วยสิ่งนี้:

main.sh

#!/bin/bash

SCRIPT_NAME=$(basename $0)
SCRIPT_DIR="$(echo $0| sed "s/$SCRIPT_NAME//g")"
source $SCRIPT_DIR/incl.sh

echo "The main script"

ที่ไม่ถูกต้อง quoting, ไม่จำเป็นต้องใช้echoและการใช้งานที่ไม่ถูกต้องsedของgตัวเลือก -1
l0b0

อย่างไรก็ตามเพื่อให้งานสคริปต์นี้ทำได้: SCRIPT_DIR=$(echo "$0" | sed "s/${SCRIPT_NAME}//")จากนั้นsource "${SCRIPT_DIR}incl.sh"
Krzysiek

0

ตามman hierสถานที่ที่เหมาะสมสำหรับสคริปต์รวมถึงคือ/usr/local/lib/

/ usr / local / lib

ไฟล์ที่เกี่ยวข้องกับโปรแกรมที่ติดตั้งในเครื่อง

ส่วนตัวฉันชอบ/usr/local/lib/bash/includesรวมถึง มีbash-helper lib สำหรับการรวม libs ด้วยวิธีดังกล่าว:

#!/bin/bash

. /usr/local/lib/bash/includes/bash-helpers.sh

include api-client || exit 1                   # include shared functions
include mysql-status/query-builder || exit 1   # include script functions

# include script functions with status message
include mysql-status/process-checker; status 'process-checker' $? || exit 1
include mysql-status/nonexists; status 'nonexists' $? || exit 1

bash-helpers รวมเอาท์พุทสถานะ


-5

คุณยังสามารถใช้:

PWD=$(pwd)
source "$PWD/inc.sh"

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