Come posso aggiungere una directory a PATH in uno script in modo che influenzi la shell chiamante e il resto della sessione?

Sono nuovo di Ubuntu.

Ho scritto uno script per aggiungere una dir all’ambiente PATH. Quando eseguo lo script, viene eseguito correttamente e la dir viene aggiunta al PERCORSO. Ma sembra che la modifica duri solo fino alla fine dello script invece di durare per tutta la durata della sessione. Quando guardo il PERCORSO dopo che lo script è stato eseguito, la directory non è più lì. Eventuali suggerimenti?

Ci sono due cose da ricordare:

  1. I comandi, inclusi gli script, mantengono il loro ambiente per la durata del comando in esecuzione

  2. I comandi ereditano l’ambiente dal processo principale. Per i comandi avviati tramite shell, erediteranno dalla shell.

Quindi se fai PATH=$PATH:/my/dir che durerà solo per la durata degli script. Per renderlo permanente, la shell madre deve essere consapevole della modifica. Un modo corretto per farlo sarebbe scrivere in ~/.bashrc se stai usando bash o un file rc appropriato per la tua shell. Quindi possiamo usare >> per aggiungere al file

 echo PATH=$PATH:/my/dir >> ~/.bashrc 

E quando lo script termina, esegui

 source ~/.bashrc 

in modo che la shell rilegge la configurazione e sarà a conoscenza delle modifiche. Ora ogni comando eseguito in shell e ogni nuova shell intertriggers avviata erediterà la nuova variabile PATH

I due passaggi possono essere raggruppati in una funzione poiché le funzioni (almeno per bash) vengono eseguite nell’ambiente di shell corrente, quindi diversamente da uno script quando si esegue la parte di source , richiamare l’ source di una funzione influirà sulla shell corrente.

La risposta di Sergiy Kolodyazhnyy identifica qui il problema: gli script vengono eseguiti in una shell secondaria separata della shell che li chiama, e i comandi che influenzano la shell stessa, come l’assegnazione alle variabili o la modifica della directory di lavoro, non influiscono affatto sulla shell chiamante, solo la shell figlio e (se export variabili nell’ambiente) i suoi figli.

Puoi giocare con questo usando umili variabili di shell …

 $ foo=bar $ echo $foo bar $ echo -e "foo=baz \n"'echo $foo' > script $ cat script foo=baz echo $foo $ bash script baz $ echo $foo bar 

Anche se Eliah Kagan mostra in questa risposta è più facile farlo con sottotitoli.

Sto scrivendo questa risposta nel caso in cui non si voglia aggiungere la directory permanentemente al PATH, ma solo alla sessione shell corrente.

Per fare questo è sufficiente eseguire lo script nella shell corrente . Questo viene fatto con il comando source che è abbreviato a . (punto).

Data questa versione leggermente semplificata del tuo script …

 read -rp "What did you want to add to PATH? " [ -d "$REPLY" ] && PATH="$PATH:$(readlink -m $REPLY)" && echo "OK, adding $REPLY to PATH" && echo "$PATH" || echo "seems like $REPLY is not a directory" 

Si noti che ottengo lo stesso risultato di quando eseguo lo script nel solito modo:

 $ ./add-to-path What did you want to add to PATH? /home/zanna/playground OK, adding /home/zanna/playground to PATH /home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground $ echo $PATH /home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games 

Ma quando source lo script funziona come previsto:

 $ . add-to-path What did you want to add to PATH? /home/zanna/playground OK, adding /home/zanna/playground to PATH /home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground $ echo $PATH /home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground 

Aggiungerò tre versetti:

  • Raccomando di aggiungere assegnamenti PATH a ~/.profile piuttosto che a ~/.bashrc perché ~/.bashrc è originato da ogni shell Bash intertriggers, incluse le shell avviate dalla shell corrente – questo significa che le shell figlio potrebbero finire con PATH molto lunghi, come ereditano il PATH e lo accolgono quando ~/.bashrc . Al contrario, ~/.profile solito è originato solo al login (o tramite shell di login).
  • Non è necessario export quando si assegna a PATH perché è già una variabile di ambiente: in un certo senso è già esportata e rimarrà tale: un’assegnazione a PATH sarà sempre ereditata dai processi figli (sebbene non dai processi principali, come hai scoperto!) senza essere esplicitamente export .
  • Ho citato le variabili REPLY e PATH tutto il tempo. Questa è una buona idea perché entrambi possono avere spazi o altri personaggi che triggersno espansioni di shell. Tuttavia, un effetto collaterale di questo è che ~ non è espanso, quindi lo script è adatto a restituire cose come

     looks like ~/some-existing-dir is not a directory 

    che è vero (prendendo ~ letteralmente) ma non molto utile. Forse lo script dovrebbe avvisare l’utente di questo …