Perché "A = 10 echo $ A" non stampa 10?


25

Questo comando:

A=10 echo $A

stampa una riga vuota. Perché no 10? Perché l'impostazione di ambiente temporaneo sul posto non funziona?

Voglio conoscere il motivo e la spiegazione piuttosto che la soluzione.

ero solito

LANG=C gcc ...

per forzare gcc usa la lingua di fallback (inglese) invece della lingua di sistema (cinese). Quindi suppongo che un VAR=valueprefisso configurerà un ambiente temporaneo per il comando che lo segue. Ma sembra che io abbia dei malintesi.

Risposte:


22

È una questione dell'ordine in cui avvengono le diverse fasi della valutazione di un comando.

A=10 echo $Aprimi analizza il comando in un comando semplice fatto di tre parole A=10, echoe $A. Quindi ogni parola subisce una sostituzione variabile, cioè la trasformazione di espansioni variabili come $Anei loro valori (sto omettendo i passaggi che non fanno nulla di visibile).

Se Aha il valore fooiniziale, il risultato dei passi di espansione è un semplice comando che ha ancora tre parole: A=10, echoe foo. (La shell ricorda anche a questo punto quali caratteri erano inizialmente tra virgolette - in questo caso nessuno.) Il passo successivo è eseguire il comando. Poiché A=10inizia con un nome di variabile valido seguito da un segno di uguale, viene trattato come un compito; la variabile Aè impostata 10sia nella shell che nell'ambiente durante l'esecuzione del comando. (Normalmente devi scrivere export Aper avere Anell'ambiente e non solo come variabile di shell; questa è un'eccezione.) La parola successiva non è un compito, quindi viene trattata come un nome di comando (è un comando incorporato). IlechoIl comando non dipende da nessuna variabile, quindi A=10 echo $Aha esattamente lo stesso effetto di echo $A.

Se si desidera impostare una variabile solo per la durata di un comando, ma tenendo conto dell'assegnazione durante l'esecuzione del comando, è possibile utilizzare una subshell. Una subshell, indicata da parentesi, rende tutte le modifiche allo stato (assegnazioni variabili, directory corrente, definizioni delle funzioni, ecc.) Locali alla subshell.

(A=10; echo $A)

Fare che export A=10se si vuole esportare la variabile per l'ambiente in modo che sia visto da programmi esterni.


Grazie, posso dire A=10 (echo $A)e ottenere 10?
Earth Engine,

2
@EarthEngine No, sarebbe un errore di sintassi. L'assegnazione deve essere all'inizio di un semplice comando (ovvero solo un nome di comando e alcuni parametri, e facoltativamente alcune assegnazioni iniziali e alcuni reindirizzamenti). A=10; (echo $A)output 10ma imposta anche Aper il resto dello script.
Gilles 'SO- smetti di essere malvagio'

2
@EarthEngine Ma puoi dire A=10 eval 'echo $A'. Le virgolette singole non $Avengono più interpretate finché non viene valutata l'intera riga ... A quel punto A = 10. Considero questa risposta più giusta di quella accettata.
Oli

Penso che questa sia la spiegazione corretta. Il motivo del comportamento è proprio l'ordine in cui si sta verificando l'espansione $Ae l'assegnazione di A. Ad esempio, non A=5; A=6 let 'a=A'; echo $aritorna e non credo che avvii una subshell, poiché è un comando incorporato. 65let
David Ongaro,

@EarthEngine: quando si dice che la spiegazione corretta è l' ordine di valutazione, potrebbe essere fuorviante: nonA=10 echo $A verrà impostato per nessun comando in seguito, anche se si trovano su righe diverse (quando chiaramente l'assegnazione è già stata valutata). Non si tratta di ordine, ma di portataA=10
MestreLion,

37

Quando si utilizza LANG=C gcc ... quello che succede è che la shell imposta LANG per gcc's ambiente solo , e non per l' attuale ambiente stesso ( vedi nota ). Quindi gcc, LANGal termine, torna al valore precedente (o non impostato).

Inoltre, quando lo usi A=10 echo $Aè la shell che sostituisce $ A, non l'eco, e questa sostituzione (chiamata "espansione") avviene prima della valutazione dell'istruzione (incluso l'assegnazione), quindi per funzionare come Ail valore atteso deve essere già impostato nell'ambiente attuale prima di tale affermazione.

Ecco perché A=10 echo $Anon funziona come previsto: A=10verrà impostato per l'eco, ma l'eco ignora internamente il valore della variabile d'ambiente A. E $Aviene sostituito con il valore impostato nella shell corrente (che è nessuno), e quindi passato come argomento di eco.

Quindi la tua ipotesi è corretta: VAR=value command fa il lavoro, ma questo è rilevante solo se commandinternamente usa VAR. In caso contrario, puoi comunque passare valuecome argomento a command, ma gli argomenti vengono sostituiti dalla shell corrente , quindi devono essere impostati prima dell'uso:VAR=value; command "$VAR"

Se sai come creare uno script eseguibile, puoi provare questo come test:

#!/bin/sh
echo "1st argument is $1"
echo "A is $A"

Salvalo come testscripte prova:

$ A=5; A=10 testscript "$A"; echo "$A"
1st argument is 5
A is 10
5

Ultimo ma non meno importante, vale la pena conoscere la differenza tra variabili shell e ambiente e argomenti del programma .

Ecco alcuni buoni riferimenti:

.

(*) Nota: tecnicamente la shell si trova anche nell'ambiente attuale, ed ecco perché: alcuni comandi, come echo, reade testsono built-in della shell , e come tali non generano un processo figlio. Funzionano nell'ambiente attuale. Ma la shell si occupa dell'assegnazione dura solo fino a quando il comando non è in esecuzione, quindi a tutti gli effetti pratici l'effetto è lo stesso: l'assegnazione è vista solo da quel singolo comando.


2
Questa spiegazione è in realtà errata, sebbene determini la conclusione corretta in tutti tranne alcuni casi angolari. La vera spiegazione è l'ordine di espansione: $Aviene valutata prima che l'incarico abbia luogo. Penso che la tua spiegazione fallisca solo nel caso di normali utility integrate il cui comportamento dipende dal valore della variabile: il built-in vede il valore assegnato. Un esempio comune è IFS=: read one two three restche legge i campi separati da due punti: l' readintegrato vede il valore di IFS.
Gilles 'SO- smetti di essere malvagio'

"Non per la shell corrente stessa" è errato: la variabile è impostata nella shell corrente, ma dura solo per il comando semplice corrente. echovedrebbe il valore 10per A, se curato.
Gilles 'SO- smetti di essere malvagio'

@Gilles: grazie mille per il chiarimento! Non ero a conoscenza di questa sottigliezza. Quindi, se ho capito bene, bash deve essere impostato per l' ambiente corrente , altrimenti builtin (che non genera un nuovo pid) non vedrebbe l'assegnazione come farebbero altri comandi "regurlar". Ma si disinserisce dopo aver eseguito il comando per limitare l'ambito di assegnazione. È corretto, aggiusterò la mia risposta di conseguenza. PS: tecnicismi a parte, penso ancora che una risposta dovrebbe enfatizzare l'aspetto del campo di applicazione, non l'ordine di valutazione, altrimenti si potrebbe pensare A=10 test; echo $Ache stamperà 10
MestreLion

3

Un modo possibilmente pulito per fare ciò che apparentemente desideri è emettere il comando:

A=10 eval 'echo $A'

Che in effetti rinvierà la sostituzione del valore 10 al posto di $ A in un contesto successivo (cioè, "dentro" la valutazione, che già conosce l'incarico). Nota che le virgolette singole sono essenziali. Tale costrutto comunica in modo chiaro l'assegnazione al comando desiderato (eco in questo caso) senza correre il rischio di inquinare l'ambiente attuale.

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.