Rốt cuộc, có sự khác biệt nào giữa những người khác.


37

Tôi đã tìm kiếm sự khác biệt giữa "." và các lệnh dựng sẵn "nguồn" và một vài nguồn (ví dụ, trong cuộc thảo luận này và trang bash ) gợi ý rằng chúng giống nhau.

Tuy nhiên, sau một vấn đề với các biến môi trường, tôi đã tiến hành kiểm tra. Tôi đã tạo một tệp testenv.shcó chứa:

#!/bin/bash
echo $MY_VAR

Trong dấu nhắc lệnh, tôi đã thực hiện như sau:

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

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

[lưu ý rằng biểu mẫu thứ nhất trả về một chuỗi trống]

Vì vậy, thí nghiệm này ít thấy rằng có một sự khác biệt sau khi tất cả, nơi cho lệnh "nguồn", các kế thừa Môi trường lấy trẻ tất cả các biến từ phụ huynh một, nơi cho "" nó không.

Tôi có thiếu một cái gì đó không, hay đây là một tính năng không có giấy tờ / không dùng nữa của bash ?

[GNU bash, phiên bản 4.1.5 (1) -release (x86_64-pc-linux-gnu)]

Câu trả lời:


68

Câu trả lời ngắn

Trong câu hỏi của bạn, lệnh thứ hai không sử dụng hàm .dựng sẵn hay vỏ sourcedựng. Thay vào đó, bạn thực sự đang chạy tập lệnh trong một shell riêng, bằng cách gọi nó theo tên như bạn làm với bất kỳ tệp thực thi nào khác. Điều này không cung cấp cho nó một tập hợp các biến riêng biệt (mặc dù nếu bạn xuất một biến trong vỏ cha của nó, nó sẽ là một biến môi trường cho bất kỳ tiến trình con nào , và do đó sẽ được bao gồm trong các biến của vỏ con ). Nếu bạn thay đổi /thành một khoảng trắng, thì nó sẽ chạy nó với tích .hợp sẵn, tương đương với source.

Giải thích mở rộng

Đây là cú pháp của trình sourcebao dựng sẵn, thực thi nội dung của tập lệnh trong trình bao hiện tại (và do đó với các biến của trình bao hiện tại):

source testenv.sh

Đây là cú pháp của tích .hợp sẵn, thực hiện tương tự như source:

. testenv.sh

Tuy nhiên, cú pháp này chạy tập lệnh dưới dạng tệp thực thi, khởi chạy shell mới để chạy nó:

./testenv.sh

Đó là không sử dụng tích .hợp. Thay vào đó, .là một phần của đường dẫn đến tệp bạn đang thực hiện. Nói chung, bạn có thể chạy bất kỳ tệp thực thi nào trong trình bao bằng cách gọi nó với tên chứa ít nhất một /ký tự. Để chạy một tệp trong thư mục hiện tại, trước đó ./là cách dễ nhất. Trừ khi thư mục hiện tại nằm trong bạn PATH, bạn không thể chạy tập lệnh bằng lệnh testenv.sh. Điều này là để ngăn mọi người vô tình thực thi các tệp trong thư mục hiện tại khi họ có ý định thực thi lệnh hệ thống hoặc một số tệp khác tồn tại trong một số thư mục được liệt kê trong PATHbiến môi trường.

Vì chạy tệp theo tên (chứ không phải với sourcehoặc .) chạy tệp trong trình bao mới, nên nó sẽ có tập hợp các biến shell riêng. Shell mới kế thừa các biến môi trường từ quá trình gọi (trong trường hợp này là shell tương tác của bạn) và các biến môi trường đó trở thành biến shell trong shell mới. Tuy nhiên, để một biến shell được chuyển sang shell mới, một trong những điều sau đây phải là trường hợp:

  1. Biến shell đã được xuất, làm cho nó trở thành biến môi trường. Sử dụng exportvỏ tích hợp cho việc này. Trong ví dụ của bạn, bạn có thể sử dụng export MY_VAR=12345để đặt và xuất biến trong một bước hoặc nếu nó đã được đặt, bạn chỉ cần sử dụng export MY_VAR.

  2. Biến shell được đặt rõ ràng và được truyền cho lệnh bạn đang chạy, khiến nó là biến môi trường trong suốt thời gian của lệnh đang được chạy. Điều này thường hoàn thành rằng:

    MY_VAR=12345 ./testenv.sh

    Nếu MY_VARlà một biến shell chưa được xuất, bạn thậm chí có thể chạy testenv.shvới MY_VARbiến như một biến môi trường bằng cách đặt nó vào chính nó :

    MY_VAR="$MY_VAR" ./testenv.sh

./ Cú pháp cho tập lệnh Yêu cầu một dòng Hashbang hoạt động (Chính xác)

Nhân tiện, xin lưu ý rằng, khi bạn gọi một tệp thực thi bằng tên như trên (chứ không phải với phần tử tích hợp .hoặc sourceshell), chương trình shell nào được sử dụng để chạy nó thường không được xác định bởi shell nào bạn đang chạy nó . Thay thế:

  • Đối với các tệp nhị phân, kernel có thể được cấu hình để chạy các tệp thuộc loại cụ thể đó. Nó kiểm tra hai byte đầu tiên của tệp để tìm "số ma thuật" cho biết loại thực thi nhị phân đó là gì. Đây là cách nhị phân thực thi có thể chạy.

    Tất nhiên, điều này là vô cùng quan trọng, bởi vì một tập lệnh không thể chạy mà không có trình bao hoặc trình thông dịch khác, đây là một tệp nhị phân thực thi! Thêm vào đó, nhiều lệnh và ứng dụng được biên dịch nhị phân chứ không phải tập lệnh.

    ( #!là biểu diễn văn bản của "số ma thuật" biểu thị một văn bản thực thi.)

  • Đối với các tệp được cho là chạy trong trình bao hoặc ngôn ngữ được dịch khác, dòng đầu tiên trông như sau:

    #!/bin/sh

    /bin/shcó thể được thay thế bằng bất kỳ trình bao hoặc trình thông dịch nào khác nhằm chạy chương trình. Ví dụ: chương trình Python có thể bắt đầu bằng dòng:

    #!/usr/bin/python

    Những dòng này được gọi là hashbang, shebang và một số tên tương tự khác. Xem mục FOLDOC này , bài viết Wikipedia này#! / Bin / sh được người phiên dịch đọc? để biết thêm thông tin.

  • Nếu một tệp văn bản được đánh dấu thực thi và bạn chạy nó từ shell của bạn (như ./filename) nhưng nó không bắt đầu bằng #!, kernel không thực thi được. Tuy nhiên, thấy rằng điều này đã xảy ra, shell của bạn sẽ cố gắng chạy nó bằng cách chuyển tên của nó cho một số shell. Có vài yêu cầu đặt trên những gì shell đó là ( "vỏ trách nhiệm thực thi một lệnh tương đương với việc có một vỏ gọi ..." ). Trong thực tế , một số shell - bao gồm bash* - vượt qua một thể hiện khác của chúng, trong khi một số khác sử dụng/bin/sh. Tôi đặc biệt khuyên bạn nên tránh điều này và sử dụng một dòng hashbang thay vào đó (hoặc chạy tập lệnh bằng cách chuyển nó đến trình thông dịch mong muốn, ví dụ bash filename:).

    * Hướng dẫn sử dụng GNU Bash , 3.7.2 Tìm kiếm và thực thi lệnh : "Nếu việc thực thi này thất bại vì tệp không ở định dạng thực thi và tệp không phải là một thư mục, nó được coi là một tập lệnh shell và shell thực thi nó như được mô tả trong Shell Script . "


2
Những gì tôi thấy hữu ích sourcelà các chức năng đã có sẵn từ bash mà không cần phải tải hoặc khởi chạy lại. Ví dụ #!/bin/bash function olakease {echo olakease;}. Khi bạn tải nó với source file.shbạn có thể gọi trực tiếp olakeasetừ bash. Tôi thực sự thích điều đó. Nguồn thực thi sau đó tải rất nhiều thứ, dấu chấm .chỉ để thực hiện và là một cái gì đó giống như sử dụngbash file.sh
erm3nda

2
@ erm3nda .cũng có hành vi này: . file.shsource file.shthực hiện chính xác điều tương tự, bao gồm các hàm giữ lại được xác định trong file.sh. (Có lẽ bạn đang nghĩ đến ./file.sh, đó là khác nhau Nhưng điều đó. Không sử dụng .được xây dựng trong, thay vào đó, .là một phần của con đường.)
Eliah Kagan

Ồ!, Tôi đã không đọc kỹ tập tin. [Space]. Cảm ơn bạn rất mach.
erm3nda

13

Vâng, bạn đang thiếu một cái gì đó.

Tôi nghĩ bạn đang nhầm lẫn giữa '.' có nghĩa là thư mục hiện tại, như trong ./testenv.shvà '.' điều đó có nghĩa là source(đó là một lệnh tích hợp). Vì vậy, trong trường hợp khi '.' có nghĩa là sourcenó sẽ được . ./testenv.sh. Có lý?

Vì vậy, hãy thử điều này:

MY_VAR=12345 
. ./testenv.sh

2
./cho nó biết chính xác nơi tập tin mà không có nó, bash sẽ xem qua PATH trước, sau đó thử thư mục hiện tại nếu nó không tìm thấy nó. Nếu bash đang chạy ở chế độ POSIX và bạn không cung cấp đường dẫn đến tệp (như ./), nó sẽ chỉ tìm kiếm trong PATH và không tìm thấy tệp nếu thư mục hiện tại không ở trong PATH.
geirha

@geirha Vâng, bạn đúng, source(và .) thực sự sẽ kiểm tra $ PATH trước, mặc dù họ không thực sự chạy tập lệnh theo nghĩa thông thường. Nhận xét (trước đây) của tôi là không chính xác.
Eliah Kagan

Ngắn và đến điểm +1
David Morales
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.