Satu-satunya perbedaan utama adalah antara sumber dan pelaksanaan skrip. source foo.sh
akan sumber itu dan semua contoh lain yang Anda tunjukkan mengeksekusi. Lebih detail:
./file.sh
Ini akan menjalankan skrip bernama file.sh
yang ada di direktori saat ini ( ./
). Biasanya, ketika Anda menjalankan command
, shell akan melihat direktori di dalam Anda $PATH
untuk file executable yang disebut command
. Jika Anda memberikan path lengkap, seperti /usr/bin/command
atau ./command
, maka $PATH
diabaikan dan file tertentu dijalankan.
../file.sh
Ini pada dasarnya sama dengan ./file.sh
kecuali bahwa alih-alih mencari di direktori saat ini untuk file.sh
, itu mencari di direktori induk ( ../
).
sh file.sh
Ini setara dengan sh ./file.sh
, seperti di atas akan menjalankan skrip yang disebut file.sh
dalam direktori saat ini. Perbedaannya adalah Anda menjalankannya secara eksplisit dengan sh
shell. Pada sistem Ubuntu, itu dash
dan tidak bash
. Biasanya, skrip memiliki garis shebang yang memberikan program yang seharusnya dijalankan. Memanggil mereka dengan yang berbeda akan menimpanya. Sebagai contoh:
$ cat foo.sh
#!/bin/bash
## The above is the shebang line, it points to bash
ps h -p $$ -o args='' | cut -f1 -d' ' ## This will print the name of the shell
Script itu hanya akan mencetak nama shell yang digunakan untuk menjalankannya. Mari kita lihat apa yang dikembalikan ketika dipanggil dengan cara yang berbeda:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
Jadi, panggilan memanggil skrip dengan shell script
akan menimpa garis shebang (jika ada) dan menjalankan skrip dengan shell apa pun yang Anda katakan.
source file.sh
atau . file.sh
Ini disebut, cukup mengejutkan, sumber skrip. Kata kunci source
adalah alias untuk .
perintah builtin shell . Ini adalah cara menjalankan skrip di dalam shell saat ini. Biasanya, ketika sebuah skrip dieksekusi, ia dijalankan di dalam cangkangnya sendiri yang berbeda dari yang sekarang. Menggambarkan:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
Sekarang, jika saya mengatur variabel foo
ke sesuatu yang lain di shell induk dan kemudian menjalankan skrip, skrip akan mencetak nilai yang berbeda dari foo
(karena itu juga diatur dalam skrip) tetapi nilai foo
di dalam shell induk tidak akan berubah:
$ foo="Parent"
$ bash foo.sh
Foo (script) is Script ## This is the value from the script's shell
$ echo "$foo"
Parent ## The value in the parent shell is unchanged
Namun, jika saya sumber skrip alih-alih menjalankannya, skrip akan dijalankan di shell yang sama sehingga nilai foo
di dalam induk akan diubah:
$ source ./foo.sh
Foo (script) is Script ## The script's foo
$ echo "$foo"
Script ## Because the script was sourced,
## the value in the parent shell has changed
Jadi, sumber digunakan dalam beberapa kasus di mana Anda ingin skrip memengaruhi shell tempat Anda menjalankannya. Ini biasanya digunakan untuk mendefinisikan variabel shell dan membuatnya tersedia setelah skrip selesai.
Dengan semua itu dalam pikiran, alasan Anda mendapatkan jawaban yang berbeda adalah, pertama-tama, bahwa skrip Anda tidak melakukan apa yang Anda pikirkan. Ini menghitung berapa kali yang bash
muncul di output ps
. Ini bukan jumlah terminal terbuka , ini adalah jumlah cangkang yang berjalan (pada kenyataannya, bahkan bukan itu, tapi itu diskusi lain). Untuk memperjelas, saya sedikit menyederhanakan skrip Anda untuk ini:
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
Dan jalankan dengan berbagai cara hanya dengan satu terminal terbuka:
Peluncuran langsung ./foo.sh
,.
$ ./foo.sh
The number of shells opened by terdon is 1
Di sini, Anda menggunakan garis shebang. Ini berarti bahwa skrip dijalankan secara langsung oleh apa pun yang diatur di sana. Ini mempengaruhi cara skrip ditampilkan dalam output dari ps
. Alih-alih terdaftar sebagai bash foo.sh
, itu hanya akan ditampilkan sebagai foo.sh
yang berarti bahwa Anda grep
akan melewatkannya. Sebenarnya ada 3 instance bash yang berjalan: proses induk, bash menjalankan skrip dan yang lainnya menjalankan ps
perintah . Yang terakhir ini penting, meluncurkan perintah dengan substitusi perintah ( `command`
atau $(command)
) menghasilkan salinan shell induk yang diluncurkan dan menjalankan perintah. Di sini, bagaimanapun, tidak ada yang ditampilkan karena cara yang ps
menunjukkan hasilnya.
Peluncuran langsung dengan shell eksplisit (bash)
$ bash foo.sh
The number of shells opened by terdon is 3
Di sini, karena Anda menjalankan bash foo.sh
, output dari ps
akan ditampilkan bash foo.sh
dan dihitung. Jadi, di sini kita memiliki proses induk, bash
skrip yang berjalan, dan shell kloning (running the ps
) semua ditampilkan karena sekarang ps
akan menampilkan masing-masing karena perintah Anda akan memasukkan kata bash
.
Peluncuran langsung dengan shell yang berbeda ( sh
)
$ sh foo.sh
The number of shells opened by terdon is 1
Ini berbeda karena Anda menjalankan skrip dengan sh
dan tidak bash
. Karena itu, satu-satunya bash
contoh adalah shell induk tempat Anda meluncurkan skrip Anda. Semua shell lain yang disebutkan di atas dijalankan oleh sh
sebagai gantinya.
Sumber (baik dengan .
atau source
, hal yang sama)
$ . ./foo.sh
The number of shells opened by terdon is 2
Seperti yang saya jelaskan di atas, sumber skrip membuatnya berjalan di shell yang sama dengan proses induk. Namun, subkulit terpisah mulai meluncurkan ps
perintah dan itu menjadikan total menjadi dua.
Sebagai catatan terakhir, cara yang benar untuk menghitung proses yang sedang berjalan bukan untuk menguraikan ps
tetapi untuk digunakan pgrep
. Semua masalah ini akan dihindari seandainya Anda baru saja berlari
pgrep -cu terdon bash
Jadi, versi skrip Anda yang selalu mencetak angka yang tepat adalah (perhatikan tidak adanya substitusi perintah):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Itu akan mengembalikan 1 ketika bersumber dan 2 (karena bash baru akan diluncurkan untuk menjalankan skrip) untuk semua cara lain peluncuran. Itu masih akan mengembalikan 1 ketika diluncurkan dengan sh
karena proses anak tidak bash
.