มีความแตกต่างระหว่าง“.” และ“ แหล่ง” ในการทุบตีหลังจากทั้งหมดหรือไม่


37

ฉันกำลังมองหาความแตกต่างระหว่าง "." และคำสั่ง builtin "source" และแหล่งข้อมูลบางแหล่ง (เช่นในการสนทนานี้และbash manpage) แนะนำว่าสิ่งเหล่านี้เหมือนกัน

อย่างไรก็ตามหลังจากปัญหาเกี่ยวกับตัวแปรสภาพแวดล้อมฉันทำการทดสอบ ฉันสร้างไฟล์testenv.shที่มี:

#!/bin/bash
echo $MY_VAR

ในคอมมานด์พรอมต์ฉันทำสิ่งต่อไปนี้:

> chmod +x testenv.sh
> MY_VAR=12345
> ./testenv.sh

> source testenv.sh
12345
> MY_VAR=12345 ./testenv.sh
12345

[โปรดทราบว่าแบบฟอร์มที่ 1 ส่งคืนสตริงว่างเปล่า]

ดังนั้นนี้ทดลองเล็ก ๆ น้อย ๆ แสดงให้เห็นว่ามีคือความแตกต่างหลังจากที่ทุกที่สำหรับคำสั่ง "ต้นฉบับ" เด็กสืบทอดตัวแปรสภาพแวดล้อมทั้งหมดจากผู้ปกครองคนหนึ่งที่สำหรับ "" มันไม่ใช่.

ฉันไม่มีอะไรหรือนี้เป็นคุณลักษณะที่ไม่มีเอกสาร / เลิกการทุบตี ?

[GNU bash, รุ่น 4.1.5 (1) - ปล่อย (x86_64-pc-linux-gnu)]

คำตอบ:


67

คำตอบสั้น ๆ

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

คำอธิบายเพิ่มเติม

นี่คือไวยากรณ์ของsourceเชลล์ในตัวซึ่งดำเนินการเนื้อหาของสคริปต์ในเชลล์ปัจจุบัน (และด้วยตัวแปรของเชลล์ปัจจุบัน)

source testenv.sh

นี่คือไวยากรณ์ของ.บิวด์อินซึ่งทำสิ่งเดียวกับsource:

. testenv.sh

อย่างไรก็ตามไวยากรณ์นี้เรียกใช้สคริปต์เป็นไฟล์เรียกทำงานโดยเรียกใช้เชลล์ใหม่เพื่อเรียกใช้งาน:

./testenv.sh

ที่ไม่ได้ใช้งาน.ในตัว ค่อนข้าง.เป็นส่วนหนึ่งของเส้นทางไปยังไฟล์ที่คุณกำลังดำเนินการ โดยทั่วไปคุณสามารถเรียกใช้ไฟล์เรียกใช้งานใด ๆ ในเชลล์โดยเรียกใช้ไฟล์ด้วยชื่อที่มี/อักขระอย่างน้อยหนึ่งตัว หากต้องการเรียกใช้ไฟล์ในไดเรกทอรีปัจจุบันการทำก่อนหน้าให้./เป็นวิธีที่ง่ายที่สุด เว้นแต่ไดเรกทอรีปัจจุบันอยู่ในของคุณคุณจะไม่สามารถเรียกใช้สคริปต์ที่มีคำสั่งPATH testenv.shนี่คือการป้องกันไม่ให้คนเรียกใช้ไฟล์โดยไม่ตั้งใจในไดเรกทอรีปัจจุบันเมื่อพวกเขาตั้งใจที่จะใช้คำสั่งระบบหรือไฟล์อื่น ๆ ที่มีอยู่ในบางไดเรกทอรีที่ระบุไว้ในPATHตัวแปรสภาพแวดล้อม

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

  1. ตัวแปรเชลล์ได้ถูกส่งออกทำให้เป็นตัวแปรสภาพแวดล้อม ใช้exportเปลือกในตัวสำหรับสิ่งนี้ ในตัวอย่างของคุณคุณสามารถใช้export MY_VAR=12345เพื่อตั้งค่าและส่งออกตัวแปรในขั้นตอนเดียวหรือถ้าตั้งค่าไว้แล้วคุณก็สามารถใช้งานexport MY_VARได้

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

    MY_VAR=12345 ./testenv.sh

    ถ้าMY_VARเป็นตัวแปรเชลล์ที่ยังไม่ได้รับการส่งออกคุณยังสามารถทำงานtestenv.shกับMY_VARผ่านเป็นตัวแปรสภาพแวดล้อมโดยการตั้งค่าให้ตัวเอง :

    MY_VAR="$MY_VAR" ./testenv.sh

./ ไวยากรณ์สำหรับสคริปต้องใช้สาย Hashbang ในการทำงาน (ถูกต้อง)

โปรดทราบว่าเมื่อคุณเรียกใช้ไฟล์ปฏิบัติการตามชื่อด้านบน (และไม่ได้ใช้กับ.หรือsourceเชลล์ในตัว) โปรแกรมเชลล์ใดที่ใช้ในการเรียกใช้โปรแกรมนั้นมักไม่ได้รับการพิจารณาจากเชลล์ที่คุณใช้งาน . แทน:

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

    แน่นอนนี่เป็นสิ่งสำคัญอย่างยิ่งเพราะสคริปต์ไม่สามารถทำงานได้หากไม่มีเชลล์หรือล่ามอื่น ๆ ซึ่งเป็นไบนารีที่สามารถเรียกทำงานได้! นอกจากนี้คำสั่งและแอปพลิเคชันจำนวนมากยังมีการรวบรวมไบนารีมากกว่าสคริปต์

    ( #!คือการแทนข้อความของ "หมายเลขมายากล" ที่ระบุถึงข้อความที่สามารถเรียกใช้งานได้)

  • สำหรับไฟล์ที่ควรรันในเชลล์หรือภาษาที่ตีความอื่น ๆ บรรทัดแรกจะมีลักษณะดังนี้:

    #!/bin/sh

    /bin/shอาจถูกแทนที่ด้วยสิ่งอื่น ๆ เปลือกหรือล่ามมีวัตถุประสงค์เพื่อเรียกใช้โปรแกรม ตัวอย่างเช่นโปรแกรม Python อาจเริ่มต้นด้วยบรรทัด:

    #!/usr/bin/python

    บรรทัดเหล่านี้เรียกว่า hashbang, shebang และชื่ออื่นที่คล้ายคลึงกัน ดูรายการ FOLDOCนี้บทความ Wikipedia นี้และ#! / bin / sh อ่านโดยล่ามหรือไม่ สำหรับข้อมูลเพิ่มเติม.

  • หากไฟล์ข้อความถูกทำเครื่องหมายว่าสามารถเรียกใช้งานได้และคุณเรียกใช้จากเชลล์ (เช่น./filename) แต่ไม่ได้ขึ้นต้นด้วย#!เคอร์เนลจะไม่สามารถเรียกใช้งานได้ อย่างไรก็ตามเห็นว่านี้ได้เกิดขึ้นเปลือกของคุณจะพยายามที่จะรันโดยผ่านชื่อบางเปลือก มีความต้องการไม่กี่วางอยู่บนสิ่งที่เปลือกที่เป็น ( "เปลือกจะปฏิบัติเทียบเท่ากับคำสั่งที่จะมีเปลือกเรียก ..." ) ในทางปฏิบัติเชลล์บางตัว - รวมถึงbash* - รันอินสแตนซ์อื่นของตัวเองขณะที่บางตัวใช้/bin/sh. ฉันขอแนะนำให้คุณหลีกเลี่ยงปัญหานี้และใช้บรรทัด hashbang แทน (หรือเรียกใช้สคริปต์โดยส่งต่อไปยังล่ามที่ต้องการเช่นbash filename)

    * GNU Bash manual , 3.7.2 Command Search and Execution : "ถ้าการดำเนินการนี้ล้มเหลวเนื่องจากไฟล์ไม่อยู่ในรูปแบบที่สามารถใช้งานได้และไฟล์นั้นไม่ใช่ไดเรกทอรีจะถือว่าเป็นเชลล์สคริปต์และเชลล์เรียกใช้งานตามที่อธิบายไว้ ในShell Scripts "


2
สิ่งที่ฉันพบว่ามีประโยชน์sourceคือฟังก์ชั่นพร้อมใช้งานจากการทุบตีโดยไม่จำเป็นต้องโหลดหรือเปิดอีกครั้ง #!/bin/bash function olakease {echo olakease;}ตัวอย่าง เมื่อคุณโหลดด้วยsource file.shคุณสามารถโทรolakeaseจากทุบตีโดยตรง ฉันชอบมันมาก ที่มาประมวลผลจะโหลดหลายสิ่งหลายอย่างจุด.นี้เป็นเพียงการดำเนินการและเป็นสิ่งที่ต้องการใช้งานbash file.sh
erm3nda

2
@ erm3nda .มีพฤติกรรมนี้เช่นกัน: . file.shและsource file.shทำสิ่งเดียวกันfile.shทุกประการรวมถึงฟังก์ชั่นการเก็บรักษาที่กำหนดไว้ (บางทีคุณกำลังคิด./file.shซึ่งแตกต่างกัน แต่ที่ไม่ได้ใช้.builtin; แต่.เป็นส่วนหนึ่งของเส้นทาง)
Eliah Kagan

โอ้! ฉันไม่ได้อ่านไฟล์ [space] อย่างระมัดระวัง ขอบคุณมาก
erm3nda

13

ใช่คุณหายไปบางอย่าง

ฉันคิดว่าคุณสับสน '.' นั่นหมายถึงไดเรกทอรีปัจจุบันใน./testenv.shและ '.' นั่นหมายถึงsource(ซึ่งเป็นคำสั่งในตัว) ดังนั้นในกรณีที่ '.' หมายความว่ามันจะเป็นsource . ./testenv.shทำให้รู้สึก?

ลองทำสิ่งนี้:

MY_VAR=12345 
. ./testenv.sh

2
ตัว./บอกว่าไฟล์อยู่ตรงไหนโดยไม่มีไฟล์ bash จะดูผ่าน PATH ก่อนจากนั้นลองใช้ dir ปัจจุบันหากไม่พบมัน หาก bash กำลังทำงานในโหมด POSIX และคุณไม่ได้ระบุเส้นทางไปยังไฟล์ (เช่น./) มันจะค้นหาเฉพาะใน PATH และล้มเหลวในการค้นหาไฟล์หาก dir ปัจจุบันไม่ได้อยู่ใน PATH
geirha

@geirha ใช่คุณพูดถูกsource(และ.) จริง ๆ แล้วจะตรวจสอบ $ PATH ก่อนถึงแม้ว่าพวกเขาจะไม่ได้ใช้งานสคริปต์ตามปกติ ความคิดเห็น (อดีต) ของฉันไม่ถูกต้อง
Eliah Kagan

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