Bagaimana saya bisa mencegah opsi 'shopt' yang tidak didukung dari menyebabkan kesalahan di .bashrc saya?

9

Saya bekerja di lingkungan yang relatif heterogen di mana saya dapat menjalankan versi Bash yang berbeda pada node HPC, VM, atau stasiun kerja pribadi saya yang berbeda. Karena saya meletakkan skrip login saya di repo Git, saya ingin menggunakan (ish) yang sama .bashrcdi seluruh papan, tanpa banyak "jika host ini, maka ..." - ketik kekacauan.

Aku seperti perilaku default Bash ≤ 4.1 yang mengembang cd $SOMEPATHke cd /the/actual/pathsaat menekan Tabtombol. Di Bash 4.2 dan di atasnya, Anda harus shopt -s direxpandmengaktifkan kembali perilaku ini, dan itu tidak tersedia hingga 4.2.29 . Ini hanyalah satu contoh; shoptopsi lain yang mungkin terkait , complete_fullquote(meskipun saya tidak tahu persis apa yang dilakukannya) mungkin juga telah mengubah perilaku default di v4.2.

Namun, direxpandtidak dikenali oleh versi Bash sebelumnya, dan jika saya coba shopt -s direxpanddi Bash saya .bashrc, itu menghasilkan pesan kesalahan yang dicetak ke konsol setiap kali saya masuk ke sebuah simpul dengan Bash yang lebih lama:

-bash: shopt: direxpand: invalid shell option name

Apa yang ingin saya lakukan adalah membungkus kondisional shop -s direxpanduntuk mengaktifkan opsi itu pada Bash> 4.1 dengan cara yang kuat, tanpa merusak versi Bash yang lebih lama ( yaitu , tidak hanya mengarahkan output kesalahan ke /dev/null).

TheDudeAbides
sumber
Bagaimana jawaban saya tidak membantu?
Luciano Andress Martini
@LucianoAndressMartini melakukannya, dan itulah solusi yang akhirnya saya gunakan sendiri .bashrc. Saya masih menginginkan catatan tentang bagaimana menggunakannya $BASH_VERSINFOuntuk menginterogasi versi utama / minor dari shell yang sedang berjalan, untuk perbaikan saya sendiri, itulah sebabnya saya selesai memposting jawaban saya sendiri. :)
TheDudeAbides
Lihat di jawaban saya, saya punya sesuatu tentang membandingkan versi program dengan skrip shell.
Luciano Andress Martini

Jawaban:

14

Periksa apakah direxpandada dalam output shoptdan aktifkan jika:

shopt | grep -q '^direxpand\b' && shopt -s direxpand
Luciano Andress Martini
sumber
4
Lebih baik membuatnya grep -q '^direxpand\b'jika beberapa versi masa depan atau garpu bash memiliki opsi yang berisi ini sebagai substring dan menghapus direxpand. Tidak mungkin dalam kasus khusus ini, tetapi tidak mahal untuk menjadi kuat.
Gilles 'SO- stop being evil'
Terima kasih Luciano. Saya bermaksud menjawab pertanyaan saya sendiri, tetapi saya akan menerima jawaban Anda setelah suntingan saya melalui tinjauan sejawat. Mungkin Anda bisa menyetujuinya sendiri?
TheDudeAbides
4
Bash memungkinkan untuk menanyakan opsi shell tertentu, sehingga orang dapat menggunakannya [ -z "$(shopt -po direxpand 2>&-)" ] || shopt -s direxpand. Tidak ada lagi masalah regex! :-)
David Foerster
@ DavidFoerster Saya akan membalikkan logika: [ -n "blah" ] && shopt blahCara Anda mengatakannya, Anda mengatakan "jika direxpand tidak didukung, maka jangan lakukan hal ini".
Kaya
1
@ Rich: Sebagian besar skrip shell saya termasuk set -edi bagian atas, jadi saya cenderung menggunakan jalan pintas dengan cara ini.
David Foerster
16

Saya tidak melihat apa yang salah dengan mengarahkan ulang kesalahan ke /dev/null. Jika Anda ingin kode Anda kuat set -e, gunakan idiom umum … || true:

shopt -s direxpand 2>/dev/null || true

Jika Anda ingin menjalankan beberapa kode cadangan jika opsi tidak ada, gunakan status pengembalian shopt:

if shopt -s direxpand 2>/dev/null; then
   # the direxpand option exists
else
   # the direxpand option does not exist
fi

Tetapi jika Anda benar-benar tidak suka mengalihkan kesalahan, Anda dapat menggunakan mekanisme penyelesaian untuk melakukan introspeksi. Ini mengasumsikan bahwa Anda tidak memiliki mesin kuno dengan bash ≤ 2.03 yang tidak memiliki penyelesaian terprogram.

shopt_exists () {
  compgen -A shopt -X \!"$1" "$1" >/dev/null
}
if shopt_exists direxpand; then
  shopt -s direxpand
fi

Metode ini menghindari forking, yang lambat pada beberapa lingkungan seperti Cygwin. Begitu juga langsung 2>/dev/null, saya tidak berpikir Anda bisa mengalahkan itu pada kinerja.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Itu bukan tempat otak saya akan pergi, tapi saya suka compgenproposal. Itu barang tingkat universitas di sana! Menghindari pengalihan /dev/nullhanya preferensi pribadi. Saya suka meminta izin daripada pengampunan, jika itu masuk akal? :)
TheDudeAbides
1 untuk sekolah yang benar-benar tidak terduga dalam penyelesaian Bash yang dapat diprogram, yang memaksa saya untuk pergi ke manual untuk menguraikan apa yang compgen -A shopt -X ...dimaksud.
TheDudeAbides
4
@ TheDudeAbides Saya membaca tentang menggunakan compgencara ini di Unix & Linux , saya tidak tahu siapa yang pertama kali mengusulkannya. (Saya berhenti menggunakan bash sebagai shell utama saya sebelum selesai diprogram.) Dalam pemrograman biasanya ide yang buruk untuk meminta izin karena ada risiko bahwa pemeriksaan izin tidak akan cocok dengan apa yang sebenarnya Anda lakukan, baik karena pengkodean kesalahan (di mana Anda tidak cukup memeriksa apa yang Anda pikir Anda periksa) atau karena apa yang Anda periksa berubah sebelum Anda menggunakannya .
Gilles 'SO- stop being evil'
5

Ketika Anda tahu pasti bahwa shoptopsi tertentu tersedia di rilis utama / minor / patch tertentu dari Bash, Anda dapat memeriksa $BASH_VERSIONvariabel atau elemen-elemen $BASH_VERSINFO[]array untuk mengaktifkannya secara kondisional.

Inilah tes untuk Bash 4.2.29 atau lebih tinggi, versi di mana direxpand pertama kali diperkenalkan ke seri 4.2:

if [[ $BASH_VERSION == 4.2.* && ${BASH_VERSINFO[2]} -ge 29 ]] ||
   [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 3 ]] ||
   [[ ${BASH_VERSINFO[0]} -ge 5 ]]; then
    shopt -s direxpand
fi

Sunting: Agar lebih jelas, ini adalah solusi yang sangat direkayasa secara berlebihan untuk sekadar mengabaikan pesan kesalahan yang berasal dari skrip login Anda, tetapi saya ingin mendokumentasikannya, untuk perbaikan saya sendiri.

Perhatikan kawat gigi sekitar , yang sedang dibutuhkan, dan penggunaan dan , yang melakukan bilangan bulat daripada (dependen-lokal) perbandingan leksikal. Jika tidak dikutip, RHS operator diperlakukan sebagai pola "ekstglob" dalam Bash / kondisional, seperti yang disebutkan di sini , yang membuat perbandingan "dimulai dengan" yang lebih estetis dibandingkan dengan regex, IMO.${BASH_VERSINFO[index]}-eq-gt==[[]]

The $BASH_VERSINFOArray berisi semua informasi yang Anda akan melihat dalam output dari bash --version:

bash --version | head -1
# result:
# GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

declare -p BASH_VERSINFO
# result:
# declare -ar BASH_VERSINFO='([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")'

Ketika tidak jelas dari dokumentasi yang mendukung shoptversi Bash atau mengubah perilaku mereka, metode yang diusulkan oleh Luciano baik-baik saja:

# note the '-q' so that the matched pattern isn't actually printed
shopt | grep -q direxpand && shopt -s direxpand

... seperti solusi yang diusulkan oleh Gilles hanya mengabaikan kesalahan ( shopt -s direxpand 2>/dev/null), dan mungkin memeriksa $?jika benar-benar diperlukan.

Referensi: 1 , 2 , 3
Bacaan terkait: Set and Shopt - Why Two?

TheDudeAbides
sumber
Anda mungkin juga dapat menggunakan sesuatu seperti if [[ $BASH_VERSION > 4.3 ]];(yang cocok 4.3.0, 5.0dll., Tetapi juga 4.3.0-alpha. Saya tidak tahu apakah fakta nanti penting.)
ilkkachu
Hai @ilkkachu. Terima kasih atas hasil edit Anda untuk mencakup Bash v5.x. The direxpandpilihan memang tersedia untuk Bash 4.2, meskipun; Saya telah memverifikasi ini dengan gambar Docker di v4.2.53 dengan menjalankan docker run --rm bash:4.2 bash -c shopt | grep direxpand(dan, untuk ukuran yang baik, bahwa memang tidak tersedia di v4.1.17 dengan menjalankan docker run --rm bash:4.1 bash -c shopt | grep direxpand).
TheDudeAbides
ah ok, saya menguji 4.2.0dan menemukan fakta bahwa itu tidak bekerja di sana. The changelog juga menyebutkan itu ditambahkan dalam bash-4.3-alpha. Saya kira kemudian bahwa seseorang perlu memeriksa ${BASH_VERSINFO[2]}untuk menjadi tepat tentang hal itu, tapi saya tidak tahu rilis titik mana menambahkannya ...
ilkkachu
Saya pikir pada dasarnya kami telah membuktikan poin yang dibuat Gilles di atas; sebenarnya lebih baik untuk mencoba mengaktifkan opsi shell, dan kemudian menangani kesalahan (atau menekannya) jika tidak didukung.
TheDudeAbides