Shell interpreter mana yang menjalankan skrip tanpa shebang?

16

Misalkan shell default untuk akun saya adalah zsh tetapi saya membuka terminal dan menjalankan bash dan mengeksekusi skrip bernama prac002.sh, interpreter shell mana yang akan digunakan untuk mengeksekusi skrip, zsh atau bash? Perhatikan contoh berikut:

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf: 
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
Rex
# Which interpreter did it just use?

** EDIT: ** Inilah isi naskahnya

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname
7_R3X
sumber
Apa yang dikatakan skrip di atas?
Michael Homer
@MichaelHomer: Tidak ada. Saya baru saja mengedit pertanyaan untuk menambahkan konten skrip. Skrip memiliki izin yang dapat dieksekusi.
7_R3X
Jika Anda ingin menggunakan shell saat ini, dot sumbernya, seperti pada . prac002.sh, dengan asumsi skrip Anda ada di dir saat ini.
thecarpy
1
Jalankan . ./prac002.shdan itu akan berjalan dengan shell saat ini, yaitu, dot ( .), space () diikuti oleh path skrip Anda. Ini disebut dot-sourcing skrip Anda. ;-)
thecarpy

Jawaban:

18

Karena skrip tidak dimulai dengan #!garis shebang yang menunjukkan penerjemah mana yang akan digunakan, POSIX mengatakan bahwa :

Jika execl()fungsi gagal karena kesalahan yang setara dengan kesalahan [ENOEXEC] yang didefinisikan dalam volume Antarmuka Sistem POSIX.1-2008, shell harus menjalankan perintah yang setara dengan membuat shell dipanggil dengan pathname yang dihasilkan dari pencarian sebagai yang pertama operan , dengan argumen yang tersisa diteruskan ke shell baru, kecuali bahwa nilai "$ 0" di shell baru dapat diatur ke nama perintah. Jika file yang dapat dieksekusi bukan file teks, shell dapat memotong eksekusi perintah ini. Dalam hal ini, ia akan menulis pesan kesalahan, dan akan mengembalikan status keluar dari 126.

Ungkapan itu sedikit ambigu, dan cangkang yang berbeda memiliki interpretasi yang berbeda.

Dalam hal ini, Bash akan menjalankan skrip menggunakan dirinya sendiri . Di sisi lain, jika Anda menjalankannya dari zsh, zsh akan menggunakansh (apa pun yang ada di sistem Anda) sebagai gantinya.

Anda dapat memverifikasi perilaku itu untuk kasus ini dengan menambahkan baris ini ke skrip:

echo $BASH_VERSION
echo $ZSH_VERSION

Anda akan mencatat bahwa, dari Bash, baris pertama menampilkan versi Anda, sedangkan yang kedua tidak pernah mengatakan apa-apa, tidak peduli shell mana yang Anda gunakan.

  • Jika Anda /bin/sh, katakanlah, dashmaka baris tidak akan menghasilkan apa pun ketika skrip dieksekusi dari zsh atau dash.
  • Jika Anda /bin/shadalah tautan ke Bash, Anda akan melihat output baris pertama dalam semua kasus.
  • Jika /bin/shadalah versi yang berbeda dari Bash dari yang Anda gunakan secara langsung, Anda akan melihat output yang berbeda ketika Anda menjalankan script dari bash langsung dan dari zsh.

The ps -p $$perintah dari jawaban rools ini juga akan menampilkan informasi yang berguna tentang perintah shell yang digunakan untuk menjalankan script.

Michael Homer
sumber
Namun saya memutarnya menggunakan DEFAULT SHELL jika tidak ada shebang yang ditentukan, apapun shell default yang telah Anda tentukan. Inilah sebabnya mengapa Anda harus selalu, imho, tentukan shebang.
thecarpy
4
Tidak, yang benar-benar inti dari jawabannya. Kalimat kedua Anda tentu benar.
Michael Homer
Anda benar, bash menjalankannya menggunakan dirinya sendiri, shell default pada sebagian besar kotak saya kebetulan .... bash, dari sana kebingungan saya. Saya baru saja menguji pada Solaris 8 dengan ksh sebagai shell default, tentu saja, bash menjalankannya sebagai bash ... belajar sesuatu yang baru hari ini, terima kasih (tervotifikasi!) ;-)
thecarpy
Terima kasih. Apakah kegagalan execl()panggilan terjadi ketika skrip shell tidak berisi shebang dan dieksekusi sebagai scriptname? Apakah itu tidak terjadi ketika skrip shell dieksekusi bash scriptname? Apakah itu tidak terjadi ketika skrip shell berisi shebang dan dieksekusi sebagai scriptname?
StackExchange for All
Saya membuat posting di unix.stackexchange.com/questions/439376/...
StackExchange for All
7

Karena file tersebut bukan tipe yang dapat dieksekusi yang dikenali oleh sistem, dan dengan asumsi Anda memiliki izin untuk mengeksekusi file itu, execve()panggilan sistem biasanya akan gagal dengan kesalahan ENOEXEC( bukan yang dapat dieksekusi ).

Apa yang terjadi kemudian tergantung pada fungsi aplikasi dan / atau perpustakaan yang digunakan untuk menjalankan perintah.

Itu bisa berupa shell, fungsi execlp()/ execvp()libc.

Sebagian besar aplikasi lain akan menggunakan salah satu dari mereka ketika mereka menjalankan perintah. Mereka akan memanggil shell misalnya dengan menggunakan system("command line")fungsi libc yang biasanya akan memanggil shuntuk mem-parsing baris perintah itu (jalur yang dapat ditentukan pada waktu kompilasi (seperti /bin/shvs /usr/xpg4/bin/shpada Solaris)), atau memanggil shell yang disimpan dalam $SHELLdirinya sendiri seperti videngan !perintahnya, atau xterm -e 'command line'dan banyak perintah lainnya ( su user -cakan meminta shell login pengguna alih-alih $SHELL).

Secara umum, file teks tanpa-shebang yang tidak dimulai #dianggap sebagai shskrip. Yang shitu akan bervariasi.

execlp()/ execvp(), setelah execve()kembali ENOEXECbiasanya akan memintanya sh. Untuk sistem yang memiliki lebih dari satu shkarena mereka dapat memenuhi lebih dari satu standar, yang shbiasanya akan ditentukan pada waktu kompilasi (dari aplikasi menggunakan execvp()/ execlp()dengan menghubungkan gumpalan kode yang berbeda yang merujuk ke jalur yang berbeda dengan sh). Sebagai contoh, pada Solaris, itu akan menjadi /usr/xpg4/bin/sh(standar, POSIX sh) atau /bin/sh(cangkang Bourne (cangkang kuno) pada Solaris 10 dan lebih lama, ksh93 dalam Solaris 11).

Ketika datang ke kerang, ada banyak variasi. bash, AT&T ksh, shell Bourne biasanya akan menafsirkan skrip itu sendiri (dalam proses anak kecuali execdigunakan) setelah mensimulasikan a execve(), yang tidak mengatur semua variabel yang tidak diekspor, menutup semua fds close-on-exec, menghapus semua jebakan kustom, alias, fungsi ... ( bashakan menafsirkan skrip dalam shmode). yashakan mengeksekusi sendiri (dengan mode shseperti argv[0]dalam sh) untuk menafsirkannya.

zsh, pdksh, ashKerang berbasis biasanya akan memanggil sh(jalur yang ditentukan pada saat kompilasi).

Untuk cshdan tcsh(dan shbeberapa BSD awal), jika karakter pertama file tersebut #, maka mereka akan mengeksekusi sendiri untuk menafsirkannya, dan shsebaliknya. Yang kembali ke waktu pre-peristiwa di mana cshtidak mengakui #sebagai komentar tapi tidak Bourne shell, sehingga #merupakan petunjuk bahwa ia adalah script csh.

fish(setidaknya versi 2.4.0), baru saja mengembalikan kesalahan jika execve()gagal (tidak mencoba memperlakukannya sebagai skrip).

Beberapa shell (suka bashatau AT&T ksh) pertama-tama akan mencoba untuk menentukan apakah file tersebut dimaksudkan sebagai skrip atau tidak. Jadi Anda mungkin menemukan bahwa beberapa shell akan menolak untuk mengeksekusi skrip jika ada karakter NUL di beberapa byte pertama.

Juga perhatikan bahwa jika execve()gagal dengan ENOEXEC tetapi file tersebut memang memiliki garis shebang, beberapa shell mencoba menafsirkan garis shebang itu sendiri.

Jadi beberapa contoh:

  • Ketika $SHELLadalah /bin/bash, xterm -e 'myscript with args'akan myscriptditafsirkan oleh bashdi shmodus. Sementara dengan xterm -e myscript with args, xtermakan digunakan execvp()sehingga skrip akan ditafsirkan oleh sh.
  • su -c myscriptpada Solaris 10 di mana rootshell login adalah /bin/shdan /bin/shshell Bourne akan myscriptditafsirkan oleh shell Bourne.
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'pada Solaris 10 akan ditafsirkan oleh /usr/xpg4/bin/sh(sama untuk /usr/xpg4/bin/env myscript).
  • find . -prune -exec myscript {} \;pada Solaris 10 (menggunakan execvp()) akan menafsirkannya /bin/shbahkan dengan /usr/xpg4/bin/find, bahkan dalam lingkungan POSIX (bug kepatuhan).
  • csh -c myscriptakan menafsirkannya cshjika dimulai dengan #, dengan shsebaliknya.

Secara keseluruhan, Anda tidak dapat memastikan shell apa yang akan digunakan untuk menafsirkan skrip itu jika Anda tidak tahu bagaimana dan oleh apa itu akan dipanggil.

Dalam kasus apa pun, read -pini adalah bashhanya sintaks, jadi Anda harus memastikan bahwa skrip diinterpretasikan oleh bash(dan menghindari .shekstensi yang menyesatkan ). Entah Anda tahu jalur yang bashdapat dieksekusi dan gunakan:

#! /path/to/bash -
read -p ...

Atau Anda dapat mencoba dan mengandalkan $PATHpencarian yang bashdapat dieksekusi (dengan asumsi bashdiinstal) dengan menggunakan:

#! /usr/bin/env bash
read -p ...

( envHampir di mana-mana ditemukan di /usr/bin). Atau, Anda dapat membuatnya POSIX + Bourne kompatibel dalam hal ini Anda dapat menggunakan /bin/sh. Semua sistem akan memiliki /bin/sh. Pada sebagian besar dari mereka itu akan (sebagian besar) kompatibel dengan POSIX, tetapi Anda mungkin masih menemukan shell Bourne di sana.

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"
Stéphane Chazelas
sumber
1

Ketika Anda tidak memiliki garis #!(disebut shebang ), sh digunakan. Untuk memeriksanya, Anda dapat menjalankan skrip berikut.

ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe

Di komputer saya saya dapatkan

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real program is: /usr/bin/bash

bahkan jika shell default saya adalah zsh . Ini menggunakan bash karena pada mesin saya, perintah sh diimplementasikan oleh bash .

rools
sumber
1
Mengapa Anda membatalkan itu? Tolong jelaskan atau tidak ada gunanya ...
rools
Pembenci (silent anonymous downvoters) akan membenci. Saya belajar sesuatu dari jawaban Anda, jadi inilah jawaban positifnya. :-)
Mac