checkbashisms-compliant cara untuk menentukan shell saat ini


18

Di saya .profile, saya menggunakan kode berikut untuk memastikan bahwa alias dan fungsi terkait Bash hanya bersumber jika shell login sebenarnya adalah Bash :

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

Saat ini saya sedang dalam proses meletakkan file konfigurasi shell, skrip dan fungsi saya di bawah kontrol versi. Saya juga baru saja mulai proses menghilangkan Bashisms kasual dari script shell yang tidak mendapatkan keuntungan dari fitur Bash-spesifik, misalnya, mengganti function funcname()dengan funcname().

Untuk repositori file shell saya, saya telah mengonfigurasi hook pre-commit yang menjalankan checkbashismsutilitas dari paket devscripts Debian pada setiap shfile dalam repositori untuk memastikan bahwa saya tidak secara tidak sengaja memperkenalkan sintaks spesifik-Bash. Namun, ini memunculkan kesalahan untuk .profile:

possible bashism in .profile line 51 ($BASH_SOMETHING):
if [ "${BASH_VERSION:-}" ]; then

Saya bertanya-tanya apakah ada cara untuk memeriksa shell mana yang berjalan yang tidak akan memicu peringatan checkbashisms.

Saya memeriksa daftar variabel terkait shell yang terdaftar oleh POSIX dengan harapan salah satu dari mereka dapat digunakan untuk menampilkan shell saat ini. Saya juga telah melihat variabel yang ditetapkan dalam shell Dash interaktif tetapi, sekali lagi, gagal menemukan kandidat yang cocok.

Saat ini, saya sudah dikecualikan .profiledari sedang diproses oleh checkbashisms; ini adalah file kecil sehingga tidak sulit untuk memeriksanya secara manual. Namun, setelah meneliti masalah ini, saya masih ingin tahu apakah ada metode yang sesuai dengan POSIX untuk menentukan shell mana yang berjalan (atau setidaknya cara yang tidak menyebabkan checkbashismskegagalan).


Latar belakang / klarifikasi lebih lanjut

Salah satu alasan saya meletakkan file konfigurasi shell saya di bawah kontrol versi adalah untuk mengkonfigurasi lingkungan saya di semua sistem yang saya masuki secara teratur: Cygwin, Ubuntu dan CentOS (keduanya 5 dan 7, menggunakan Active Directory untuk pengguna autentikasi). Saya paling sering masuk melalui X Windows / lingkungan desktop dan SSH untuk host jarak jauh. Namun, saya ingin ini menjadi bukti masa depan dan memiliki sedikit ketergantungan pada ketergantungan sistem dan alat lain mungkin.

Saya telah menggunakan checkbashismssebagai pemeriksaan kewarasan otomatis sederhana untuk sintaksis file terkait shell saya. Ini bukan alat yang sempurna, misalnya, saya sudah menerapkan tambalan sehingga tidak mengeluh tentang penggunaan command -vdalam skrip saya. Saat meneliti, saya telah belajar bahwa tujuan sebenarnya dari program ini adalah untuk memastikan kepatuhan dengan kebijakan Debian yang, seperti yang saya pahami, didasarkan pada POSIX 2004 dan bukan 2008 (atau revisi 2013).


Apa yang Anda lakukan adalah sebagai POSIX-compliant sebagai sesuatu yang dapat dilakukan ketika seluruh titik dijalankan secara berbeda pada lingkungan yang sesuai dengan POSIX. Daging sapi Anda menggunakan checkbashisms.
Gilles 'SANGAT berhenti menjadi jahat'

Dan omong-omong saya tidak pernah menggunakan checkbashisms untuk memeriksa portabilitas konfigurasi saya. Saya memeriksanya dengan menggunakannya pada sistem yang berbeda.
Gilles 'SANGAT berhenti menjadi jahat'

2
Dan omong-omong, untuk hal spesifik yang Anda lakukan di sini, tulis .bash_profilesumber yang keduanya .profiledan (kondisional) .bashrc.
Gilles 'SANGAT berhenti menjadi jahat'

Terima kasih atas umpan balik @Gilles. Itu solusi yang sangat elegan untuk masalah spesifik.
Anthony G - keadilan untuk Monica

Jawaban:


16

Anda

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

kode benar-benar sesuai POSIX dan cara terbaik untuk memeriksa bahwa Anda sedang berjalan bash. Tentu saja $BASH_VERSIONvariabelnya khusus untuk bash, tapi itu sebabnya Anda menggunakannya! Untuk memeriksa apakah Anda menjalankan bash!

Catatan yang $BASH_VERSIONakan ditetapkan apakah bashdipanggil sebagai bashatau sh. Setelah Anda menegaskan bahwa Anda sedang menjalankan bash, Anda dapat menggunakan [ -o posix ]sebagai indikator bahwa shell dipanggil sebagai sh(meskipun opsi itu juga diatur ketika POSIXLY_CORRECT berada di lingkungan atau bashdipanggil dengan -o posix, atau dengan SHELLOPTS = posix di lingkungan. Tetapi dalam semua kasus itu, bashakan berperilaku seolah-olah disebut sebagai sh).


Variabel lain yang bisa Anda gunakan alih-alih $BASH_VERSIONdan yang checkbashismtampaknya tidak mengeluh kecuali melewati -xopsi tersebut $BASH. Itu juga spesifik untuk yang bashAnda juga harus dapat digunakan untuk menentukan apakah Anda sedang berlari bashatau tidak.


Saya juga berpendapat itu bukan penggunaan yang tepat checkbashisms. checkbashismsadalah alat untuk membantu Anda menulis shskrip portabel (sesuai shspesifikasi dalam kebijakan Debian, superset dari POSIX), ini membantu mengidentifikasi sintaksis non-standar yang diperkenalkan oleh orang yang menulis skrip pada sistem di mana shterdapat symlink bash.

A .profileditafsirkan oleh cangkang yang berbeda, banyak di antaranya yang tidak sesuai dengan POSIX. Secara umum, Anda tidak menggunakan shsebagai shell login Anda, tetapi shell suka zsh, fishatau bashdengan fitur interaktif yang lebih maju.

bashdan zsh, ketika tidak dipanggil sebagai shdan ketika file sesi profil masing-masing ( .bash_profile, .zprofile) tidak sesuai POSIX (terutama zsh) tetapi masih dibaca .profile.

Jadi itu bukan sintaks POSIX yang Anda inginkan .profiletetapi sintaks yang kompatibel dengan POSIX (untuk sh), bashdan zshjika Anda pernah menggunakan shell tersebut (bahkan mungkin Bourne sebagai shell Bourne juga membaca .profiletetapi tidak umum ditemukan pada sistem berbasis Linux ).

checkbashismspasti akan membantu Anda mengetahui bashism tetapi mungkin tidak menunjukkan sintaks POSIX yang tidak kompatibel dengan zshatau bash.

Di sini, jika Anda ingin menggunakan bashkode-spesifik (seperti menyelesaikan bashbug yang tidak dibaca ~/.bashrcdi shell login interaktif), pendekatan yang lebih baik adalah dengan ~/.bash_profilemelakukan itu (sebelum atau setelah sumber di ~/.profilemana Anda meletakkan sesi umum Anda inisialisasi).


Itu tidak lulus checkbashismskarena variabel yang digunakan.
Thomas Dickey

2
@ThomasDickey, ya tapi itu adalah kasus di mana laporan checkbashisms harus diabaikan. Semua solusi lain yang diberikan sejauh ini jauh lebih buruk.
Stéphane Chazelas

@ StéphaneChazelas Anda mengatakan bahwa semua solusi lain lebih buruk. Apa yang salah dengan menggunakan $0untuk kasus khusus ini?
Anthony G - keadilan untuk Monica

2
@AnthonyGeoghegan Itu tidak membedakan antara bash dipanggil as shdan beberapa shell lain disebut sebagai sh.
Gilles 'SANGAT berhenti menjadi jahat'

Ini juga memberikan positif palsu jika dashatau shell non-bash lainnya tidak tepat disebut dengan argv[0] == "bash", yang sepenuhnya legal, jika tidak mungkin.
Kevin

17

Biasanya orang menggunakan $0untuk tujuan ini. Di situs yang Anda tautkan berdiri:

0
   (Zero.) Expands to the name of the shell or shell script.

Sangat sederhana! Saya sudah sangat terbiasa menggunakan $0untuk nama skrip shell, yang saya bersihkan lupa bahwa itu juga dapat merujuk ke shell saat ini. Terima kasih!
Anthony G - keadilan untuk Monica

1
Seperti biasa, konvensi ini dihormati oleh semua program yang berperilaku baik tetapi program jahat dapat mengacaukan Anda dengan menggunakan berbagai panggilan sistem dari execkeluarga karena memungkinkan program panggilan untuk mengidentifikasi biner dijalankan secara terpisah dari string yang akan diteruskan sebagai argumen 0.
dmckee

Itu bekerja untuk .profile :) cute
Rob

5

Biasanya variabel lingkungan SHELL memberi tahu Anda shell default. Anda seharusnya tidak perlu mencari file .bashrc secara manual (tidak benar melihat pembaruan di bawah), bash harus melakukan ini secara otomatis hanya pastikan itu ada di direktori $ HOME.

Pilihan lainnya adalah melakukan sesuatu seperti:

 ps -o cmd= $$

yang akan memberi tahu Anda perintah (tanpa argumen, = tanpa nilai tidak akan menampilkan tajuk kolom) dari proses saat ini. Contoh output:

 $ps -o cmd= $$
 bash
 $sh
 $ps -o cmd= $$
 sh

MEMPERBARUI:

Saya berdiri dikoreksi! :)

.Bashrc tidak selalu bersumber seperti yang disebutkan dalam komentar di bawah dan /programming/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

Jadi, Anda dapat memindahkan .bashrc ke .bash_profile dan melihat apakah itu berhasil tanpa harus melakukan tes. Jika tidak, Anda memiliki tes di atas.


1
.bashrcsebenarnya tidak bersumber dari shell login tetapi .profile(jika ada) atau .bash_profilesedang. SHELLtidak ditentukan oleh POSIX dan sayangnya, tidak ada cmdopsi format untuk ps.
Anthony G - keadilan untuk Monica

1
Ini yang akan saya gunakan, tetapi ini juga bisa dimanipulasi secara jahat.
dmckee

Perhatikan bahwa sementara ps -o cmd= $$seharusnya bekerja cukup banyak di mana-mana, $SHELLvariabelnya sama sekali tidak relevan. Itu adalah shell default pengguna dan tidak memiliki kaitan dengan shell apa yang sedang dijalankan atau apa shell yang menjalankan skrip shell.
terdon

2
Tidak juga, tidak. banyak cangkang akan membaca ~/.profileketika dimulai sebagai cangkang login. Jadi, misalnya, Anda $SHELLbisa cshdan Anda berlari bash -l. Itu akan mulai bash sebagai shell login, jadi itu akan membaca ~/.profile, tetapi Anda $SHELLakan tetap menunjuk ke csh. Plus, .profilemungkin bersumber secara eksplisit oleh skrip lain. Karena OP akan mencapai ketahanan maksimum, semua jenis kasing harus dipertimbangkan. Bagaimanapun, $SHELLtidak memberikan informasi berguna tentang shell yang sedang berjalan.
terdon

2
FWIW, saya juga berdiri dikoreksi - tentang SHELLtidak ditentukan oleh POSIX: pubs.opengroup.org/onlinepubs/9699919799/basedefs/...
Anthony G - keadilan untuk Monica

4

Pertanyaannya menanyakan shell login pengguna serta shell saat ini dengan cara yang sesuai checkbashisms. Jika itu dimaksudkan sebagai shell di mana pengguna masuk, saya akan menggunakan yang dari /etc/passwd, misalnya,

MY_UID=$(id -u)
MYSHELL=$(awk -F: '$3 == '$MY_UID'{ print $7; }' </etc/passwd )

Pengguna tentu saja dapat memulai shell baru setelah masuk. Tentu saja, jika satu bash dan yang lainnya tidak, tes variabel lingkungan bash mungkin tidak membantu.

Beberapa mungkin ingin menggunakan getentbukan hanya passwdfile (tapi itu tidak akan berada dalam ruang lingkup pertanyaan).

Berdasarkan komentar tentang LDAP, dan saran untuk logname, formulir alternatif ini dapat digunakan:

MY_NAME=$(logname)
MYSHELL=$(getent passwd | awk -F: '$1 ~ /^'$MY_NAME'$/ {print $7;}' )

Saat mengujinya, saya perhatikan bahwa lognametidak suka inputnya dialihkan (jadi saya meninggalkan ekspresi terpecah). Pemeriksaan cepat getentharus dilakukan pada platform yang disebutkan (meskipun itu seharusnya disediakan dalam pertanyaan awal):


1
Dengan asumsi database pengguna ada di / etc / passwd (bukan LDAP / NIS / mysql ...) dan hanya ada satu nama pengguna per userid. (akan lebih baik untuk memeriksa kolom pertama yang menentang $(logname)).
Stéphane Chazelas

iirc, POSIX tidak mendefinisikan utilitas yang dibutuhkan (atau saya akan menggunakannya dalam jawaban saya). Apakah untuk menggunakan idatau variabel yang dapat dimodifikasi pengguna adalah pilihan yang berbeda.
Thomas Dickey

1
Perhatikan bahwa saya katakan $(logname), tidak $LOGNAME. ID pengguna dan shell login keduanya berasal dari nama pengguna yang digunakan saat login. Anda tidak dapat kembali dari id pengguna ke shell login kecuali pada sistem di mana hanya ada satu nama pengguna per id pengguna. Atau TKI, kunci utama dalam database pengguna adalah nama pengguna, bukan uid.
Stéphane Chazelas

Terima kasih @ThomasDickey untuk jawaban yang datang dari perspektif yang berbeda. Namun, ini sedikit lebih kompleks dari apa yang saya pikirkan (lebih seperti jawaban jimmij dan Stéphane ). Salah satu alasan saya meletakkan file konfigurasi shell saya di bawah kontrol versi adalah untuk mengkonfigurasi lingkungan saya di semua sistem yang saya masuki secara teratur: Cygwin, Ubuntu dan CentOS (keduanya 5 dan 7, menggunakan Active Directory untuk pengguna autentikasi). Untuk alasan itu, saya lebih memilih untuk menjaga logika sesederhana mungkin.
Anthony G - keadilan untuk Monica
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.