L'impostazione di una variabile d'ambiente prima di un comando in Bash non funziona per il secondo comando in una pipe


351

In una determinata shell, normalmente imposti una o più variabili e quindi eseguo un comando. Recentemente ho appreso il concetto di anteporre una definizione variabile a un comando:

FOO=bar somecommand someargs

Questo funziona ... in un certo senso. Non funziona quando si modifica una variabile LC_ * (che sembra influenzare il comando, ma non i suoi argomenti, ad esempio, gli intervalli di caratteri '[az]') o quando si esegue il piping dell'output su un altro comando in questo modo:

FOO=bar somecommand someargs | somecommand2  # somecommand2 is unaware of FOO

Posso anteporre somecommand2 anche con "FOO = bar", che funziona, ma che aggiunge duplicazioni indesiderate e non aiuta con argomenti interpretati a seconda della variabile (ad esempio, "[az]").

Quindi, qual è un buon modo per farlo su una sola riga?

Sto pensando a qualcosa sull'ordine di:

FOO=bar (somecommand someargs | somecommand2)  # Doesn't actually work

Ho un sacco di buone risposte! L'obiettivo è quello di mantenere questo aspetto unico, preferibilmente senza utilizzare "export". Il metodo che utilizzava una chiamata a Bash era nel complesso il migliore, sebbene la versione tra parentesi con "esportazione" fosse un po 'più compatta. Anche il metodo di usare il reindirizzamento piuttosto che una pipe è interessante.


1
(T=$(date) echo $T)funzionerà
vp_arth il

Nel contesto di script multipiattaforma (incl. Windows) o progetti basati su npm (js o altro), potresti voler dare un'occhiata al modulo cross-env .
Frank Nocke,

5
Speravo che una delle risposte spiegasse anche perché questo unico tipo di funzionamento, ovvero perché non equivale a esportare la variabile prima della chiamata.
Brecht Machiels,

Risposte:


317
FOO=bar bash -c 'somecommand someargs | somecommand2'

2
Questo soddisfa i miei criteri (one-liner senza bisogno di "export") ... Immagino che non ci sia modo di farlo senza chiamare "bash -c" (es. Uso creativo delle parentesi)?
MartyMacGyver,

1
@MartyMacGyver: nessuno a cui riesco a pensare. Non funzionerà neanche con le parentesi graffe.
In pausa fino a nuovo avviso.

7
Nota che se devi eseguire il tuo somecommandsudo, devi passare sudo il -Eflag per passare attraverso le variabili. Perché le variabili possono introdurre vulnerabilità. stackoverflow.com/a/8633575/1695680
ThorSummoner

11
Nota che se il tuo comando ha già due livelli di virgolette, questo metodo diventa estremamente insoddisfacente a causa dell'inferno delle virgolette. In quella situazione esportare in subshell è molto meglio.
Pushpendre,

Dispari: su OSX, FOO_X=foox bash -c 'echo $FOO_X'funziona come previsto ma con nomi var specifici non riesce: DYLD_X=foox bash -c 'echo $DYLD_X'echos blank. entrambi funzionano evalinvece dibash -c
mwag il

209

Che ne dici di esportare la variabile, ma solo all'interno della subshell ?:

(export FOO=bar && somecommand someargs | somecommand2)

Keith ha un punto, per eseguire incondizionatamente i comandi, fare questo:

(export FOO=bar; somecommand someargs | somecommand2)

15
Userei ;piuttosto che &&; non c'è modo export FOO=bardi fallire.
Keith Thompson,

1
@MartyMacGyver: &&esegue il comando sinistro, quindi esegue il comando destro solo se il comando sinistro ha avuto esito positivo. ;esegue entrambi i comandi incondizionatamente. L' cmd.exeequivalente batch ( ) di Windows ;è &.
Keith Thompson,

2
In zsh non mi sembra di aver bisogno dell'esportazione per questa versione: (FOO=XXX ; echo FOO=$FOO) ; echo FOO=$FOOrese FOO=XXX\nFOO=\n.
rampion

3
@PopePoopinpants: perché non usare source(aka .) in quel caso? Inoltre, i backtick non dovrebbero più essere utilizzati in questi giorni e questo è uno dei motivi per cui, l'uso $(command)è waaaaay più sicuro.
0xC0000022L

3
Così semplice, ma così elegante. E mi piace la tua risposta meglio della risposta accettata, in quanto avvierà una sotto-shell uguale alla mia attuale (che potrebbe non essere bashma potrebbe essere qualcos'altro, ad esempio dash) e non ho alcun problema se devo usare le virgolette all'interno del comando args ( someargs).
Mecki,

45

Puoi anche usare eval:

FOO=bar eval 'somecommand someargs | somecommand2'

Dato che questa risposta evalnon sembra piacere a tutti, vorrei chiarire qualcosa: se usato come scritto, con le virgolette singole , è perfettamente sicuro. È buono in quanto non avvierà un processo esterno (come la risposta accettata) né eseguirà i comandi in una subshell aggiuntiva (come l'altra risposta).

Dato che otteniamo alcuni punti di vista regolari, è probabilmente utile fornire un'alternativa evalche piacerà a tutti e abbia tutti i vantaggi (e forse anche di più!) Di questo rapido eval"trucco". Basta usare una funzione! Definisci una funzione con tutti i tuoi comandi:

mypipe() {
    somecommand someargs | somecommand2
}

ed eseguirlo con le variabili di ambiente in questo modo:

FOO=bar mypipe

7
@Alfe: hai anche votato in negativo la risposta accettata? perché presenta gli stessi "problemi" di eval.
gniourf_gniourf,

10
@Alfe: purtroppo non sono d'accordo con la tua critica. Questo comando è perfettamente sicuro. Sembri davvero un ragazzo che una volta ha letto il evalmale senza capire di cosa si tratta eval. E forse non stai davvero capendo questa risposta dopo tutto (e davvero non c'è niente di sbagliato in essa). Allo stesso livello: diresti che lsè male perché for file in $(ls)è cattivo? (e sì, non hai votato in basso la risposta accettata e non hai nemmeno lasciato un commento). SO è un posto così strano e assurdo a volte.
gniourf_gniourf,

7
@Alfe: quando dico che sembri davvero un tipo che una volta letto evalè malvagio senza capire di cosa sia malvagio eval, mi riferisco alla tua frase: in questa risposta mancano tutti gli avvertimenti e le spiegazioni necessarie quando si parla eval. evalnon è cattivo o pericoloso; non più di bash -c.
gniourf_gniourf,

1
Voti a parte, il commento fornito da @Alfe implica in qualche modo che la risposta accettata è in qualche modo più sicura. Ciò che sarebbe stato più utile sarebbe stato per te descrivere ciò che ritieni non sia sicuro sull'uso di eval. Nella risposta fornita gli arg sono stati citati singolarmente proteggendo dall'espansione variabile, quindi non vedo alcun problema con la risposta.
Brett Ryan,

Ho rimosso i miei commenti per concentrare la mia preoccupazione in un nuovo commento: evalè un problema di sicurezza in generale (come bash -cma meno ovvio), quindi i pericoli dovrebbero essere menzionati in una risposta che ne propone l'uso. Gli utenti negligenti possono prendere la risposta ( FOO=bar eval …) e applicarla alla loro situazione in modo da sollevare problemi. Ma ovviamente era più importante per il rispondente capire se ho votato per il downvoting della sua e / o di altre risposte piuttosto che per migliorare qualcosa. Come ho scritto prima, l'equità non dovrebbe essere la principale preoccupazione; essere non peggio di qualsiasi altra risposta data è anche senza regole.
Alfe,

12

Usa env.

Ad esempio env FOO=BAR command,. Si noti che le variabili di ambiente verranno ripristinate / invariate nuovamente al commandtermine dell'esecuzione.

Fai solo attenzione al fatto che si verifichi una sostituzione della shell, cioè se vuoi fare riferimento $FOOesplicitamente sulla stessa riga di comando, potresti dover scappare in modo che l'interprete della shell non esegua la sostituzione prima che venga eseguita env.

$ export FOO=BAR
$ env FOO=FUBAR bash -c 'echo $FOO'
FUBAR
$ echo $FOO
BAR

-5

Usa uno script di shell:

#!/bin/bash
# myscript
FOO=bar
somecommand someargs | somecommand2

> ./myscript

13
Hai ancora bisogno export; altrimenti $FOOsarà una variabile di shell, non una variabile di ambiente, e quindi non visibile a somecommando somecommand2.
Keith Thompson,

Funzionerebbe ma vanifica lo scopo di avere un comando a una riga (sto cercando di imparare modi più creativi per evitare multi-line e / o script per casi relativamente semplici). E cosa ha detto @Keith, anche se almeno l'esportazione sarebbe rimasta nell'ambito dello script.
MartyMacGyver,
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.