Perché le shell interattive sulle shell di login OSX sono predefinite?


43

In Linux e, per quanto ne so, tutti i sistemi Unix, gli emulatori di terminali eseguono shell interattive senza accesso per impostazione predefinita. Ciò significa che, per bash, la shell avviata:

Quando viene avviata una shell interattiva che non è una shell di accesso, bash legge ed esegue i comandi da /etc/bash.bashrce ~/.bashrc, se questi file esistono. Questo può essere inibito usando l' --norc opzione.

L' --rcfile opzione file forzerà bash a leggere ed eseguire comandi da file anziché /etc/bash.bashrce ~/.bashrc.

E per le shell di login:

Quando bash viene invocato come shell di login interattiva o come shell non interattiva con l' --loginopzione, legge innanzitutto ed esegue i comandi dal file /etc/profile, se quel file esiste. Dopo aver letto il file, si cerca ~/.bash_profile, ~/.bash_logine ~/.profile, in questo ordine, e legge ed esegue i comandi dal primo che esiste ed è leggibile.

L' --noprofileopzione può essere utilizzata all'avvio della shell per inibire questo comportamento.

Su OSX, tuttavia, la shell predefinita (che è bash) è stata avviata nel terminale predefinito (Terminal.app) in realtà genera ~/.bash_profileo ~.profileecc. In altre parole, si comporta come una shell di accesso.

Domanda principale : perché la shell interattiva predefinita è una shell di accesso su OSX? Perché OSX ha scelto di farlo? Ciò significa che tutte le istruzioni / tutorial per cose basate su shell che menzionano il cambiamento di cose ~/.bashrcfalliranno su OSX o viceversa ~/.profile. Tuttavia, mentre molte accuse possono essere mosse alla Apple, assumere sviluppatori incompetenti o idioti non è uno di questi. Presumibilmente, avevano una buona ragione per questo, quindi perché?

Domande secondarie: Terminal.app esegue effettivamente una shell di login interattiva o ha cambiato il comportamento di bash? È specifico per Terminal.app o è indipendente dall'emulatore di terminale?


2
Terminal.app esegue una shell di accesso. Non so perché Apple abbia scelto di farlo.
Gilles 'SO-smetti di essere malvagio' il

Risposte:


33

Il modo in cui dovrebbe funzionare è che, nel momento in cui ricevi un prompt della shell, entrambi .profilee .bashrcsono stati eseguiti. I dettagli specifici di come arrivare a quel punto sono di rilevanza secondaria, ma se uno dei file non viene eseguito, avresti una shell con impostazioni incomplete.

Il motivo per cui gli emulatori di terminale su Linux (e altri sistemi basati su X) non devono funzionare da .profilesoli è che normalmente sarà già stato eseguito quando si è effettuato l'accesso a X. Le impostazioni in .profiledovrebbero essere del tipo che può essere ereditato da sottoprocessi, quindi fintanto che viene eseguito una volta al momento del login (ad es. tramite .Xsession), non è necessario rieseguirlo per ulteriori sottotitoli.

Come spiega la pagina wiki Debian collegata da Alan Shutko:

"Perché è .bashrcun file separato da .bash_profile, allora? Questo è fatto per ragioni per lo più storiche, quando le macchine erano estremamente lente rispetto alle stazioni di lavoro di oggi. L'elaborazione dei comandi in .profileo .bash_profilepotrebbe richiedere parecchio tempo, specialmente su una macchina in cui gran parte del lavoro dovevano essere eseguiti da comandi esterni (pre-bash). Quindi vengono inseriti i difficili comandi iniziali di configurazione, che creano variabili d'ambiente che possono essere passate ai processi figlio. Vengono messe .bash_profilele impostazioni e gli alias transitori che non sono ereditati in .bashrcmodo che possano essere riletti da ogni subshell. "

Tutte le stesse regole valgono anche su OSX, ad eccezione di una cosa: la GUI di OSX non viene eseguita al .profilemomento del login, apparentemente perché ha il suo metodo di caricamento delle impostazioni globali. Ma questo significa che un emulatore di terminale su OSX fa necessario eseguire .profile(raccontando la shell che lancia che si tratta di una shell di login), altrimenti si finirebbe con un guscio potenzialmente storpio.


Ora, una specie di sciocca peculiarità di bash, non condivisa dalla maggior parte delle altre shell, è che non verrà eseguito automaticamente .bashrcse viene avviato come shell di accesso. La soluzione standard è quella di includere qualcosa come i seguenti comandi in .bash_profile:

[[ -e ~/.profile ]] && source ~/.profile    # load generic profile settings
[[ -e ~/.bashrc  ]] && source ~/.bashrc     # load aliases etc.

In alternativa, è possibile non avere .bash_profileaffatto e includere solo un codice specifico di bash nel .profilefile generico da eseguire .bashrcse necessario.

Se l'OSX è predefinito .bash_profileo .profile non lo fa, è probabilmente un bug. In ogni caso, la soluzione corretta è semplicemente aggiungere quelle righe .bash_profile.


Modifica: come nota lo strugee , la shell predefinita su OSX era tcsh, il cui comportamento è molto più sano a questo proposito: quando eseguito come shell di login interattiva, tcsh legge automaticamente entrambi .profile e .tcshrc / .cshrc, e quindi non ha bisogno di soluzioni alternative come il .bash_profiletrucco mostrato sopra.

Sulla base di questo, sono sicuro al 99% che l'incapacità di OSX di fornire un valore predefinito appropriato .bash_profileè perché, quando sono passati da TCH a BASH, la gente di Apple semplicemente non ha notato questa piccola verruca nel comportamento di avvio di Bash. Con tcsh non sono stati necessari trucchi del genere: avviare tcsh come shell di login da un emulatore di terminale OSX Just Plain Works e fa la cosa giusta senza tali kluges.


1
Grazie, in un certo senso lo sapevo. La mia domanda è: perché OSX ha scelto di impostare in modo da renderlo .bashrcirrilevante? Perché hanno scelto di creare tutte le shell di login shell? Per quanto ne so, solo la tua ultima frase si rivolge a questo e solo per dire che è un bug.
terdon

1
No, il problema è che iniziano le shell di accesso nei loro emulatori terminali. I dotfile sono comportamenti bash predefiniti, l'avvio delle shell di accesso leggerà il profilo ecc., Non bashrc ecc. La domanda è perché le shell di accesso vengono eseguite invece di quelle non di accesso.
terdon

2
Sì, e sia io che Alan abbiamo spiegato che è perché, diversamente da Linux, OSX non viene eseguito .profilequando l'utente accede alla GUI, quindi devono eseguirlo in un secondo momento per ottenere variabili di ambiente come $PATH, che sono normalmente impostate .profile, configurate correttamente . Il fatto che, come effetto collaterale, ciò .bashrcnon provenga è un bug; puoi discutere se si tratta di un bug in bash o in OSX, ma ciò non cambia il fatto che il comportamento corretto sarebbe quello di garantire che vengano caricate sia le variabili di ambiente sia le .profileimpostazioni di configurazione di bash .bashrc.
Ilmari Karonen,

1
Avere .bashrcsorgente .profilesarebbe una cattiva idea, dal momento che provocherebbe la riesecuzione di ogni subshell .profile. Se non altro, ciò causerebbe .profileespressioni comuni come export PATH = "$HOME/bin:$PATH"continuare a anteporre voci ridondanti a $PATH. Avere .profilesorgente .bashrcha molto più senso, ma solo dopo aver verificato che la shell su cui è in esecuzione è, in effetti, bash. Avere .bash_profilefonte sia .profile e .bashrc , come ho suggerito sopra, è, IMO, l'opzione più sensata.
Ilmari Karonen,

2
E sto dicendo la risposta a "perché usano una shell di login?" è "perché hanno bisogno di caricare .profile", mentre la risposta a "perché non .bashrcprovengono da .profile, allora?" è "perché è un bug!" Seriamente, questa non può essere una decisione intenzionale; è solo qualcosa che hanno trascurato, presumibilmente perché, nell'ecosistema Mac, le shell sono cittadini di seconda classe con cui la maggior parte degli utenti non dovrebbe occuparsi. (Sal. Vedi anche la risposta di Strugee per una spiegazione storica; è quasi certamente una regressione dal passaggio da tcsh a bash.)
Ilmari Karonen

14

Il motivo principale per cui le applicazioni del terminale X eseguono shell non di accesso per impostazione predefinita è che all'inizio del tempo la tua .Xsession avrebbe eseguito il .profile per impostare gli elementi di accesso iniziali. Quindi, poiché era già tutto impostato, le app del terminale non dovevano eseguirlo, potevano eseguire il .bashrc. La discussione del perché questo sarebbe importante è su https://wiki.debian.org/DotFiles :

Prendiamo xdm come esempio. un giorno pierre torna dalle vacanze e scopre che il suo amministratore di sistema ha installato xdm sul sistema Debian. Accede bene e xdm legge il suo file .xsession ed esegue fluxbox. Tutto sembra andare bene fino a quando non riceve un messaggio di errore nella locale errata! Poiché sostituisce la variabile LANG nel suo .bash_profile e poiché xdm non legge mai .bash_profile, la sua variabile LANG è ora impostata su en_US anziché fr_CA.

Ora, la soluzione ingenua a questo problema è che invece di avviare "xterm", potrebbe configurare il suo gestore di finestre per avviare "xterm -ls". Questo flag dice a xterm che invece di lanciare una shell normale, dovrebbe lanciare una shell di login. Con questa configurazione, xterm genera / bin / bash ma inserisce "- / bin / bash" (o forse "-bash") nel vettore argomento, quindi bash si comporta come una shell di login. Ciò significa che ogni volta che apre un nuovo xterm, leggerà / etc / profile e .bash_profile (comportamento bash incorporato), e poi .bashrc (perché .bash_profile dice di farlo). All'inizio può sembrare che funzioni bene - i suoi file dot non sono pesanti, quindi non si accorge nemmeno del ritardo - ma c'è un problema più sottile. Avvia anche un browser web direttamente dal suo menu Fluxbox, e il browser Web eredita la variabile LANG da fluxbox, che ora è impostata sulla locale errata. Quindi, anche se i suoi xterm potrebbero andare bene, e qualsiasi cosa lanciata dai suoi xterm potrebbe andare bene, il suo browser gli sta ancora dando pagine con impostazioni internazionali errate.

Su OS X, gli ambienti utente non sono avviati da una pila di script di shell e launchd non genera in qualsiasi momento .profile. (È un po 'un peccato, dal momento che significa che è molto più fastidioso impostare le variabili di ambiente, ma tale è la vita.) Dal momento che non funziona, quando dovrebbe eseguire .profile? Solo se avessi partecipato? Sembra un po 'inutile, dal momento che molte scatole non saranno mai bersagli di SSH. Potrebbe anche fare in modo che i terminali eseguano le shell di login di default, in modo che .profile venga eseguito qualche volta.

Che dire di .bashrc, allora? È inutile? No. Ha ancora lo scopo che aveva ai giorni del VT100. Viene usato ogni volta che si apre una shell diversa dall'apertura di una finestra Terminale. Quindi, se esci da Emacs o vi, o se fai un utente su.


1
La pagina Debian che stai citando spiega perché le .profilefonti di Debian .bashrc, non perché OSX abbia deciso di creare tutte le shell di login shell di default. In realtà, stai rispondendo a un'altra mia domanda , e grazie! Tuttavia, la tua risposta non spiega la scelta dell'OSX e la citazione di Debian è completamente irrilevante qui per quanto posso dire (per favore fammi sapere se mi manca il punto). Il modo OSX rende .bashrcecc inutili e non rende .profilepiù utili.
terdon

4

Non so perché lo avrebbero fatto. Tuttavia, ecco la mia ipotesi.

Per cominciare, vale la pena notare che su un sistema GNU / Linux, puoi ovviamente passare a vt1, vt2, ecc. Puoi ottenere una shell di login lì. Su un sistema OS X, non c'è equivalente. L'unico modo per accedere alle basi UNIX è tramite un emulatore di terminale o tramite la modalità a singolo utente (dichiarazione di non responsabilità: non ho mai usato la modalità a singolo utente; è IIRC guidato dalla riga di comando ma potrei sbagliarmi). Pertanto, in OS X qualunque sia l'impostazione predefinita nell'emulatore è l'impostazione predefinita per l'intero sistema.

Ora, perché dovresti rendere l'impostazione predefinita una shell di accesso? Ci sono un paio (leggi: non molte) ragioni a cui posso pensare di farlo.

  • Fornisce un'esperienza utente coerente se si SSH nella casella. (Particolarmente importante per l'edizione server di OS X - presumibilmente se stai eseguendo un server OS X, sei un principiante.)
  • La shell predefinita di OS X era una volta tcsh. Si tratta di un'ipotesi quanto più selvaggia possibile, ma potrebbe essere che tcshnormalmente facesse qualcosa quando eseguito come shell di login e il modello storico bloccato. (Ne dubito, però - forse uno dei più vecchi clienti abituali potrebbe dircelo.)
  • "Siamo Apple. Siamo i fornitori della più grande distribuzione UNIX sul pianeta. Non importa quanto siano insignificanti le nostre ragioni; se prendiamo una decisione, il tuo strumento deve occuparsene."

Onestamente, sto usando Darwin da ~ 6 anni e non posso rispondere correttamente a questa domanda. Neanche per me ha senso.

Per rispondere alla tua domanda, bashnon è patchato o altro (almeno per questo). L'emulatore di terminale predefinito esegue le shell di login per impostazione predefinita e presumibilmente iTerm lo copia.


1
+1 per menzionare tcsh, dal momento che il suo comportamento è molto più sano in questo senso rispetto a quello di bash: quando è iniziato come una shell di login interattiva, tcsh genera sia .profile e .tcshrc/ .cshrcsenza richiedere kluges come quello che ho dato nella mia risposta. Detto questo, sospetto che questo comportamento sia una regressione non corretta causata dal passaggio da tcsh a bash.
Ilmari Karonen,

@IlmariKaronen Intendi (qui e ) che fonti tcsh .logine .tcshrc/ .cshrc? Non avrebbe senso che tcsh si procurasse .profile; in genere contiene comandi con shsintassi che tcshnon accetteranno. Non ho macOS ma ho creato la /bin/tcshmia shell di login su Ubuntu 16.04. .profilenon è di provenienza. tcsh (1) non menziona quel file, né tcsh (1) .
Eliah Kagan,

3

Questo è un aggiornamento allo stato attuale: le risposte sono diventate obsolete quando sono state rilasciate nuove versioni di MacOSX e il comportamento di accesso è cambiato.

Questa domanda è stata posta e ha risposto nel 2014. Ho studiato l'argomento nel tentativo di creare un comune set .bashrc e .bash_profile da utilizzare in diverse distro Linux e BSD (qualunque cosa li chiamino se non sono distro).

Di recente ho acquistato un Mac Mini usato con Sierra installato, quindi ora ho sistemi con 10.6, 10.10, 10.11 e 10.12. Anche se ho rovinato quelli più vecchi (lasciando indizi sul loro status precedente), l'installazione di Sierra 10.12 non è stata toccata.

Risultati: in 10.12 Sierra, NON è stato creato alcun file .bashrc, .bash_profile o .profile predefinito. In / etc, ci sono bashrc, bashrc_Apple_Terminal e profilo. Contenuto di / etc / profile:

# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
fi

Contenuti di / etc / bashrc:

# System-wide .bashrc file for interactive bash(1) shells.
if [ -z "$PS1" ]; then
   return
fi

PS1='\h:\W \u\$ '
# Make bash check its window size after a process completes
shopt -s checkwinsize

[ -r "/etc/bashrc_$TERM_PROGRAM" ] && . "/etc/bashrc_$TERM_PROGRAM"

Lo script / etc / bashrc_Apple_Terminal imposta la variabile PROMPT_COMMAND e imposta un meccanismo per preservare lo stato della sessione per ciascun terminale; se l'app del terminale viene chiusa, lo stato della sessione precedente viene ripristinato al riavvio dell'app. Altrimenti, quasi nulla (minimo $ PS1) è impostato in / etc / bashrc, lasciando qualsiasi altra personalizzazione (reali $ PS1, alias, funzioni) da impostare nelle impostazioni ~ / .bashrc e $ PATH in .bash_profile (o in .profile , fornito da .bash_profile).

Ho confermato che iTerm2 si comporta allo stesso modo dell'app Terminale, tranne per il fatto che il comportamento predefinito dell'avvio di una sessione di accesso è visibile e può essere modificato.

Se esegui una sessione terminale XTerm X11 (ora XQuartz), vedrai una sessione non di accesso, la stessa di un sistema Linux; .bash_profile (o .profile) viene ignorato e ottieni solo .bashrc.

Ed ecco la mia risposta:

Sebbene l'app Terminal affermi di essere un xterm (TERM = xterm-256color), non imposta la variabile $ DISPLAY a meno che non sia installato X11. Stiamo assistendo a una simulazione di un ambiente X, ma non completamente reale. Se si SSH in un altro sistema con l'opzione -X (abilita l'inoltro X11), non funzionerà perché non è presente alcuna variabile $ DISPLAY. Se hai installato X11, quindi SSH in un altro sistema, l'inoltro X11 avrà esito positivo.

Conclusione (e risposta breve): l'app Terminale non è un vero terminale X11 (non imposta $ DISPLAY); si comporta molto più come un login SSH che una sessione XTerm, in quanto deve essere una sessione di login per impostare i valori da / etc / profile e ~ / .bash_profile o ~ / .profile


0

Le risposte sopra spiegano il motivo per cui le shell interattive sono shell di login su macOS per impostazione predefinita: le impostazioni in /etc/profile, ~/.profilesono ereditate da una shell non di login su sistemi basati su X, ma non su macOS . Qui voglio ricordarti che dovresti sempre usare la shell di login su macOS a causa dell'esistenza di path_helper:

 cat /etc/profile # or cat /etc/zprofile
# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
fi

L' path_helperutilità legge il contenuto dei file nelle directory /etc/paths.de /etc/manpaths.d e aggiunge il loro contenuto alle PATHe MANPATHambientali variabili, rispettivamente. (La MANPATHvariabile di ambiente non verrà modificata se non è già impostata nell'ambiente.)

Se si utilizza una shell non di accesso, alcuni percorsi non verranno importati.

Penso che sia sempre una buona idea per gli sviluppatori inserire un file /etc/paths.dper importare i loro valori di percorso, ma non collegare in modo simbolico i file binari richiamabili in posizioni come /usr/bino /bin. (L'impostazione predefinita PATHè /usr/bin:/bin:/usr/sbin:/sbin. Non tutti usano Homebrew o MacPorts aggiungendo valori personalizzati in PATH. Quindi non c'è sempre un posto sicuro dove mettere i collegamenti simbolici dei comandi richiamabili.)

Ecco un esempio di file in /etc/paths.d. Fondamentalmente stai mettendo un valore ogni riga.

 cat /etc/paths.d/Wireshark
/Applications/Wireshark.app/Contents/MacOS

Riferimento:

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.