#! / bin / sh vs #! / bin / bash untuk portabilitas maksimum

cherouvim 07/30/2017. 3 answers, 2.886 views
linux bash shell sh

Saya biasanya bekerja dengan server Ubuntu LTS yang dari apa yang saya mengerti symlink /bin/sh ke /bin/dash . Banyak distro lain meskipun symlink /bin/sh ke /bin/bash .

Dari itu saya mengerti bahwa jika skrip menggunakan #!/bin/sh di atasnya mungkin tidak berjalan dengan cara yang sama di semua server?

Apakah ada praktik yang disarankan di mana shell digunakan untuk skrip ketika seseorang menginginkan portabilitas maksimum dari skrip-skrip antara server?

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
Ada sedikit perbedaan antara berbagai cangkang. Jika portabilitas adalah yang paling penting bagi Anda maka gunakan #!/bin/sh dan jangan gunakan apa pun selain dari yang disediakan shell asli.

3 Answers


Gordon Davisson 08/01/2017.

Ada sekitar empat tingkat portabilitas untuk skrip shell (sejauh menyangkut garis shebang):

  1. Paling portabel: gunakan shebang #!/bin/sh dan gunakan only shell sintaks dasar yang ditentukan dalam standar POSIX . Ini harus bekerja pada hampir semua sistem POSIX / unix / linux. (Nah, kecuali Solaris 10 dan sebelumnya yang memiliki Bourne shell asli, mendahului POSIX sehingga tidak sesuai, sebagai /bin/sh .)

  2. Paling portabel kedua: gunakan baris shebang #!/bin/bash (atau #!/usr/bin/env bash ), dan tetap berpegang pada bash v3. Ini akan bekerja pada sistem yang memiliki bash (di lokasi yang diharapkan).

  3. Paling portabel ketiga: gunakan baris shebang #!/bin/bash (atau #!/usr/bin/env bash ), dan gunakan fitur bash v4. Ini akan gagal pada sistem apa pun yang memiliki bash v3 (mis. MacOS, yang harus menggunakannya untuk alasan perizinan).

  4. Paling tidak portabel: gunakan #!/bin/sh shebang dan gunakan bash extension 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 hanya salah. Sayangnya, ini kesalahan yang dilakukan banyak orang.

Rekomendasi saya: gunakan yang paling konservatif dari tiga yang pertama yang menyediakan semua fitur shell yang Anda butuhkan untuk skrip. Untuk portabilitas maksimal, gunakan opsi # 1, tetapi dalam 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 apa fitur dasar POSIX dan yang merupakan ekstensi bash, baik tetap dengan bash shebang (yaitu opsi # 2), atau uji skrip secara menyeluruh dengan shell yang sangat dasar (seperti dash pada server Ubuntu LTS Anda). Wiki Ubuntu memiliki daftar bashisms yang bagus untuk diwaspadai .

Ada beberapa info yang sangat bagus tentang sejarah dan perbedaan antara shell dalam pertanyaan Unix & Linux "Apa artinya menjadi kompatibel?" dan pertanyaan Stackoverflow "Perbedaan antara sh dan bash" .

Juga, sadarilah bahwa shell bukanlah satu-satunya hal yang berbeda 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 pada sistem unix lain (mis. bsd, macOS). Sayangnya, tidak ada aturan sederhana di sini, Anda hanya perlu mengetahui kisaran variasi untuk perintah yang Anda gunakan.

Salah satu perintah paling menjijikkan dalam hal portabilitas adalah salah satu yang paling mendasar: echo . Setiap kali Anda menggunakannya dengan opsi apa pun (misalnya echo -n atau echo -e ), atau dengan pelarian (backslashes) apa pun dalam string untuk dicetak, versi yang berbeda akan melakukan hal yang berbeda. Kapan saja Anda ingin mencetak string tanpa linefeed setelahnya, atau dengan escapes dalam string, gunakan printf (dan pelajari cara kerjanya - lebih rumit daripada echo ). Perintah ps juga berantakan .

Hal umum lain yang perlu diperhatikan adalah ekstensi terbaru / GNUish untuk sintaks opsi perintah: format perintah lama (standar) adalah bahwa perintah diikuti oleh opsi (dengan satu tanda pisah, dan setiap opsi adalah satu huruf), diikuti oleh argumen perintah. Varian terbaru (dan seringkali non-portabel) termasuk opsi panjang (biasanya diperkenalkan dengan -- ), memungkinkan opsi untuk muncul setelah argumen, dan menggunakan -- untuk memisahkan opsi dari argumen.

5 comments
12 pabouk 07/30/2017
Pilihan keempat hanyalah ide yang salah. Tolong jangan gunakan itu.
3 Gordon Davisson 07/30/2017
@pabouk Saya setuju sepenuhnya, jadi saya mengedit jawaban saya untuk membuat ini lebih jelas.
1 jlliagre 07/30/2017
Pernyataan pertama Anda sedikit menyesatkan. Standar POSIX tidak menentukan apa-apa tentang shebang di luar yang mengatakan menggunakannya mengarah pada perilaku yang tidak ditentukan. Selain itu, POSIX tidak menentukan di mana shell POSIX harus ditempatkan, hanya namanya ( sh ), jadi /bin/sh tidak dijamin sebagai jalur yang benar. Yang paling portabel kemudian tidak untuk menentukan shebang sama sekali, atau untuk mengadaptasi shebang ke OS yang digunakan.
2 Michael Kjörling 07/30/2017
Saya jatuh ke # 4 dengan naskah saya baru-baru ini, dan tidak tahu mengapa itu tidak berhasil; setelah semua, perintah yang sama bekerja dengan baik, dan melakukan apa yang saya ingin mereka lakukan, ketika saya mencoba mereka langsung di shell. Segera setelah saya mengubah #!/bin/sh ke #!/bin/bash , skrip bekerja dengan sempurna. (Untuk pembelaan saya, skrip itu telah berevolusi dari waktu ke waktu yang benar-benar hanya membutuhkan sh-isme, ke salah satu yang bergantung pada perilaku bash-like.)
2 jlliagre 07/30/2017
@ MichaelKjörling Shell Bourne (benar) hampir tidak pernah dibundel dengan distribusi Linux dan tidak kompatibel dengan POSIX. Cangkang standar POSIX dibuat dari perilaku ksh , bukan Bourne. Distribusi Linux apa yang paling mengikuti FHS adalah memiliki /bin/sh sebagai tautan simbolis ke shell yang mereka pilih untuk menyediakan kompatibilitas POSIX, biasanya bash atau dash .

Kaz 07/31/2017.

Di skrip ./configure yang mempersiapkan bahasa TXR untuk dibuat, saya menulis prolog berikut untuk portabilitas yang lebih baik. Skrip akan bootstrap sendiri bahkan jika #!/bin/sh adalah Bourne Shell non-POSIX-conforming. (Saya membangun 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 ${@+"$@"}
fi

# rest of the script here, executing in upgraded shell 

Idenya di sini adalah bahwa kita menemukan shell yang lebih baik daripada yang kita jalankan di bawah, dan menjalankan kembali skrip menggunakan shell itu. txr_shell lingkungan txr_shell disetel, sehingga skrip yang dieksekusi ulang mengetahuinya adalah contoh rekursif yang dijalankan ulang.

(Dalam skrip saya, variabel txr_shell juga kemudian digunakan, untuk dua tujuan: pertama dicetak sebagai bagian dari pesan informatif dalam output skrip. Kedua, itu diinstal sebagai variabel SHELL di Makefile , sehingga make akan menggunakan shell ini untuk mengeksekusi resep.)

Pada sistem di mana /bin/sh adalah dash, Anda dapat melihat bahwa logika di atas akan menemukan /bin/bash dan menjalankan kembali skrip dengan itu.

Pada kotak Solaris 10, /usr/xpg4/bin/sh akan menendang jika tidak ditemukan Bash.

Prolog ditulis dalam dialek shell konservatif, menggunakan test untuk tes keberadaan file, dan trik ${@+"$@"} untuk memperluas argumen yang melayani beberapa shell lama yang rusak (yang hanya akan menjadi "$@" jika kita dalam POSIX menyesuaikan shell).

2 comments
Charles Duffy 07/31/2017
Seseorang tidak akan membutuhkan x hackery jika kutipan yang tepat digunakan, karena situasi yang mengamanatkan idiom itu mengepung invokasi tes yang sekarang ditinggalkan dengan -a atau -o menggabungkan beberapa tes.
Kaz 07/31/2017
@CharlesDuffy Memang; test x$whatever yang saya lakukan di sana terlihat seperti bawang di pernis. Jika kita tidak bisa mempercayai cangkang lama yang rusak untuk melakukan penawaran, maka upaya ${@+"$@"} tidak ada gunanya.

zwol 07/31/2017.

Semua variasi bahasa shell Bourne secara objektif mengerikan dibandingkan dengan bahasa scripting 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 Anda menggunakan salah satu di atas daripada skrip shell.

Satu-satunya keuntungan bahasa shell masih memiliki lebih dari bahasa-bahasa baru adalah bahwa something memanggil dirinya /bin/sh dijamin ada pada apa pun yang dimaksudkan untuk menjadi Unix. Namun, sesuatu itu mungkin tidak sesuai dengan POSIX; banyak dari Unix milik warisan membekukan bahasa yang diimplementasikan oleh /bin/sh dan utilitas dalam PATH default prior 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-alat dalam direktori not pada PATH default (misalnya /usr/xpg4/bin ) tetapi mereka tidak dijamin ada.

Namun, dasar-dasar Perl more likely hadir pada instalasi Unix yang dipilih secara acak daripada Bash. (Dengan "dasar-dasar Perl" Maksudku /usr/bin/perl ada dan some , mungkin cukup lama, versi Perl 5, dan jika Anda beruntung set modul yang dikirimkan dengan versi interpreter juga tersedia.)

Karena itu:

Jika Anda menulis sesuatu yang harus bekerja di everywhere yang dimaksudkan sebagai Unix (seperti skrip "configure"), Anda harus menggunakan #! /bin/sh #! /bin/sh , dan Anda tidak perlu menggunakan ekstensi apa pun. Saat ini saya akan menulis shell POSIX.1-2001-compliant dalam keadaan ini, tetapi saya akan siap untuk menambal POSIXisms jika seseorang meminta dukungan untuk besi berkarat.

Tetapi jika Anda not menulis sesuatu yang harus bekerja di mana-mana, maka pada saat Anda tergoda untuk menggunakan Bashisme sama sekali, Anda harus berhenti dan menulis ulang semuanya dalam bahasa scripting yang lebih baik. Diri masa depan Anda akan berterima kasih.

(Jadi kapan waktu yang tepat untuk menggunakan ekstensi Bash? Untuk urutan pertama: tidak pernah. Untuk urutan kedua: hanya untuk memperluas lingkungan interaktif Bash - misalnya untuk menyediakan penyelesaian tab yang cerdas dan permintaan yang menarik.)

Related questions

Hot questions

Language

Popular Tags