Pertanyaan Apakah ada perbedaan antara "." Dan "sumber" di bash, setelah semua?


Saya mencari perbedaan antara "." dan "sumber" perintah bawaan dan beberapa sumber (mis., di ini diskusi, dan pesta manpage) menunjukkan bahwa ini sama saja.

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

#!/bin/bash
echo $MY_VAR

Pada command prompt, 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, eksperimen kecil ini menunjukkan bahwa ada aku s perbedaan setelah semua, di mana untuk "sumber" perintah, lingkungan anak mewarisi semua variabel dari orang tua, di mana untuk "." itu tidak.

Apakah saya kehilangan sesuatu, atau ini adalah fitur yang tidak terdokumentasi / usang pesta?

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


30
2017-08-30 01:07


asal




Jawaban:


Jawaban singkat

Dalam pertanyaan Anda, perintah kedua tidak menggunakan keduanya . shell built-in maupun source built-in. Sebaliknya, Anda benar-benar menjalankan skrip dalam shell terpisah, dengan memanggilnya dengan nama (seperti file executable lainnya). Ini tidak memberinya kumpulan variabel lingkungan yang terpisah (meskipun jika Anda ekspor variabel lingkungan dalam shell induknya, itu akan dimasukkan). Jika Anda mengubah / ke spasi, maka itu akan menjalankannya dengan . built-in, yang setara dengan source.

Penjelasan yang Diperluas

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

source testenv.sh

Ini adalah sintaks dari . built-in, yang melakukan hal yang sama 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. Agak, . adalah bagian dari path ke file yang sedang Anda eksekusi. Secara umum, Anda dapat menjalankan file yang dapat dieksekusi dalam cangkang dengan memohonnya dengan nama yang mengandung setidaknya satu / karakter. Untuk menjalankan file di direktori saat ini, mendahuluinya dengan ./ dengan demikian adalah cara termudah. Kecuali direktori saat ini ada di Anda PATH, Anda tidak dapat menjalankan skrip dengan perintah testenv.sh. Ini untuk mencegah orang secara tidak sengaja mengeksekusi file di direktori saat ini ketika mereka berniat menjalankan perintah sistem atau beberapa file lain yang ada di beberapa direktori yang terdaftar di PATH variabel lingkungan.

Sejak menjalankan file berdasarkan nama (bukan dengan source atau .) menjalankannya dalam shell baru, ia akan memiliki kumpulan variabel lingkungan sendiri. Variabel lingkungan lakukan mewarisi dari variabel lingkungan dari proses panggilan (yang dalam hal ini adalah shell interaktif Anda). Namun, untuk variabel lingkungan yang akan diteruskan ke shell baru, salah satu hal berikut harus menjadi kasus:

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

  • Variabel lingkungan secara eksplisit ditetapkan dan dilewatkan untuk perintah yang Anda jalankan. Ini biasanya menyelesaikan itu:

    MY_VAR=12345 ./testenv.sh
    

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

By the way, harap dicatat bahwa, ketika Anda memanggil dieksekusi dengan nama seperti di atas (dan tidak dengan . atau source shell built-in), program shell apa yang digunakan untuk menjalankannya tidak biasanya ditentukan oleh shell apa yang Anda jalankan. Sebagai gantinya:

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

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

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

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

    #!/bin/sh
    

    /bin/sh dapat 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 lain yang serupa. Lihat ini Entri FOLDOCini Artikel Wikipedia dan Apakah #! / Bin / sh dibaca oleh interpreter? untuk informasi lebih lanjut.

  • Jika file teks ditandai dapat dieksekusi dan Anda menjalankannya dari shell Anda (seperti ./filename) tapi tidak mulai dengan #!, kernel gagal mengeksekusinya. Namun, melihat bahwa ini telah terjadi, shell Anda akan mencoba untuk menjalankannya dengan melewatkan namanya beberapa kulit. Ada beberapa persyaratan ditempatkan pada apa shell itu adalah ("shell akan mengeksekusi perintah yang setara dengan shell yang dipanggil ..."). Dalam praktek, beberapa kerang - termasuk bash*- menjalankan contoh lain dari diri mereka sendiri, sementara yang lain menggunakan /bin/sh. Saya sangat menyarankan Anda menghindari hal ini dan gunakan garis hashbang (jalankan skrip dengan meneruskannya ke interpreter yang diinginkan, misalnya, bash filename).

    *Manual GNU Bash, 3.7.2 Pencarian Perintah dan Eksekusi: "Jika eksekusi ini gagal karena file tidak dalam format eksekusi, dan file tersebut bukan direktori, itu dianggap sebagai skrip shell dan shell mengeksekusinya seperti yang dijelaskan di Skrip Shell. "


56
2017-08-30 01:52



Apa yang saya anggap berguna source adalah bahwa fungsi menjadi tersedia dari bash tanpa perlu memuat atau meluncurkan lagi. Contoh #!/bin/bash function olakease {echo olakease;}. Setelah Anda memuatnya source file.sh Anda dapat langsung menelepon olakease dari bash. Saya sangat suka itu. Sumber mengeksekusi kemudian memuat banyak hal, titik . hanya untuk eksekusi dan sesuatu seperti penggunaan bash file.sh - erm3nda
@ erm3nda . memiliki perilaku ini juga: . file.sh dan source file.sh lakukan hal yang persis sama, termasuk mempertahankan fungsi yang didefinisikan di file.sh. (Mungkin Anda memikirkannya ./file.sh, yang berbeda. Tapi itu benar tidak menggunakan . builtin; sebagai gantinya, . adalah bagian dari jalan.) - Eliah Kagan
Oh !, saya tidak membaca dengan seksama file. [Space]. Terima kasih, mach. - erm3nda


Ya, kamu kehilangan sesuatu.

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

Jadi coba ini:

MY_VAR=12345 
. ./testenv.sh

13
2017-08-30 01:43



Itu ./ Memberitahu dengan tepat di mana file itu, tanpa itu, bash akan melihat melalui PATH terlebih dahulu, lalu coba dir yang sekarang 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 untuk menemukan file jika dir saat ini tidak dalam PATH. - geirha
@geirha Ya, Anda benar, source (dan .) sebenarnya akan memeriksa $ PATH terlebih dahulu, meskipun sebenarnya tidak berlari naskah dalam arti biasa. Komentar saya (sebelumnya) tidak benar. - Eliah Kagan
Singkat dan to the point +1 - David Morales