Mengapa "-" di "#! / bin / sh - ”shebang?

28
#! / bin / sh -

Apakah (atau paling tidak itu) sering merupakan shebang yang direkomendasikan untuk ditafsirkan oleh sebuah skrip /bin/sh.

Kenapa tidak adil #! /bin/shatau #!/bin/sh?

Untuk apa itu -?

Stéphane Chazelas
sumber

Jawaban:

38

Itu untuk alasan yang sama seperti mengapa Anda perlu menulis:

rm -- *.txt

Dan tidak

rm *.txt

Kecuali Anda dapat menjamin tidak ada .txtfile di direktori saat ini yang memiliki nama yang dimulai -.

Di:

rm <arg>

<arg>dianggap pilihan jika dimulai dengan -atau file untuk menghapus sebaliknya. Di

rm - <arg>

argselalu dianggap sebagai file yang akan dihapus terlepas dari apakah itu dimulai dengan -atau tidak.

Itu sama untuk sh.

Ketika seseorang menjalankan skrip yang dimulai dengan

#! /bin/sh

Biasanya dengan:

execve("path/to/the-script", ["the-script", "arg"], [environ])

Sistem mengubahnya menjadi:

execve("/bin/sh", ["/bin/sh", "path/to/the-script", "arg"], [environ])

The path/to/the-scriptbiasanya tidak sesuatu yang di bawah kendali penulis naskah. Penulis tidak dapat memperkirakan di mana salinan skrip akan disimpan atau dengan nama apa. Secara khusus, mereka tidak dapat menjamin bahwa path/to/the-scriptdengan yang dipanggil tidak akan dimulai -(atau +yang juga merupakan masalah dengan sh). Itu sebabnya kita perlu di -sini untuk menandai akhir opsi.

Misalnya, pada sistem saya zcat(seperti kebanyakan skrip lainnya sebenarnya) adalah salah satu contoh skrip yang tidak mengikuti rekomendasi itu:

$ head -n1 /bin/zcat
#!/bin/sh
$ mkdir +
$ ln -s /bin/zcat +/
$ +/zcat
/bin/sh: +/: invalid option
[...]

Sekarang Anda mungkin bertanya mengapa #! /bin/sh - dan tidak #! /bin/sh --?

Meskipun #! /bin/sh --akan bekerja dengan cangkang POSIX, yang #! /bin/sh -lebih portabel; khususnya untuk versi kuno sh. shmemperlakukan -sebagai dan akhir opsi mendahului getopt()dan penggunaan umum --untuk menandai akhir opsi pada waktu yang lama. Cara shell Bourne (dari akhir 70-an) mengurai argumennya, hanya argumen pertama yang dipertimbangkan untuk opsi jika dimulai dengan -. Semua karakter setelahnya -akan diperlakukan sebagai nama opsi; jika tidak ada karakter setelah- , tidak ada opsi. Kerang yang macet dan semua seperti Bourne kemudian mengenali -sebagai cara untuk menandai akhir opsi.

Dalam cangkang Bourne (tetapi tidak dalam cangkang mirip Bourne modern), #! /bin/sh -euf juga akan mengatasi masalah karena hanya argumen pertama yang dipertimbangkan untuk opsi.

Sekarang, orang bisa mengatakan bahwa kita menjadi jagoan di sini, dan itu juga mengapa saya menulis kebutuhan dalam huruf miring di atas:

  1. tidak ada yang waras mereka akan memanggil script dengan sesuatu dimulai dengan -atau +atau menempatkan mereka dalam sebuah direktori yang namanya dimulai dengan- atau +.
  2. bahkan jika mereka melakukannya, yang pertama akan berpendapat bahwa mereka hanya dapat menyalahkan diri mereka sendiri, tetapi juga, ketika Anda menjalankan skrip, lebih sering daripada tidak, itu dari shell atau dari execvp()/ execlp()-type fungsi. Dan dalam hal itu, secara umum Anda memanggil mereka baik the-scriptagar itu dicari dalam $PATHkasus mana argumen path ke execve()system call biasanya akan dimulai dengan /(tidak -atau tidak +) atau seolah- ./the-scriptolah Anda ingin the-scriptdirektori saat ini dijalankan (dan kemudian jalan dimulai dengan ./, bukan -juga bukan +).

Sekarang selain masalah kebenaran teoretis , ada alasan lain mengapa #! /bin/sh -menjadi direkomendasikan sebagai praktik yang baik . Dan itu kembali ke masa di mana beberapa sistem masih mendukung skrip setuid.

Jika Anda memiliki skrip yang berisi:

#! /bin/sh
/bin/echo "I'm running as root"

Dan skrip itu adalah setuid root (seperti dengan -r-sr-xr-x root binizin), pada sistem itu, ketika dijalankan oleh pengguna biasa, the

execve("/bin/sh", ["/bin/sh", "path/to/the-script"], [environ])

akan dilakukan root!

Jika pengguna membuat symlink /tmp/-i -> path/to/the-scriptdan menjalankannya sebagai -i, maka itu akan memulai shell interaktif ( /bin/sh -i) sebagai root.

The -akan bekerja di sekitar itu (itu tidak akan bekerja di sekitar masalah ras-kondisi , atau fakta bahwa beberapa shimplementasi seperti beberapa ksh88yang berbasis akan lookup argumen naskah tanpa/ di $PATHmeskipun).

Saat ini, hampir tidak ada sistem yang mendukung skrip setuid lebih lama, dan beberapa dari mereka yang masih melakukan (biasanya tidak secara default), akhirnya melakukan a execve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])(di mana <n>file deskriptor terbuka untuk membaca skrip) yang bekerja di sekitar masalah ini dan kondisi balapan.

Perhatikan bahwa Anda mendapatkan masalah serupa dengan sebagian besar penerjemah, tidak hanya cangkang mirip Bourne. Cangkang non-Bourne umumnya tidak mendukung -sebagai penanda opsi akhir, tetapi umumnya mendukung --sebagai gantinya (setidaknya untuk versi modern).

Juga

#! /usr/bin/awk -f
#! /usr/bin/sed -f

Tidak memiliki masalah sebagai argumen berikutnya dianggap sebagai argumen ke -fpilihan dalam hal apapun, tetapi masih tidak bekerja jika path/to/scriptadalah -(dalam hal ini, dengan sebagian besar sed/ awkimplementasi, sed/awk tidak membaca kode dari -file, tetapi dari stdin sebagai gantinya).

Perhatikan juga bahwa pada kebanyakan sistem, seseorang tidak dapat menggunakan:

#! /usr/bin/env sh -
#! /usr/bin/perl -w --

Seperti pada kebanyakan sistem, mekanisme shebang hanya memungkinkan satu argumen setelah jalur juru bahasa.

Seperti apakah akan menggunakan #! /bin/sh -vs #!/bin/sh -, itu hanya masalah selera. Saya lebih suka yang pertama karena membuat jalur juru bahasa lebih terlihat dan membuat pemilihan mouse lebih mudah. Ada legenda yang mengatakan bahwa ruang itu dibutuhkan dalam beberapa versi Unix kuno tetapi AFAIK, yang belum pernah diverifikasi.

Referensi yang sangat bagus tentang Unix shebang dapat ditemukan di https://www.in-ulm.de/~mascheck/various/shebang

Stéphane Chazelas
sumber
1
Apakah ini memiliki relevansi dengan #! / Bin / bash?
Joe
1
@ Jo, ya tentu saja, bashmenerima opsi seperti setiap shell lainnya. Menjadi shell Bourne-like dan POSIX, bashmenerima keduanya #! /bin/bash -dan #! /bin/bash --.
Stéphane Chazelas