Solusi 1:
Ada kira-kira empat tingkat portabilitas untuk skrip shell (sejauh menyangkut garis shebang):
-
Paling portabel:gunakan
#!/bin/sh
shebang dan gunakan hanya sintaks shell dasar yang ditentukan dalam standar POSIX. Ini seharusnya bekerja pada hampir semua sistem POSIX/unix/linux. (Yah, kecuali Solaris 10 dan sebelumnya yang memiliki warisan nyata Bourne shell, mendahului POSIX sehingga tidak sesuai, seperti/bin/sh
.) -
Paling portabel kedua:gunakan
#!/bin/bash
(atau#!/usr/bin/env bash
) baris shebang, dan tetap menggunakan fitur bash v3. Ini akan berfungsi pada sistem apa pun yang memiliki bash (di lokasi yang diharapkan). -
Paling portabel ketiga:gunakan
#!/bin/bash
(atau#!/usr/bin/env bash
) garis shebang, dan gunakan fitur bash v4. Ini akan gagal pada sistem apa pun yang memiliki bash v3 (mis. macOS, yang harus menggunakannya karena alasan lisensi). -
Paling tidak portabel:gunakan
#!/bin/sh
shebang dan gunakan ekstensi bash ke sintaks shell POSIX. Ini akan gagal pada sistem apa pun yang memiliki sesuatu selain bash untuk /bin/sh (seperti versi Ubuntu terbaru). Jangan pernah melakukan ini; itu bukan hanya masalah kompatibilitas, itu benar-benar salah. Sayangnya, ini merupakan kesalahan yang dilakukan banyak orang.
Rekomendasi saya:gunakan yang paling konservatif dari tiga yang pertama yang menyediakan semua fitur shell yang Anda perlukan untuk skrip. Untuk portabilitas maksimal, gunakan opsi #1, tetapi menurut pengalaman saya, beberapa fitur bash (seperti array) cukup membantu sehingga saya akan menggunakan #2.
Hal terburuk yang dapat Anda lakukan adalah #4, menggunakan shebang yang salah. Jika Anda tidak yakin fitur apa yang merupakan POSIX dasar dan mana yang merupakan ekstensi bash, tetap gunakan bash shebang (yaitu opsi #2), atau uji skrip secara menyeluruh dengan shell yang sangat mendasar (seperti tanda hubung di server Ubuntu LTS Anda). Wiki Ubuntu memiliki daftar bashisme yang harus diperhatikan.
Ada beberapa info yang sangat bagus tentang sejarah dan perbedaan antara shell dalam pertanyaan Unix &Linux "Apa artinya kompatibel dengan sh?" dan pertanyaan Stackoverflow "Perbedaan antara sh dan bash".
Perlu diketahui juga bahwa shell bukanlah satu-satunya hal yang berbeda di antara sistem yang berbeda; jika Anda terbiasa dengan linux, Anda terbiasa dengan perintah GNU, yang memiliki banyak ekstensi tidak standar yang mungkin tidak Anda temukan di sistem unix lain (mis. bsd, macOS). Sayangnya, tidak ada aturan sederhana di sini, Anda hanya perlu mengetahui kisaran variasi perintah yang Anda gunakan.
Salah satu perintah yang paling menjijikkan dalam hal portabilitas adalah salah satu yang paling dasar:echo
. Setiap kali Anda menggunakannya dengan opsi apa pun (mis. echo -n
atau echo -e
), atau dengan jalan keluar (garis miring terbalik) dalam string untuk dicetak, versi yang berbeda akan melakukan hal yang berbeda. Setiap kali Anda ingin mencetak string tanpa umpan baris setelahnya, atau dengan escape dalam string, gunakan printf
sebagai gantinya (dan pelajari cara kerjanya -- ini lebih rumit daripada echo
adalah). ps
perintah juga berantakan.
Hal umum lainnya yang harus diperhatikan adalah ekstensi baru-baru ini/GNUish ke sintaks opsi perintah:format perintah lama (standar) adalah bahwa perintah tersebut diikuti oleh opsi (dengan satu tanda hubung, dan setiap opsi adalah satu huruf), diikuti oleh argumen perintah. Varian terbaru (dan seringkali non-portabel) menyertakan opsi panjang (biasanya diperkenalkan dengan --
), mengizinkan opsi muncul setelah argumen, dan menggunakan --
untuk memisahkan opsi dari argumen.
Solusi 2:
Di ./configure
script yang mempersiapkan bahasa TXR untuk membangun, saya menulis prolog berikut untuk portabilitas yang lebih baik. Skrip akan mem-bootstrap dirinya sendiri meskipun #!/bin/sh
adalah Bourne Shell lama yang tidak sesuai dengan POSIX. (Saya membuat setiap rilis pada Solaris 10 VM).
#!/bin/sh
# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells
if test x$txr_shell = x ; then
for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
if test -x $shell ; then
txr_shell=$shell
break
fi
done
if test x$txr_shell = x ; then
echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
txr_shell=/bin/sh
fi
export txr_shell
exec $txr_shell $0 ${@+"[email protected]"}
fi
# rest of the script here, executing in upgraded shell
Idenya di sini adalah kita menemukan shell yang lebih baik daripada shell yang sedang kita jalankan, dan mengeksekusi ulang skrip menggunakan shell itu. txr_shell
variabel lingkungan disetel, sehingga skrip yang dieksekusi ulang mengetahui bahwa itu adalah instance rekursif yang dieksekusi ulang.
(Dalam skrip saya, txr_shell
variabel juga kemudian digunakan, tepat untuk dua tujuan:pertama dicetak sebagai bagian dari pesan informatif dalam keluaran skrip. Kedua, diinstal sebagai SHELL
variabel di Makefile
, sehingga make
akan menggunakan shell ini juga untuk menjalankan resep.)
Pada sistem di mana /bin/sh
adalah tanda hubung, Anda dapat melihat bahwa logika di atas akan menemukan /bin/bash
dan jalankan kembali skrip dengan itu.
Pada kotak Solaris 10, /usr/xpg4/bin/sh
akan menendang jika tidak ada Bash yang ditemukan.
Prolog ditulis dalam dialek shell konservatif, menggunakan test
untuk pengujian keberadaan file, dan ${@+"[email protected]"}
trik untuk memperluas argumen yang melayani beberapa cangkang lama yang rusak (yang hanya akan menjadi "[email protected]"
jika kita berada di shell yang sesuai dengan POSIX).
Solusi 3:
Semua variasi bahasa shell Bourne secara objektif mengerikan dibandingkan dengan bahasa skrip modern seperti Perl, Python, Ruby, node.js, dan bahkan (bisa dibilang) Tcl. Jika Anda harus melakukan sesuatu yang sedikit rumit, Anda akan lebih bahagia dalam jangka panjang jika menggunakan salah satu dari yang di atas, bukan skrip shell.
Satu-satunya keuntungan yang masih dimiliki bahasa shell dibandingkan bahasa yang lebih baru adalah sesuatu itu menyebut dirinya /bin/sh
dijamin ada pada apa pun yang mengaku sebagai Unix. Namun, sesuatu itu bahkan mungkin tidak sesuai dengan POSIX; banyak dari warisan Unix yang dibekukan membekukan bahasa yang diterapkan oleh /bin/sh
dan utilitas di PATH default sebelum untuk perubahan yang diminta oleh Unix95 (ya, Unix95, dua puluh tahun yang lalu dan terus bertambah). Mungkin ada satu set Unix95, atau bahkan POSIX.1-2001 jika Anda beruntung, alat di direktori tidak pada PATH default (mis. /usr/xpg4/bin
) tetapi mereka tidak dijamin ada.
Namun, dasar-dasar Perl lebih mungkin untuk hadir pada instalasi Unix yang dipilih secara sewenang-wenang daripada Bash. (Dengan "dasar-dasar Perl" maksud saya /usr/bin/perl
ada dan beberapa , mungkin cukup lama, versi Perl 5, dan jika Anda beruntung kumpulan modul yang disertakan dengan versi juru bahasa tersebut juga tersedia.)
Oleh karena itu:
Jika Anda menulis sesuatu yang harus berfungsi di mana saja yang dimaksudkan sebagai Unix (seperti skrip "konfigurasi"), Anda perlu menggunakan #! /bin/sh
, dan Anda tidak perlu menggunakan ekstensi apa pun. Saat ini saya akan menulis shell yang sesuai dengan POSIX.1-2001 dalam keadaan ini, tetapi saya akan siap untuk menambal POSIXisme jika seseorang meminta dukungan untuk besi berkarat.
Tetapi jika Anda tidak menulis sesuatu yang harus bekerja di mana-mana, maka saat Anda tergoda untuk menggunakan Bashisme apa pun, Anda harus berhenti dan menulis ulang semuanya dalam bahasa skrip yang lebih baik. Diri Anda di masa depan akan berterima kasih.
(Jadi kapan adalah apakah pantas menggunakan ekstensi Bash? Untuk urutan pertama:tidak pernah. Ke urutan kedua:hanya untuk memperluas lingkungan interaktif Bash — mis. untuk memberikan penyelesaian tab yang cerdas dan permintaan yang menarik.)