Apakah ada perbedaan antara "." Dan "sumber" di bash?

38

Saya mencari perbedaan antara "." dan "source" perintah builtin dan beberapa sumber (misalnya, dalam diskusi ini , dan manual bash ) menyarankan bahwa ini sama saja.

Namun, setelah masalah dengan variabel lingkungan, saya melakukan tes. Saya membuat file testenv.shyang berisi:

#!/bin/bash
echo $MY_VAR

Di prompt perintah, saya melakukan hal berikut:

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

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

[perhatikan bahwa formulir 1 mengembalikan string kosong]

Jadi, percobaan kecil ini menunjukkan bahwa ada adalah perbedaan setelah semua, di mana untuk perintah "sumber", anak lingkungan mewarisi semua variabel dari induk satu, di mana untuk "" itu tidak.

Apakah saya kehilangan sesuatu, atau apakah ini fitur bash yang tidak didokumentasikan / usang ?

[GNU bash, versi 4.1.5 (1) -release (x86_64-pc-linux-gnu)]

ya
sumber

Jawaban:

68

Jawaban singkat

Dalam pertanyaan Anda, perintah kedua tidak menggunakan .shell builtin atau sourcebuiltin. Alih-alih, Anda sebenarnya menjalankan skrip dalam shell yang terpisah, dengan menjalankannya dengan nama seperti yang Anda lakukan dengan file yang dapat dieksekusi lainnya. Ini memberikannya seperangkat variabel terpisah (meskipun jika Anda mengekspor variabel dalam shell induknya, itu akan menjadi variabel lingkungan untuk setiap proses anak , dan karenanya akan dimasukkan dalam variabel shell anak ). Jika Anda mengubah /ke ruang, maka itu akan menjalankannya dengan .built-in, yang setara dengan source.

Penjelasan Lebih Lanjut

Ini adalah sintaks dari sourcebuilt-in shell, yang mengeksekusi isi skrip di shell saat ini (dan dengan demikian dengan variabel shell saat ini):

source testenv.sh

Ini adalah sintaks dari .built-in, yang melakukan hal yang sama seperti source:

. testenv.sh

Namun, sintaks ini menjalankan skrip sebagai file yang dapat dieksekusi, meluncurkan shell baru untuk menjalankannya:

./testenv.sh

Itu tidak menggunakan .built-in. Sebaliknya, .adalah bagian dari jalur ke file yang Anda jalankan. Secara umum, Anda dapat menjalankan file yang dapat dieksekusi di shell dengan menjalankannya dengan nama yang mengandung setidaknya satu /karakter. Untuk menjalankan file di direktori saat ini, mendahului dengan ./demikian merupakan cara termudah. Kecuali direktori saat ini ada di Anda PATH, Anda tidak dapat menjalankan skrip dengan perintah testenv.sh. Ini untuk mencegah orang dari mengeksekusi file secara tidak sengaja di direktori saat ini ketika mereka bermaksud untuk mengeksekusi perintah sistem atau file lain yang ada di beberapa direktori yang tercantum dalam PATHvariabel lingkungan.

Karena menjalankan file dengan nama (bukan dengan sourceatau .) menjalankannya di shell baru, itu akan memiliki set variabel shell sendiri. Shell baru memang mewarisi variabel lingkungan dari proses pemanggilan (yang dalam hal ini adalah shell interaktif Anda) dan variabel lingkungan tersebut menjadi variabel shell di shell baru. Namun, untuk variabel shell yang akan diteruskan ke shell baru, salah satu dari berikut ini harus terjadi:

  1. Variabel shell telah diekspor, menyebabkannya menjadi variabel lingkungan. Gunakan exportshell built-in untuk ini. Dalam contoh Anda, Anda dapat menggunakan export MY_VAR=12345untuk mengatur dan mengekspor variabel dalam satu langkah, atau jika sudah diatur, Anda dapat menggunakannya export MY_VAR.

  2. Variabel shell secara eksplisit diatur dan diteruskan untuk perintah yang Anda jalankan, menjadikannya variabel lingkungan selama durasi perintah dijalankan. Ini biasanya mencapai bahwa:

    MY_VAR=12345 ./testenv.sh

    Jika MY_VARvariabel shell yang belum diekspor, Anda bahkan dapat menjalankan testenv.shdengan MY_VARmeneruskan sebagai variabel lingkungan dengan mengaturnya sendiri :

    MY_VAR="$MY_VAR" ./testenv.sh

./ Sintaks untuk Skrip Membutuhkan Baris Hashbang untuk Bekerja (dengan Benar)

Ngomong-ngomong, perlu diketahui bahwa, ketika Anda menjalankan executable dengan nama seperti di atas (dan tidak dengan .atau sourcebuilt-in shell), program shell apa yang digunakan untuk menjalankannya biasanya tidak ditentukan oleh shell yang Anda jalankan dari mana . Sebagai gantinya:

  • Untuk file biner, kernel dapat dikonfigurasi untuk menjalankan file jenis tertentu. Ini memeriksa dua byte pertama dari file untuk "angka ajaib" yang menunjukkan jenis biner yang dapat dieksekusi. Ini adalah bagaimana binari yang dapat dieksekusi dapat dijalankan.

    Ini, tentu saja, sangat penting, karena skrip tidak dapat berjalan tanpa shell atau penerjemah lain, yang merupakan biner yang dapat dieksekusi! Plus, banyak perintah dan aplikasi yang disusun binari daripada skrip.

    ( #!adalah representasi teks dari "angka ajaib" yang menunjukkan teks yang dapat dieksekusi.)

  • Untuk file yang seharusnya dijalankan di shell atau bahasa yang diterjemahkan lainnya, baris pertama terlihat seperti:

    #!/bin/sh

    /bin/shdapat diganti dengan shell atau interpreter lain apa pun yang dimaksudkan untuk menjalankan program. Misalnya, program Python mungkin dimulai dengan baris:

    #!/usr/bin/python

    Garis-garis ini disebut hashbang, shebang, dan sejumlah nama serupa lainnya. Lihat entri FOLDOC ini , artikel Wikipedia ini dan Apakah #! / Bin / sh dibaca oleh penerjemah? untuk informasi lebih lanjut.

  • Jika file teks ditandai dapat dieksekusi dan Anda menjalankannya dari shell Anda (seperti ./filename) tetapi tidak dimulai dengan #!, kernel gagal untuk mengeksekusinya. Namun, melihat bahwa ini telah terjadi, shell Anda akan mencoba menjalankannya dengan memberikan namanya ke beberapa shell. Ada beberapa persyaratan yang ditempatkan pada shell apa itu ( "shell harus menjalankan perintah yang setara dengan meminta shell dipanggil ..." ). Dalam praktiknya , beberapa shell - termasuk bash* - menjalankan instance lain dari dirinya, sementara yang lain menggunakannya/bin/sh. Saya sangat menyarankan Anda menghindari ini dan menggunakan baris hashbang sebagai gantinya (atau menjalankan skrip dengan meneruskannya ke penerjemah yang diinginkan, misalnya, bash filename).

    * Manual GNU Bash , 3.7.2 Pencarian dan Eksekusi Perintah : "Jika eksekusi ini gagal karena file tidak dalam format yang dapat dieksekusi, dan file tersebut bukan direktori, itu diasumsikan sebagai skrip shell dan shell mengeksekusi seperti yang dijelaskan dalam Script Shell . "

Eliah Kagan
sumber
2
Apa yang saya temukan berguna sourceadalah bahwa fungsi menjadi tersedia dari bash tanpa perlu memuat atau meluncurkan lagi. Contoh #!/bin/bash function olakease {echo olakease;}. Setelah Anda memuatnya, source file.shAnda dapat langsung menelepon olakeasedari bash. Saya sangat suka itu. Sumber mengeksekusi kemudian memuat banyak hal, titik .hanya untuk eksekusi dan sesuatu seperti penggunaanbash file.sh
m3nda
2
@ erm3nda .memiliki perilaku ini juga: . file.shdan source file.shmelakukan hal yang persis sama, termasuk mempertahankan fungsi yang didefinisikan dalam file.sh. (Mungkin Anda berpikir ./file.sh, yang berbeda. Tetapi itu tidak menggunakan .builtin; sebaliknya, .adalah bagian dari jalan.)
Eliah Kagan
Oh !, saya tidak membaca dengan seksama file [spasi]. Terima kasih, mach.
m3nda
13

Ya, Anda melewatkan sesuatu.

Saya pikir Anda membingungkan '.' itu berarti direktori saat ini, seperti pada ./testenv.shdan '.' itu berarti source(yang merupakan perintah bawaan). Jadi dalam kasus ketika '.' berarti sourceitu akan terjadi . ./testenv.sh. Masuk akal?

Jadi coba ini:

MY_VAR=12345 
. ./testenv.sh
pengguna1477
sumber
2
The ./mengatakan itu persis di mana file itu, tanpa itu, bash akan melihat melalui PATH pertama, kemudian mencoba dir saat ini jika tidak menemukannya. Jika bash berjalan dalam mode POSIX, dan Anda tidak memberikan path ke file (seperti ./), itu hanya akan mencari di PATH, dan gagal menemukan file jika direktori saat ini tidak di PATH.
geirha
@geirha Ya, Anda benar, source(dan .) sebenarnya akan memeriksa $ PATH terlebih dahulu, meskipun mereka tidak benar-benar menjalankan skrip seperti biasanya. Komentar (mantan) saya salah.
Eliah Kagan
Singkat dan to the point +1
David Morales