Qual è la differenza tra “eval” e “source / dev / stdin”?


17

Tra le seguenti alternative ...

  1. con eval.

    comd="ls"
    eval "$comd"
  2. con source /dev/stdin

    printf "ls" | source /dev/stdin
  3. con source /dev/stdine ( )o{ }

    ( printf "ls" ) | source /dev/stdin
    { printf "ls"; } | source /dev/stdin

    (Quando si corre printfin { }, c'è qualche vantaggio diverso da non utilizzare subshell?)

    • Qual'è la differenza tra loro?

    • Quale è preferito?

    • Qual è il modo preferito per eseguire i comandi? ()o {}?


1
Non consiglierei nessuno dei due approcci. Cosa stai effettivamente cercando di fare , di cui pensi di dover eseguire codice arbitrario inviato da un utente?
Chepner,

2
Pensavo anche che stessero eseguendo input dell'utente arbitrario (così com'è), fino a quando non ho letto la domanda. Ma potresti prevedere che lo faranno.
ctrl-alt-delor

Risposte:


17
  • Qual è la differenza tra i modi?

da bash manpage:

eval [arg ...]
              The  args  are read and concatenated together into a single com
              mand.  This command is then read and executed by the shell,  and
              its  exit status is returned as the value of eval.  If there are
              no args, or only null arguments, eval returns 0.

source filename [arguments]
              Read and execute commands from filename  in  the  current  shell
              environment  and return the exit status of the last command exe
              cuted from filename.  If filename does not contain a slash, file
              names  in  PATH  are used to find the directory containing file
              name.  The file searched for in PATH  need  not  be  executable.
              When  bash  is  not  in  posix  mode,  the  current directory is
              searched if no file is found in PATH.  If the sourcepath  option
              to  the  shopt  builtin  command  is turned off, the PATH is not
              searched.  If any arguments are supplied, they become the  posi
              tional  parameters  when  filename  is  executed.  Otherwise the
              positional parameters are unchanged.  The return status  is  the
              status  of  the  last  command exited within the script (0 if no
              commands are executed), and false if filename is  not  found  or
              cannot be read.

Non ci sono differenze tra i due modi.

C'è solo una nota: evalconcatenati tutti i suoi argomenti, che viene quindi eseguito come un singolo comando. sourcelegge i contenuti di un file e li esegue. evalpuò solo costruire comandi dai suoi argomenti, no stdin. Quindi non puoi fare così:

printf "ls" | eval
  • Qual è più preferito?

Il tuo esempio fornisce lo stesso risultato, ma lo scopo di evaled sourceè diverso. sourceviene generalmente utilizzato per fornire una libreria per altri script, mentre evalviene utilizzato solo per valutare i comandi. evalSe possibile, dovresti evitare di usarlo , perché non esiste alcuna garanzia che la stringa valutata sia pulita; dobbiamo fare alcuni controlli di sanità mentale, usando subshellinvece.

  • Se eseguiamo alcuni comandi tra () o {}, quale è più preferito?

Quando si eseguono comandi di sequenza all'interno di parentesi graffe { }, tutti i comandi vengono eseguiti nella shell corrente , anziché in una shell secondaria (che è il caso se si esegue tra parentesi (consultare il riferimento bash )).

L'utilizzo subshell ( )utilizza più risorse, ma l'ambiente attuale non è interessato. L'utilizzo { }esegue tutti i comandi nella shell corrente, quindi l'ambiente è interessato. A seconda del tuo scopo, puoi sceglierne uno.


2
Penso che tu abbia frainteso la domanda. Certo, non puoi sostituirlo evalcon source. Immagino che la domanda sia: eval "$cmd"equivale a echo "$cmd" | source /dev/stdin. La mia opinione attuale è: si.
Hauke ​​Laging,

3

La differenza principale è che la seconda e la terza forma usano una pipe, che costringerà bash a eseguire il comando "source" in una subshell (a meno che non sia impostato lastpipe, disponibile solo in bash 4.2+), il che lo renderà praticamente equivalente a :

printf "ls" | bash

Le conseguenze sono che qualsiasi variabile d'ambiente impostata dal tuo codice andrà persa, quindi questo non funzionerà come previsto:

printf "abc=2" | source /dev/stdin

Per eseguire i comandi nella shell corrente, è possibile utilizzare la sostituzione del processo:

source <(printf "abc=2")

Puoi inserire più comandi tra parentesi usando il punto e virgola come al solito.

Se elimini la pipa in questo modo, credo che non ci sia alcuna differenza tra l'uso di "eval" e "source". Dovresti preferire quello che è più semplice da usare nel tuo caso particolare:

  • se hai già comandi da eseguire in variabile, usa "eval"
  • se li hai in un file o li ottieni da un comando esterno, usa "source"

0

A complemento delle risposte già fornite:

Un sourceequivalente a ...

comd="ls"
eval "$comd"

... è ...

source <(printf ls)

In caso di lsnon vi è alcuna differenza significativa.

Ma nel caso di un comando che ha lo scopo di influenzare il tuo ambiente attuale (ciò che di solito intendi quando usi source) questa variante lo farebbe (come evalfarebbe anche la tua prima soluzione ) mentre il tuo secondo approccio influenza solo l'ambiente di una subshell che ha vinto ' essere disponibile dopo aver eseguito la riga di codice.

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.