Imposta variabili d'ambiente variabili in bash (o altro)


9

Voglio che il mio script legga un file contenente coppie chiave / valore di variabili d'ambiente da impostare, quindi le imposti.

Finora ho questo:

#!/bin/bash

cat $1 | while read kv
do
    key=${kv%=*}
    val=`echo ${kv#*=} | sed 's/^"\|"$//g'`
    export $key="$val"
done

E voglio leggere un file come questo:

XAUTHLOCALHOSTNAME="localhost"
DISPLAY=":0"
XAUTHORITY="/tmp/some-XAuthority"

Ho solo bisogno di queste variabili nell'ambito per la durata dello script, quindi non ho bisogno di risolvere il problema di impostare una variabile nello script per l'ambito padre .

Dai miei test, penso che il mio problema risieda export $key="$val", quindi penso di aver solo bisogno di un rimpiazzo per quella linea.


un po 'fuori tema ma dato che mi sono bloccato mentre cercavo di imitare il tuo script, sto condividendo ... quando afferro una chiave ... usare key=${kv%%=*}è meglio di key=${kv%=*}perché %% e ## differiscono da% e # per sapere se la parte rimossa è la parte rimovibile più corta o più lunga
pulkitsinghal

Risposte:


10

export $key=$valdovrebbe funzionare bene dentro bash. Sospetto che il tuo problema sia la pipe ( |). Tutti i comandi in una pipeline vengono eseguiti in una subshell. Nel tuo esempio, l'istanza di bashquello che sta eseguendo il tuo script eliminerà 2 subshells: uno per cat $1e un altro per il while read .... Le variabili vengono assegnate ed esportate nella subshell che esegue il ciclo while, quindi tutte vengono prontamente eliminate quando la subshell esce. Un modo per risolvere questo problema è non generare affatto le conchiglie. Invece di un uso inutile di cat , prova invece il reindirizzamento:

while read ...; do
   ...
done < "$1"

BashFAQ 24 lo spiega in modo molto più dettagliato.

Un approccio alternativo è semplicemente il sorgente del file, con le esportazioni inserite:

. <(sed '/^export/!s/^/export /' "$1")

È la <( )sostituzione del processo.

Come ulteriore avvertenza, poiché stai leggendo le variabili di ambiente da un argomento, devi assicurarti che il file degli argomenti $1sia una fonte attendibile, quindi non può fare cose cattive per il tuo script.


Ho usato il reindirizzamento in precedenza; qualcos'altro non deve aver funzionato quando ho cambiato lo script da usare cat. Ma la sceneggiatura con reindirizzamento funziona ora, grazie!
palswim,

L'uso inutile del gatto è considerato dannoso!
Scott,

2

( jw013 ha già dato una risposta corretta , ma voglio sottolineare qualche altro problema.)

Il lato destro di una pipeline viene eseguito nella propria subshell in bash (così come nella maggior parte delle altre shell, con l'eccezione ATT ksh e zsh). Quindi stai impostando le variabili di ambiente nella subshell che esegue il ciclo, ma non nel processo principale della shell.

Ecco una soluzione semplice che è quella di sostituire l'uso inutile di catun reindirizzamento:

while read …; do …; done <"$1"

In generale, se è necessario preelaborare l'input, inserire il ciclo e il resto dello script (almeno la parte che necessita delle variabili modificate) all'interno di un blocco.

grep '^foo_' "$1" | {
  while read …; do …; done
  do_something
}

Si noti che in generale, while read line; do …non scorre completamente sulle righe di input. Vedi Perché viene while IFS= readusato così spesso, invece di IFS=; while read..? per una spiegazione. Il linguaggio corretto per iterare sulle righe di input è

while IFS= read -r line; do 

Qui, la rimozione di spazi bianchi iniziali e finali non ha importanza poiché non dovrebbe esserci alcun input di esempio, ma potrebbe essere necessario -revitare l'espansione della barra rovesciata. È possibile che while read linesia in realtà un modo corretto di leggere il tuo input (ma sospetto solo se i valori delle variabili non contengono righe). È anche possibile che il tuo formato di input sia ambiguo o più complesso da analizzare. Controlla cosa succede se uno dei valori delle variabili contiene un carattere di nuova riga, una doppia virgoletta o una barra rovesciata.

Un punto in cui stai decisamente rovinando l'input è quando estrai il valore:

val=`echo ${kv#*=} | sed 's/^"\|"$//g'`

In primo luogo, è necessario utilizzare le virgolette attorno alla sostituzione di variabile: echo "${kv#*=}"; in caso contrario la shell esegue la suddivisione delle parole e la generazione del nome del file (ovvero globbing) su di essa. In secondo luogo, echonon è un modo affidabile per stampare una stringa, poiché alcune stringhe che iniziano con a -sono trattate come opzioni e alcune shell eseguono l'espansione della barra rovesciata sull'argomento. Un modo affidabile e portatile per stampare una stringa è printf %s "${kv#*=}". Inoltre, se il valore si estende su più righe, il programma sed agirà su ciascuna separatamente, il che è errato. Questo è risolvibile, ma c'è un metodo più semplice, che è quello di utilizzare gli impianti di manipolazione delle stringhe della shell: val=${kv#*=}; val=${val#\"}; val=${val%\"}.

Supponendo che il tuo input sia correttamente analizzato con un semplice read, ecco un modo più semplice per analizzarlo, sfruttando l' IFSimpostazione per dividere ogni riga:

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"


0

/ ENV

EXPORT=value
#NOTEXPORT=notvalue

copione

#!/bin/sh

for entry in $(cat /env)
do
  if [[ ! $entry == \#* ]]
  then
    export $entry
  fi
done
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.