Menulis skrip shell yang akan dijalankan pada shell apa saja (menggunakan beberapa baris shebang?)

19

Saya baru saja mulai masuk lebih dalam ke skrip shell, dan saya selalu baru saja melempar skrip saya ke dalam file, menandainya chmod +xdan kemudian selesai /path/to/script.shdan membiarkan penerjemah apa pun yang ada di dalamnya menggunakan cara default, yang saya asumsikan sebagai zsh karena itulah yang Saya menggunakan shell saya. Tampaknya itu hanya /bin/shsecara default, bahkan jika saya menjalankan skrip dari prompt zsh, karena saya mulai memasukkan hal-hal khusus-zsh di skrip saya dan gagal kecuali saya menjalankannya zsh /path/to/script.sh.

Untuk sampai ke titik, berikut adalah pertanyaan saya:

  1. Shell mana yang menjalankan skrip ketika tidak ada baris shebang ( #!/path/to/shell) di awal? Saya berasumsi /bin/shtetapi saya tidak bisa mengkonfirmasi.
  2. Apa yang dianggap "praktik terbaik" dalam hal menulis skrip shell yang akan berjalan pada platform apa pun? (ok, ini semacam open-ended)
  3. Apakah mungkin untuk menulis skrip yang mencoba menggunakan zsh dan kembali ke bash jika zsh tidak tersedia? Saya sudah mencoba meletakkan dua garis shebang, seperti di bawah ini, tetapi hanya kesalahan bad interpreter: /bin/zsh: no such file or directoryjika saya mencobanya pada mesin tanpa zsh.

    #!/bin/zsh

    #!/bin/bash

swrobel
sumber

Jawaban:

26

Shell mana yang menjalankan skrip ketika tidak ada baris shebang (#! / Path / ke / shell) di awal? Saya berasumsi / bin / sh tapi saya tidak bisa mengkonfirmasi.

Kernel menolak untuk mengeksekusi skrip dan kembali seperti ENOEXEC, sehingga perilaku yang tepat tergantung pada program Anda menjalankan script tersebut dari .

  • bash 4.2.39 - menggunakan dirinya sendiri
  • busybox-ash 1.20.2 - menggunakan dirinya sendiri
  • dash 0.5.7 - menjalankan / bin / sh
  • fish 1.23.1 - mengeluh tentang ENOEXEC, lalu menyalahkan file yang salah
  • AT&T ksh 93u + 2012.08.01 - menggunakan dirinya sendiri
  • mksh R40f - menjalankan / bin / sh
  • pdksh 5.2.14 - menjalankan / bin / sh
  • sh-heirloom 050706 - menggunakan sendiri
  • tcsh 6.18.01 - menjalankan / bin / sh
  • zsh 5.0.0 - menjalankan / bin / sh
  • cmd.exe 5.1.2600 - melihat Anda lucu.

Dalam glibc , berfungsi execv()atau execve()kembali saja ENOEXEC. Tetapi execvp()menyembunyikan kode kesalahan ini dan secara otomatis memanggil / bin / sh. (Ini didokumentasikan dalam exec (3p) .)

Apa yang dianggap "praktik terbaik" dalam hal menulis skrip shell yang akan berjalan pada platform apa pun? (ok, ini semacam open-ended)

Baik tetap berpegang pada shdan hanya fitur-fitur yang didefinisikan POSIX, atau cukup gunakan bash penuh (yang tersedia secara luas) dan sebutkan dalam persyaratan Anda jika mendistribusikannya.

(Sekarang saya memikirkannya, Perl - atau mungkin Python - akan menjadi lebih portabel, belum lagi memiliki sintaks yang lebih baik.)

Selalu tambahkan baris shebang. Jika menggunakan bash atau zsh, gunakan #!/usr/bin/env bashsebagai ganti hardcoding lintasan shell. (Namun, shell POSIX dijamin berada di /bin/sh, jadi lewati envkasus itu.)

(Sayangnya, bahkan /bin/shtidak selalu sama. Program autoconf GNU harus berurusan dengan banyak quirks yang berbeda .)

Apakah mungkin untuk menulis skrip yang mencoba menggunakan zsh dan kembali ke bash jika zsh tidak tersedia? Saya sudah mencoba meletakkan dua baris shebang, seperti di bawah ini, tetapi hanya kesalahan dengan juru bahasa yang buruk: / bin / zsh: tidak ada file atau direktori seperti itu jika saya mencobanya pada mesin tanpa zsh.

Hanya ada satu garis shebang; semuanya setelah karakter baris baru bahkan tidak dibaca oleh kernel, dan diperlakukan sebagai komentar oleh shell.

Ini mungkin untuk menulis naskah yang berjalan sebagai #!/bin/sh, cek yang shell tersedia, dan berjalan exec zsh "$0" "$@"atau exec bash "$0" "$@"tergantung pada hasil. Namun, sintaks yang digunakan oleh bash dan zsh sangat berbeda di berbagai tempat sehingga saya tidak akan merekomendasikan melakukan ini untuk kewarasan Anda sendiri.

grawity
sumber
1
bagaimana Anda melacak apa yang sebenarnya disebut oleh setiap shell ketika menerima ENOEXEC?
swrobel
2
@ Swrobel: Menggunakan strace -f -e fork,clone,execve. Beberapa shell pergi ke eksekutif /bin/shsetelah kegagalan; yang lain menafsirkan skrip itu sendiri.
grawity
1
@ Swrobel: Metode lain adalah menjalankan skrip yang terdiri dari readlink /proc/$$/exe.
grawity
2
Lihat juga jawaban Stéphane Chazelas untuk Interpreter shell mana yang menjalankan skrip tanpa shebang? (duplikat lintas-situs dari sub-pertanyaan # 1)
G-Man Mengatakan 'Reinstate Monica'
1
Untuk cshdan tcsh, perilaku tergantung pada apakah skrip dimulai dengan #(dalam hal ini mereka memanggil diri mereka sendiri daripada harus menafsirkan skrip). Itu kembali ke masa ketika csh mendukung komentar tetapi bukan shell Bourne, jadi #itu petunjuk bahwa itu adalah skrip csh.
sch
2

1) Shell saat ini tempat Anda menjalankan. (Shell apa pun itu)

2) Tetap dengan jenis shell yang sama (bash / dash / ash / csh / apa pun selera Anda) dan pastikan "platform yang didukung" Anda menginstal shell yang ingin Anda gunakan secara default. Juga, cobalah untuk menggunakan perintah yang tersedia secara umum pada sistem. Hindari opsi spesifik mesin.

3) Sebenarnya tidak ada logika "jika-maka-lain" untuk interpreter directive. Anda harus menentukan shell yang harus ada pada semua sistem yang ingin Anda dukung ... yaitu #!/bin/bashatau menentukan generik #!/bin/shselama skrip Anda cukup generik di semua shell.

TheCompWiz
sumber