Tujuan [-n “$ PS1”] di bashrc

10

Apa tujuan melakukan [ -n "$PS1" ]di [ -n "$PS1" ] && source ~/.bash_profile;layani? Baris ini termasuk dalam repo.bashrc dotfiles .

tembakan
sumber

Jawaban:

20

Ini memeriksa apakah shell itu interaktif atau tidak. Dalam hal ini, hanya sumber ~/.bash_profilefile jika shell bersifat interaktif.

Lihat "Apakah Shell ini Interaktif?" dalam manual bash, yang mengutip ungkapan spesifik itu. (Ini juga merekomendasikan memeriksa apakah shell bersifat interaktif dengan menguji apakah $-variabel khusus berisi ikarakter, yang merupakan pendekatan yang lebih baik untuk masalah ini.)

filbranden
sumber
bash, setidaknya, akan menghapus PS1 dan PS2 jika shellnya interaktif . Anda bisa melihatnya sendiri, dengan ( export PS1='abc$ '; bash -c 'echo "[$PS1]"' ), yang hanya dicetak []. Tampaknya zsh tidak melakukan hal yang sama, setidaknya dari percobaan ... Dalam kasus apa pun, maksud dari [ -n "$PS1" ]adalah untuk memeriksa apakah shell itu interaktif atau tidak.
filbranden
3
Itu bashunsets PS1 ketika non-interaktif (typo dalam komentar sebelumnya) adalah bug IMO, PS1 bukanlah variabel bash tertentu, tidak memiliki bisnis unsetting itu. Ini satu-satunya shell yang melakukan itu (meskipun yashjuga menetapkan PS1ke nilai default bahkan ketika non-interaktif).
Stéphane Chazelas
1
Karena kode yang dimaksud adalah dalam file khusus-bash, ini sepertinya jawaban yang masuk akal. Jawaban lain membahas kasus POSIX yang lebih umum atau cangkang lain. Anda telah menjawab "apa tujuan dari ini?" niat dalam Pertanyaan sambil menyisihkan sisanya dengan tepat. Adalah baik untuk mengetahui apa yang dilakukan bash dan mungkin cara yang lebih baik untuk mencapai tujuan juga.
Jeff Schaller
Saya akan menganggap jawaban ini lebih lengkap jika menyarankan alternatif yang lebih dapat diandalkan (katakanlah, [[ $- = *i* ]] && source ~/.bash_profile).
Charles Duffy
@CharlesDuffy Sejujurnya saya tidak berpikir ada banyak yang salah dengan [ -n "${PS1}" ], tapi saya masih memperbarui jawaban saya untuk menyoroti bahwa manual bash juga menyarankan / merekomendasikan memeriksa $-untuk menentukan apakah shell itu interaktif, saya harap Anda menemukan yang meningkatkan jawabannya. Bersulang!
filbranden
19

Apa ini?

Ini adalah cara yang luas untuk menguji apakah shell itu interaktif. Berhati-hatilah karena itu hanya bekerja di bash, tidak bekerja dengan shell lain. Jadi tidak apa-apa (jika konyol) untuk .bashrc, tetapi tidak akan bekerja di .profile(yang dibaca oleh sh, dan bash hanya salah satu dari implementasi yang mungkin dari sh, dan bukan yang paling umum).

Mengapa ini bekerja (hanya di bash!)

Shell interaktif mengatur variabel shellPS1 ke string prompt default. Jadi jika shell itu interaktif, PS1sudah diatur (kecuali jika pengguna .bashrctelah menghapusnya, yang belum bisa terjadi di atas .bashrc, dan Anda bisa menganggap itu adalah hal yang konyol untuk dilakukan).

Kebalikannya benar di bash: contoh non-interaktif bash tidak disetel PS1saat mereka mulai. Perhatikan bahwa perilaku ini adalah khusus untuk bash, dan ini bisa dibilang bug (mengapa bash -c '… do stuff with $var…'tidak bekerja ketika varadalah PS1?). Tetapi semua versi bash hingga dan termasuk 4.4 (versi terbaru saat saya menulis) melakukan ini.

Banyak sistem mengekspor PS1ke lingkungan. Ini adalah ide yang buruk, karena banyak shell yang berbeda digunakan PS1tetapi dengan sintaks yang berbeda (mis. Esk cepat prompt bash benar-benar berbeda dari eskut cepat zsh ). Tetapi cukup luas bahwa dalam praktiknya, melihat yang PS1diatur bukanlah indikator yang dapat diandalkan bahwa shell bersifat interaktif. Shell mungkin telah diwarisi PS1dari lingkungan.

Mengapa (salah) digunakan di sini

.bashrcadalah file yang dibaca bash pada saat startup ketika bersifat interaktif. Fakta yang kurang terkenal adalah bahwa bash juga membaca .bashrcadalah shell login dan heuristik bash menyimpulkan bahwa ini adalah sesi jarak jauh (bash memeriksa apakah induknya adalah rshdatau sshd). Dalam kasus kedua ini, sepertinya tidak PS1akan diatur di lingkungan, karena belum ada file dot yang berjalan.

Namun, cara kode menggunakan informasi ini kontraproduktif.

  • Jika shell adalah shell interaktif, maka ini berjalan .bash_profiledi shell itu. Tetapi .bash_profileskrip waktu masuk. Mungkin menjalankan beberapa program yang dimaksudkan untuk dijalankan hanya sekali per sesi. Ini mungkin menimpa beberapa variabel lingkungan yang sengaja ditetapkan pengguna ke nilai yang berbeda sebelum menjalankan shell itu. Menjalankan .bash_profiledi shell non-login mengganggu.
  • Jika shell adalah shell remote login non-interaktif, itu tidak akan memuat .bash_profile. Tapi ini adalah kasus di mana memuat .bash_profilebisa bermanfaat, karena shell login non-interaktif tidak otomatis memuat /etc/profiledan ~/.profile.

Saya pikir alasan orang melakukan ini adalah untuk pengguna yang masuk melalui GUI (kasus yang sangat umum) dan yang menempatkan pengaturan variabel lingkungan mereka .bash_profiledaripada .profile. Sebagian besar mekanisme login GUI dipanggil .profiletetapi tidak .bash_profile(membaca .bash_profileakan membutuhkan menjalankan bash sebagai bagian dari sesi startup, bukan sh). Dengan konfigurasi ini, ketika pengguna membuka terminal, mereka akan mendapatkan variabel lingkungannya. Namun, pengguna tidak akan mendapatkan variabel lingkungan mereka dalam aplikasi GUI, yang merupakan sumber kebingungan yang sangat umum. Solusinya di sini adalah menggunakan .profilealih-alih .bash_profilemengatur variabel lingkungan. Menambahkan jembatan di antara .bashrcdan .bash_profilemenciptakan lebih banyak masalah daripada menyelesaikannya.

Apa yang harus dilakukan

Ada cara mudah dan mudah untuk menguji apakah shell saat ini interaktif: uji apakah opsi -idiaktifkan.

case $- in
  *i*) echo "This shell is interactive";;
  *) echo "This shell is not interactive";;
esac

Ini berguna .bashrcuntuk membaca .profilehanya jika shell tidak interaktif - yaitu kebalikan dari apa yang dilakukan oleh kode! Baca .profilejika bash adalah shell login (non-interaktif), dan jangan membacanya jika itu shell interaktif.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi
Gilles 'SANGAT berhenti menjadi jahat'
sumber
4
Mungkin perlu dicatat bahwa cara yang lebih baik untuk menguji apakah shell adalah interaktif adalah dengan [[ -o interactive ]](ksh, bash, zsh) atau case $- in (*i*) ...; esac(POSIX)
Stéphane Chazelas
2
Bash saya (versi 4.4.12) sebenarnya tampaknya tidak disetel PS1jika tidak dijalankan secara interaktif. Cukup mudah untuk diuji: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'tidak akan mencetak apa pun, sambil PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'mencetak nilai yang $PS1diatur dalam file startup bash Anda (itu tidak akan mencetak string "cuckoo").
FooF
1
@ Stéphane Chazelas: POSIX tidak memerlukan yang $-berisi idengan shell interaktif.
schily
1
Bosh melakukan ini sejak 2012 agar kompatibel dengan ksh. Itu hanya tidak diperlukan oleh POSIX sampai perubahan yang Anda maksudkan menjadi efektif.
schily
1
Terus terang, saya akan mengatakan menelepon [ -n "${PS1}" ] salah agak terlalu jauh, setelah semua itu hanya rusak ketika seseorang mengekspor PS1 (yang dalam jawaban Anda, Anda mengatakan itu adalah ide yang buruk dan bahkan masuk ke alasan mengapa) dan itu tidak mempengaruhi bash anyways (karena PS1 dan PS2 tidak terhapus jika shellnya tidak interaktif.) Mungkin menggunakan kata seperti "tidak disarankan" atau berbicara tentang "keterbatasan" dari pendekatan akan lebih baik. Saya tidak berpikir itu "salah" sama sekali. Jika ada yang salah mengekspor PS1, itu pasti! Ngomong-ngomong, terima kasih sudah membahas detail ini.
filbranden
1

Tampaknya konsep aneh ini adalah hasil dari fakta yang bashtidak dimulai sebagai klon shell POSIX tetapi sebagai Bourne Shellklon.

Akibatnya, perilaku interaktif POSIX ( $ENVdipanggil untuk kerang interaktif) telah ditambahkan kemudian bashdan tidak diketahui secara luas.

Ada satu shell yang memberikan perilaku serupa. Ini cshdan hibah csh yang $promptmemiliki nilai spesifik:

$prompt not set          non-interactive shell, test $?prompt.
$prompt set but == ""    .cshrc called by the which(1) command.
$prompt set and != ""    normal interactive shell.

Tapi ini tidak berlaku untuk Shell Bourne atau untuk shell POSIX.

Untuk shell POSIX, satu-satunya metode yang diberikan adalah memasukkan kode untuk shell interaktif ke dalam file:

$ENV

yang memiliki nama spesifik shell. Itu misalnya

$HOME/.kshrc    for the korn shell
$HOME/.bashrc   for bash
$HOME/.mkshrc   for mksh
$HOME/.shrc     for the POSIX Bourne Shell

Orang lain menyebutkan flag shell -i, tetapi ini tidak dapat digunakan untuk pemrograman yang andal. POSIX tidak mensyaratkan itu set -iberfungsi, juga yang $-mengandung ishell interaktif. POSIX hanya mengharuskan sh -ishell diterapkan ke mode interaktif.

Karena variabel $PS1dapat diimpor dari lingkungan, ia mungkin memiliki nilai bahkan dalam mode non-interaktif. Fakta bahwa bash unsets PS1dalam setiap shell non-interaktif tidak diberikan oleh standar dan tidak dilakukan oleh shell lain.

Jadi pemrograman bersih (bahkan dengan bash) adalah untuk menempatkan perintah untuk shell interaktif $HOME/.bashrc.

schily
sumber
0

Saya akan berbicara terlebih dahulu tentang apa yang Debian, dan sebagian besar waktu juga Ubuntu ditetapkan untuk bash. Dan sentuhan terakhir pada sistem lain.

Dalam pengaturan file mulai shell ada banyak pendapat.
Saya juga memiliki pendapat saya tetapi saya akan mencoba menunjukkan contoh pengaturan yang benar.
Saya akan menggunakan debuan karena cukup mudah untuk menemukan contoh file-nya.
Dan debian banyak digunakan, sehingga pengaturan telah diuji dengan baik,

Apa tujuan memeriksa apakah PS1 diatur?

Hanya untuk mengetahui apakah shell itu interaktif.

The standar /etc/profiledi debian dan ubuntu (dari / usr / share / base-file / profile):

if [ "${PS1-}" ]; then
    if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then

The if's read: if interactive (PS1 default set) dan ini merupakan bash shell (tetapi tidak bertindak sebagai default sh) kemudian ubah PS1 ke yang baru (bukan yang default).

The standar /etc/bash.bashrcdi debian juga berisi:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

Yang cukup jelas dalam fungsinya: Jika interaktif tidak sumber (sisanya).

Namun, in /etc/skel/.bashrcadalah contoh cara yang benar untuk menguji shell interaktif (menggunakan $-):

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

Itu harus menunjukkan dengan jelas mengapa PS1 dan satu alternatif.

Urutan yang benar

Pengaturan yang Anda laporkan harus dihindari.
Urutan (dari pengaturan sistem untuk pengaturan pengguna yang lebih spesifik (untuk bash)) adalah /etc/profile, /etc/bash.bashrc, ~/.profiledan akhirnya ~/.bashrc. Itu menempatkan efek paling luas (dan untuk lebih banyak kerang) di /etc/profile(yang dimiliki oleh root) diikuti oleh /etc/bash.bashrc(yang juga dimiliki oleh root) tetapi hanya memengaruhi bash. Kemudian datang pengaturan pribadi $HOME, yang pertama adalah ~/.profileuntuk sebagian besar shell dan ~/.bashrc(hampir setara dengan ~/.bash_profile), khusus untuk bash saja.

Oleh karena itu salah untuk sumber ~/.bashrcdi ~/.profile, itu mengubah sebuah pengguna tertentu pengaturan untuk bash ke yang lebih umum yang mempengaruhi lebih kerang . Kecuali jika dilakukan dengan cara ini :

# ~/.profile: executed by the command interpreter for login shells
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

Ia memeriksa bahwa bash sedang berjalan dan hanya memuat .bashrcjika itu yang terjadi.

Ini adalah keputusan hulu yang berasal dari Debian. Alasannya dijelaskan di sini .

Bahkan, kebalikannya, sumber ~/.profiledalam ~/.bash_profile(atau ~/.bashrc) hanya menerapkan kembali aturan umum yang seharusnya sudah dimuat ke kasus penggunaan tertentu, dan karena itu "tidak terlalu buruk" (saya tidak mengatakan "baik"). Dan saya tidak mengatakan baik karena dapat menyebabkan sumber file berulang. Seperti ketika sub-direktori memuat orangtua, itu adalah loop direktori.

Dan dalam lintas sumber inilah pemeriksaan shell interaktif masuk akal. Hanya ketika shell interaktif ~/.bashrcdimuat, tetapi pada gilirannya dapat memuat ~/.profile(atau sebaliknya) dan dalam hal ini memeriksa shell interaktif dapat digunakan.

Ishak
sumber