Scopo di [-n “$ PS1”] in bashrc


10

A quale scopo serve l' [ -n "$PS1" ]in [ -n "$PS1" ] && source ~/.bash_profile;? Questa riga è inclusa in un repo.bashrc di un dotfiles .

Risposte:


20

Questo sta verificando se la shell è interattiva o meno. In questo caso, approvvigionare il ~/.bash_profilefile solo se la shell è interattiva.

Vedi "Questa Shell è interattiva?" nel manuale di bash, che cita quel linguaggio specifico. (Si consiglia inoltre di verificare se la shell è interattiva verificando se la $-variabile speciale contiene il icarattere, che rappresenta un approccio migliore a questo problema.)


bash, almeno, disinserirà PS1 e PS2 se la shell è interattiva . Puoi vederlo da solo, con ( export PS1='abc$ '; bash -c 'echo "[$PS1]"' ), che semplicemente stampa []. Sembra zsh non fare lo stesso, almeno da un esperimento ... In ogni caso, l' intento della [ -n "$PS1" ]è quello di verificare se la shell è interattiva o meno.
filbranden,

3
Che bashdisattiva PS1 quando non interattivo (errore di battitura nel tuo commento precedente) è un bug IMO, PS1 non è una variabile specifica per bash, non ha business che la disattivi. È l'unica shell che lo fa ( yashanche se imposta anche PS1un valore predefinito anche quando non interattivo).
Stéphane Chazelas,

1
Poiché il codice in questione si trova in un file specifico di bash, questa sembra una risposta ragionevole. Altre risposte riguardano il caso più generale di specifiche POSIX o altre shell. Hai risposto al "qual è lo scopo di questo?" intenzione nella domanda lasciando opportunamente da parte il resto. È bene sapere cosa sta facendo bash e forse anche modi migliori per raggiungere l'obiettivo.
Jeff Schaller

Considererei questa risposta più completa se suggerisse un'alternativa più affidabile (diciamo, [[ $- = *i* ]] && source ~/.bash_profile).
Charles Duffy,

@CharlesDuffy Francamente non penso che ci sia molto di sbagliato in questo [ -n "${PS1}" ], ma ho ancora aggiornato la mia risposta per evidenziare che il manuale di bash suggerisce anche / raccomanda di ispezionare $-per determinare se la shell è interattiva, spero che tu trovi che migliora la risposta. Saluti!
filbranden,

19

Cosa fa questo

Questo è un modo diffuso per verificare se la shell è interattiva. Attenzione che funziona solo in bash, non funziona con altre shell. Quindi va bene (se sciocco) per .bashrc, ma non funzionerebbe .profile(che viene letto da sh, e bash è solo una delle possibili implementazioni di sh, e non la più comune).

Perché funziona (solo in bash!)

Una shell interattiva imposta la variabile di shellPS1 sulla stringa di prompt predefinita. Quindi, se la shell è interattiva, PS1è impostata (a meno che l'utente non l' .bashrcabbia rimossa, cosa che non può essere ancora avvenuta nella parte superiore di .bashrc, e si potrebbe considerare che è una cosa sciocca fare comunque).

Il contrario è vero in bash: istanze non interattive di bash disinserite PS1quando iniziano. Nota che questo comportamento è specifico per bash ed è probabilmente un bug (perché bash -c '… do stuff with $var…'non dovrebbe funzionare quando lo varè PS1?). Ma tutte le versioni di bash fino alla 4.4 inclusa (l'ultima versione mentre scrivo) lo fanno.

Molti sistemi vengono esportati PS1nell'ambiente. È una cattiva idea, perché molte shell diverse usano PS1ma con una sintassi diversa (ad esempio , i prompt escape di bash sono completamente diversi dai prompt escape di zsh ). Ma è abbastanza diffuso che in pratica, vedere che PS1è impostato non è un indicatore affidabile che la shell sia interattiva. La shell potrebbe aver ereditato PS1dall'ambiente.

Perché è (mis) usato qui

.bashrcè il file che bash legge all'avvio quando è interattivo. Un fatto meno noto è che bash legge anche .bashrcuna shell di login e l'euristica di bash conclude che si tratta di una sessione remota (bash verifica se il suo genitore è rshdo sshd). In questo secondo caso, è improbabile che PS1venga impostato nell'ambiente, poiché non è ancora stato eseguito alcun file dot.

Tuttavia, il modo in cui il codice utilizza queste informazioni è controproducente.

  • Se la shell è una shell interattiva, questa viene eseguita .bash_profilein quella shell. Ma .bash_profileè uno script al momento del login. Potrebbe eseguire alcuni programmi che devono essere eseguiti una sola volta per sessione. Potrebbe sovrascrivere alcune variabili di ambiente che l'utente aveva deliberatamente impostato su un valore diverso prima di eseguire quella shell. L'esecuzione .bash_profilein una shell non di accesso è dirompente.
  • Se la shell è una shell di accesso remoto non interattiva, non verrà caricata .bash_profile. Ma questo è il caso in cui il caricamento .bash_profilepotrebbe essere utile, perché una shell di login non interattiva non si carica automaticamente /etc/profilee ~/.profile.

Penso che il motivo per cui le persone fanno questo sia per gli utenti che accedono tramite una GUI (un caso molto comune) e che inseriscono le impostazioni delle variabili di ambiente .bash_profileanziché .profile. La maggior parte dei meccanismi di accesso alla GUI invoca .profilema non .bash_profile(la lettura .bash_profilerichiederebbe l'esecuzione di bash come parte dell'avvio della sessione, anziché di sh). Con questa configurazione, quando l'utente apre un terminale, otterrà le variabili di ambiente. Tuttavia, l'utente non otterrà le variabili di ambiente nelle applicazioni GUI, che è una fonte di confusione molto comune. La soluzione qui è utilizzare .profileinvece di .bash_profileimpostare le variabili di ambiente. L'aggiunta di un ponte tra .bashrce .bash_profilecrea più problemi di quanti ne risolva.

Cosa fare invece

Esiste un modo semplice e portatile per verificare se la shell corrente è interattiva: verificare se l'opzione -iè abilitata.

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

Questo è utile .bashrcper leggere .profilesolo se la shell non è interattiva - cioè il contrario di ciò che fa il codice! Leggi .profilese bash è una shell di accesso (non interattiva) e non leggerla se è una shell interattiva.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi

4
Vale la pena notare che un modo migliore per testare se una shell è interattiva è con [[ -o interactive ]](ksh, bash, zsh) o case $- in (*i*) ...; esac(POSIX)
Stéphane Chazelas,

2
Il mio bash (versione 4.4.12) in realtà sembra disinserito PS1se non eseguito in modo interattivo. È abbastanza facile da testare: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'non stampa nulla, mentre PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'stampa il valore di $PS1set nei file di avvio di bash (non stampa la stringa "cuculo").
FooF

1
@ Stéphane Chazelas: POSIX non richiede che $-contenga icon una shell interattiva.
schily

1
Bosh lo fa dal 2012 per essere compatibile con ksh. POSIX non è stato richiesto fino a quando la modifica della tua mantion non diventerà effettiva.
schily

1
Francamente, direi che chiamare nel modo [ -n "${PS1}" ] sbagliato va un po 'troppo lontano, dopo tutto si interrompe solo quando qualcuno esporta PS1 (che nella tua risposta dici che è una cattiva idea e anche andare nei motivi per cui) e questo non influisce bash comunque (poiché disinserisce PS1 e PS2 se la shell non è interattiva). Forse usare una parola come "scoraggiato" o parlare dei "limiti" dell'approccio sarebbe stato migliore. Non penso che sia "sbagliato" del tutto. Se qualcosa non va nell'esportazione di PS1, questo è certo! Comunque, grazie per essere entrato nei dettagli di questo.
filbranden,

1

Sembra che questo strano concetto sia il risultato del fatto che bashnon è iniziato come un clone della shell POSIX ma come un Bourne Shellclone.

Di conseguenza, il comportamento interattivo POSIX ( $ENVviene chiamato per shell interattive) è stato aggiunto in seguito bashe non è ampiamente noto.

C'è una shell che garantisce un comportamento simile. Questo è cshe csh concede che $promptha valori specifici:

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

Ma ciò non si applica né alla Bourne Shell né alle shell POSIX.

Per una shell POSIX, l'unico metodo concesso è inserire il codice per le shell interattive nel file:

$ENV

che ha un nome specifico della shell. È ad es

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

Altre persone hanno menzionato il flag shell -i, ma questo non è utilizzabile per una programmazione affidabile. POSIX non richiede che set -ifunzioni, né che $-contenga una ishell interattiva. POSIX richiede solo che sh -iimposti la shell in modalità interattiva.

Poiché la variabile $PS1può essere importata dall'ambiente, può avere un valore anche in modalità non interattiva. Il fatto che bash unsets PS1in qualsiasi shell non interattiva non è concesso dalla norma e non fatto da qualsiasi altra shell.

Quindi una programmazione pulita (anche con bash) è quella di inserire i comandi per le shell interattive $HOME/.bashrc.


0

Per prima cosa parlerò di ciò che Debian, e la maggior parte delle volte anche Ubuntu imposta per bash. E quest'ultimo tocco su altri sistemi.

Nell'impostazione dei file di avvio della shell c'è molta opinione.
Ho anche la mia opinione ma cercherò di mostrare esempi esistenti di impostazioni corrette.
Userò debuan in quanto è abbastanza facile trovare esempi dei suoi file.
E debian è molto usato, quindi le impostazioni sono state ben testate,

Qual è l'obiettivo di verificare che PS1 sia impostato?

Solo per scoprire se la shell è interattiva.

L' impostazione predefinita /etc/profilein debian e ubuntu (da / usr / share / base-files / profile):

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

L'if è di lettura: if interattivo (set di impostazioni predefinite PS1) ed è una shell bash (ma non funziona come impostazione predefinita sh) quindi cambia PS1 con una nuova (non quella predefinita).

L' impostazione predefinita /etc/bash.bashrcin debian contiene anche:

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

Il che è abbastanza chiaro in ciò che fa: se interattivo non fonte (il resto).

Tuttavia, in /etc/skel/.bashrcè un esempio del modo corretto di testare una shell interattiva (usando $-):

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

Ciò dovrebbe mostrare chiaramente il perché di PS1 e un'alternativa.

L'ordine corretto

L'impostazione che stai segnalando dovrebbe essere evitata.
L'ordine (da impostazioni di sistema alle impostazioni utente più specifiche (per bash)) è /etc/profile, /etc/bash.bashrc, ~/.profilee infine ~/.bashrc. Questo posiziona gli effetti più ampi (e per più shell) in /etc/profile(che è di proprietà di root) seguito da /etc/bash.bashrc(che è anche di proprietà di root) ma influenza solo bash. Poi arrivano le impostazioni personali $HOME, la prima è ~/.profileper la maggior parte delle shell e ~/.bashrc(quasi equivalente a ~/.bash_profile), specifica solo per bash.

E 'quindi sbagliato fonte ~/.bashrcin ~/.profile, si sta trasformando una impostazione bash per un più generale che è specifico utente interessando più conchiglie . Tranne se fatto in questo modo :

# ~/.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

Verifica che bash sia in esecuzione e si carica solo in .bashrcquesto caso.

Questa è una decisione a monte proveniente da Debian. La logica è spiegata qui .

In effetti, il contrario, l'approvvigionamento ~/.profiledi ~/.bash_profile(o ~/.bashrc) sta solo riapplicando regole generali che avrebbero dovuto essere già caricate in un caso d'uso particolare, e quindi "non così male" (non sto dicendo "buono"). E non sto dicendo bene perché potrebbe causare il looping del sourcing dei file. Come quando una sottodirectory carica un genitore, questo è un ciclo di directory.

Ed è in questo approvvigionamento incrociato che ha senso il controllo della shell interattiva. Solo quando una shell è interattiva viene ~/.bashrccaricata, ma a sua volta può essere in fase di caricamento ~/.profile(o viceversa) ed è in questo caso che è possibile utilizzare il controllo di una shell interattiva.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.