Ketika saya menjalankan skrip ini, dimaksudkan untuk menjalankan sampai terbunuh ...
# foo.sh
while true; do sleep 1; done
... Saya tidak dapat menemukannya menggunakan ps ax
:
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3 S+ 0:00 grep --color=auto foo.sh
... tetapi jika saya hanya menambahkan #!
header " " umum ke skrip ...
#! /usr/bin/bash
# foo.sh
while true; do sleep 1; done
... maka skrip menjadi dapat ditemukan oleh ps
perintah yang sama ...
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43 S+ 0:00 /usr/bin/bash ./foo.sh
21324 pts/3 S+ 0:00 grep --color=auto foo.sh
Kenapa begitu?
Ini mungkin pertanyaan terkait: Saya pikir " #
" hanya awalan komentar, dan jika " #! /usr/bin/bash
" itu sendiri tidak lebih dari sebuah komentar. Tetapi apakah " #!
" membawa beberapa arti lebih besar daripada hanya sebagai komentar?
shell-script
shell
ps
shebang
StoneThrow
sumber
sumber
Jawaban:
Ketika shell interaktif saat ini
bash
, dan Anda menjalankan skrip tanpa-#!
line, makabash
akan menjalankan skrip. Proses akan muncul dips ax
output sebagai adilbash
.Di terminal lain:
Terkait:
Bagian yang relevan membentuk
bash
manual:Ini berarti bahwa menjalankan
./foo.sh
pada baris perintah, ketikafoo.sh
tidak memiliki#!
-line, sama dengan menjalankan perintah dalam file dalam subkulit, yaitu sebagaiDengan menunjuk tepat
#!
ke eg/bin/bash
, itu seperti melakukansumber
ps
menunjukkan skrip berjalan sebagai "/usr/bin/bash ./foo.sh
". Jadi, dalam kasus pertama, seperti yang Anda katakan, bash akan menjalankan skrip, tetapi bukankah skrip itu harus "diteruskan" ke bash bercabang yang dapat dieksekusi, seperti halnya pada kasus kedua? (dan jika demikian, saya membayangkan itu akan dapat ditemukan dengan pipa untuk menerima ...?)$$
masih menunjuk pada yang lama dalam kasus subkulit (echo $BASHPID
/bash -c 'echo $PPID'
).Ketika skrip shell dimulai
#!
, baris pertama itu adalah komentar sejauh menyangkut shell. Namun dua karakter pertama bermakna bagi bagian lain dari sistem: kernel. Dua karakter#!
ini disebut shebang . Untuk memahami peran shebang, Anda perlu memahami bagaimana program dijalankan.Menjalankan suatu program dari suatu file memerlukan tindakan dari kernel. Ini dilakukan sebagai bagian dari
execve
panggilan sistem. Kernel perlu memverifikasi izin file, membebaskan sumber daya (memori, dll.) Yang terkait dengan file yang dapat dieksekusi yang saat ini berjalan dalam proses panggilan, mengalokasikan sumber daya untuk file yang dapat dieksekusi yang baru, dan mentransfer kontrol ke program baru (dan lebih banyak hal yang Saya tidak akan menyebutkan). Theexecve
system call menggantikan kode dari proses yang sedang berjalan; ada panggilan sistem terpisahfork
untuk membuat proses baru.Untuk melakukan ini, kernel harus mendukung format file yang dapat dieksekusi. File ini harus berisi kode mesin, diatur sedemikian rupa sehingga kernel mengerti. Script shell tidak mengandung kode mesin, jadi tidak bisa dijalankan dengan cara ini.
Mekanisme shebang memungkinkan kernel untuk menunda tugas menafsirkan kode ke program lain. Ketika kernel melihat bahwa file yang dapat dieksekusi dimulai
#!
, ia membaca beberapa karakter berikutnya dan menginterpretasikan baris pertama dari file tersebut (minus ruang memimpin#!
dan opsional) sebagai jalur ke file lain (ditambah argumen, yang tidak akan saya bahas di sini ). Ketika kernel diberitahu untuk mengeksekusi file/my/script
, dan ia melihat bahwa file dimulai dengan baris#!/some/interpreter
, kernel mengeksekusi/some/interpreter
dengan argumen/my/script
. Maka terserah untuk/some/interpreter
memutuskan bahwa itu/my/script
adalah file skrip yang harus dijalankan.Bagaimana jika suatu file tidak mengandung kode asli dalam format yang dimengerti oleh kernel, dan tidak dimulai dengan shebang? Nah, maka file tidak dapat dieksekusi, dan
execve
panggilan sistem gagal dengan kode kesalahanENOEXEC
(Executable format error).Ini bisa menjadi akhir dari cerita, tetapi sebagian besar shell menerapkan fitur mundur. Jika kernel kembali
ENOEXEC
, shell melihat isi file dan memeriksa apakah itu terlihat seperti skrip shell. Jika shell mengira file tersebut terlihat seperti skrip shell, ia menjalankannya dengan sendirinya. Rincian cara melakukannya tergantung pada shell. Anda dapat melihat sebagian dari apa yang terjadi dengan menambahkanps $$
skrip Anda, dan lebih banyak lagi dengan menyaksikan proses dengan distrace -p1234 -f -eprocess
mana 1234 adalah PID dari shell.Dalam bash, mekanisme fallback ini diimplementasikan dengan memanggil
fork
tetapi tidakexecve
. Proses bash anak membersihkan keadaan internalnya sendiri dan membuka file skrip baru untuk menjalankannya. Oleh karena itu proses yang menjalankan skrip masih menggunakan gambar kode bash asli dan argumen baris perintah asli berlalu ketika Anda memanggil bash pada awalnya. ATT ksh berperilaku dengan cara yang sama.Dash, sebaliknya, bereaksi
ENOEXEC
dengan menelepon/bin/sh
dengan jalur ke skrip yang diteruskan sebagai argumen. Dengan kata lain, ketika Anda menjalankan skrip shebangless dari dash, berperilaku seolah-olah skrip tersebut memiliki garis shebang#!/bin/sh
. Mksh dan zsh berperilaku dengan cara yang sama.sumber
bash
bercabang, ia memiliki akses keargv[]
array yang sama dengan orang tuanya, yaitu bagaimana ia mengetahui "argumen baris perintah asli yang disahkan ketika Anda memanggil bash awalnya", dan jika jadi, inilah mengapa anak tidak lulus skrip asli sebagai argumen eksplisit (maka mengapa tidak dapat ditemukan oleh grep) - apakah itu akurat?BINFMT_SCRIPT
modul mengontrol ini dan dapat dihapus / dimodulasi, meskipun biasanya terhubung secara statis ke dalam kernel), tetapi saya tidak melihat mengapa Anda ingin melakukannya, kecuali mungkin dalam sistem tertanam . Sebagai solusi untuk kemungkinan ini,bash
memiliki flag konfigurasi (HAVE_HASH_BANG_EXEC
) untuk mengimbangi!ps
laporan apa sebagai argumen baris perintah, tetapi hanya sampai pada titik tertentu: ia harus memodifikasi buffer memori yang ada, tidak dapat memperbesar buffer ini. Jadi, jika bash mencoba memodifikasinyaargv
untuk menambahkan nama skrip, itu tidak akan selalu berhasil. Anak itu tidak “mengeluarkan argumen” karena tidak pernah adaexecve
panggilan sistem pada anak tersebut. Hanya gambar proses bash yang sama yang terus berjalan.Dalam kasus pertama, skrip dijalankan oleh anak bercabang dari shell Anda saat ini.
Pertama-tama Anda harus menjalankan
echo $$
dan kemudian melihat shell yang memiliki id proses shell Anda sebagai id proses induk.sumber