Qual è il comando "eval" in bash?


176

Cosa puoi fare con il evalcomando? Perché è utile È una specie di funzione integrata in bash? Non esiste una manpagina per questo ..


28
Utilizzare type commandper sapere di che tipo è un comando . ( type evalin questo caso)
rozcietrzewiacz,

9
"eval is a shell builtin"
Nuno Rafael Figueiredo,

3
help evalper ottenere la pagina "man" dalla tua shell
m-ric,

eval è un built-in bash ed è documentato nella pagina man di bash. Quindi digita "man bash" e cerca la sezione appropriata per eval. Questo vale anche per altri built-in bash.
Dirk Thannhäuser,

Risposte:


132

evalfa parte di POSIX. È un'interfaccia che può essere una shell integrata.

È descritto nel "Manuale del programmatore POSIX": http://www.unix.com/man-page/posix/1posix/eval/

eval - construct command by concatenating arguments

Prenderà un argomento e ne costruirà un comando, che sarà eseguito dalla shell. Questo è l'esempio della manpage:

1) foo=10 x=foo
2) y='$'$x
3) echo $y
4) $foo
5) eval y='$'$x
6) echo $y
7) 10
  1. Nella prima riga si definisce $foocon il valore '10'e $xcon il valore 'foo'.
  2. Ora definisci $y, che consiste nella stringa '$foo'. Il simbolo del dollaro deve essere sfuggito a '$'.
  3. Per controllare il risultato, echo $y.
  4. Il risultato sarà la stringa '$foo'
  5. Ora ripetiamo il compito con eval. Per prima cosa valuterà $xla stringa 'foo'. Ora abbiamo la dichiarazione y=$fooche verrà valutata y=10.
  6. Il risultato di echo $yè ora il valore '10'.

Questa è una funzione comune in molte lingue, ad esempio Perl e JavaScript. Dai un'occhiata a perldoc eval per altri esempi: http://perldoc.perl.org/functions/eval.html


4
Nella terminologia della shell, evalè un built-in, non una funzione. In pratica, gli incorporati si comportano in modo molto simile a funzioni che non hanno una definizione in lingua, ma non del tutto (come appare evidente se si è abbastanza contorti da definire una funzione chiamata eval).
Gilles,

grazie, risolto il
problema

4
Qual'è la differenza tra eval e backticks `?
JohnyTex,

5
I backtick sono una scorciatoia per l'esecuzione di un'altra intera shell, il che significa che avrai un altro processo bash / sh figlio in esecuzione all'interno del tuo script.
Aaron R.,

Perché echo $ y (fase 3) riporta foo (10) invece di $ foo? (cioè solo il valore di foo invece della stringa $ + il valore di foo)?
JohnDoea,

60

Sì, evalè un comando interno bash, quindi è descritto nella bashpagina man.

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.

Di solito è usato in combinazione con una sostituzione di comando . Senza un esplicito eval, la shell tenta di eseguire il risultato di una sostituzione di comando, non di valutarlo .

Supponi di voler codificare un equivalente di VAR=value; echo $VAR. Nota la differenza nel modo in cui la shell gestisce gli scritti di echo VAR=value:

  1. andcoz@...:~> $( echo VAR=value )
    bash: VAR=value: command not found
    andcoz@...:~> echo $VAR
    <empty line>

    La shell tenta di eseguire echoe VAR=valuecome due comandi separati. Genera un errore sulla seconda stringa. L'incarico rimane inefficace.

  2. andcoz@...:~> eval $( echo VAR=value )
    andcoz@...:~> echo $VAR
    value
    La shell unisce (concatena) le due stringhe echoe VAR=valueanalizza questa singola unità in base alle regole appropriate ed esegue.

Ultimo ma non meno importante, evalpuò essere un comando molto pericoloso. Qualsiasi input per un evalcomando deve essere attentamente controllato per evitare problemi di sicurezza.


18

evalnon ha una pagina man perché non è un comando esterno separato, ma piuttosto una shell incorporata, che significa un comando interno e conosciuto solo dalla shell ( bash). La parte rilevante della bashpagina man dice:

eval [arg ...]
    The args are read and concatenated together into a single command.  
    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

Inoltre, l'output help evalè:

eval: eval [arg ...]
    Execute arguments as a shell command.

    Combine ARGs into a single string, use the result as input to the shell,
    and execute the resulting commands.

    Exit Status:
    Returns exit status of command or success if command is null.

evalè un comando potente e se hai intenzione di usarlo dovresti stare molto attento a evitare i possibili rischi per la sicurezza che ne derivano.


16

L'istruzione eval dice alla shell di prendere gli argomenti di eval come comando ed eseguirli attraverso la riga di comando. È utile in una situazione come di seguito:

Nel tuo script se stai definendo un comando in una variabile e in seguito vuoi usare quel comando, allora dovresti usare eval:

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >

3
Il commento nella riga 4 del tuo esempio è errato: dovrebbe essere "Il comando precedente non ha funzionato perché bash ha cercato di trovare un comando chiamato ls | more. In altre parole: il nome del comando singolo era composto da nove caratteri, inclusi gli spazi e il simbolo della pipe .
CFI

Ho esattamente lo stesso comportamento che ha riferito. bash --version == GNU bash, versione 3.2.57 (1) -release (x86_64-apple-darwin18)
Alexander Bird

10

Che cos'è eval?

eval è un comando shell che di solito viene implementato come incorporato.

In POSIX è elencato come parte di "2.14. Utilità integrate speciali" alla voce "eval" .
Ciò che significa incorporato è:

Il termine "incorporato" implica che la shell può eseguire direttamente l'utilità e non è necessario cercarla.

Che cosa fa?

In termini semplici: crea una riga di input da analizzare due volte .

Come lo fa?

La shell ha una sequenza di passaggi che segue per "elaborare" una linea. Potresti guardare questa immagine e capire che eval è l'unica linea che sale, di nuovo al passaggio 1, a sinistra. Dalla descrizione POSIX :

2.1 Introduzione alla shell

  1. La shell legge il suo input ....
  2. La shell suddivide l'input in token: parole e operatori
  3. La shell analizza l'input in comandi semplici e composti.
  4. La shell esegue varie espansioni (separatamente) ...
  5. La shell esegue il reindirizzamento e rimuove gli operatori di reindirizzamento e i loro operandi dall'elenco dei parametri.
  6. La shell esegue una funzione, un file eseguibile, un file eseguibile o uno script integrato ...
  7. La shell opzionalmente attende il completamento del comando e raccoglie lo stato di uscita.

Al passaggio 6 verrà eseguito un built-in.
Al passaggio 6 eval fa in modo che la riga elaborata venga rinviata al passaggio 1.
È l'unica condizione in cui la sequenza di esecuzione torna indietro.

Ecco perché dico: con eval una riga di input viene analizzata due volte .

Effetti dell'analisi due volte.

Il primo.

E l'effetto più importante da capire. È una delle conseguenze della prima volta che una linea è soggetta ai sette passaggi della shell mostrati sopra, è la citazione . All'interno del passaggio 4 (espansioni), c'è anche una sequenza di passaggi per eseguire tutte le espansioni , l'ultima delle quali è la rimozione del preventivo :

La rimozione del preventivo deve essere sempre eseguita per ultima.

Quindi, sempre, viene rimosso un livello di offerta.

Secondo.

Come conseguenza di quel primo effetto, parti aggiuntive / diverse della linea vengono esposte all'analisi della shell e a tutti gli altri passaggi.

Esempio.

Indirection.

Ciò consente di eseguire espansioni indirette:

a=b b=c    ;    eval echo \$$a            ### shall produce "c"

Perché? Perché nel primo ciclo, $viene citato il primo .
Come tale, viene ignorato per le espansioni dalla shell.
Il prossimo $con il nome a viene espanso per produrre "b".
Quindi, viene rimosso un livello di offerta, rendendo il primo $non quotato.
Fine del primo ciclo.

È quindi, nel secondo ciclo, che la stringa $bviene letta dalla shell.
Quindi espanso in "c"
e fornito come argomento a echo.

Per "vedere" cosa produrrà eval sul primo loop (da valutare di nuovo), usa echo. O qualsiasi comando / script / programma che mostri chiaramente gli argomenti:

$ a=b b=c
$ eval echo \$$a;
c

Sostituisci eval con l'eco per "vedere" cosa sta succedendo:

$ echo echo \$$a
echo $b

È anche possibile mostrare tutte le "parti" di una linea con:

$ printf '<%s> ' echo \$$a
<echo> <$b>

Che, in questo esempio, è solo un'eco e una variabile, ma ricordalo per aiutare a valutare casi più complessi.

Una correzione.

Va detto che: c'è un errore nel codice sopra, riesci a vederlo ?.
Semplice: mancano alcune citazioni.

Come? potresti chiedere. Semplice, cambiamo le variabili (non il codice):

$ a=b b="hi     jk"
$ eval echo \$$a
hi jk

Vedi gli spazi mancanti?
Questo perché il valore all'interno è $bstato diviso dalla shell.

Se ciò non ti convince, prova questo:

$ a=b b="hi  *  jk"
$ eval echo \$$a              ### warning this will expand to the list  
                              ### of all files in the present directory.

Perché?

Citazioni mancanti. Per farlo funzionare correttamente (aggiungi virgolette interne "$a"ed esterne \").
Prova questo (è perfettamente sicuro):

$ a=b b="hi      *       jk"
$ eval echo \" \$"$a" \"
hi      *       jk

Informazioni sul manuale:

Non esiste una pagina man per questo ...

No, non esiste una pagina man indipendente per questo. La ricerca del manuale con man -f evalo addirittura apropos evalnon mostra alcuna voce.

È incluso all'interno man bash. Come qualsiasi altro built-in.
Cerca "SHELL BUILTIN COMMANDS" e quindi "eval".

Un modo più semplice per ottenere aiuto è: In bash, potresti help evalvedere l'aiuto per il built-in.

Perché eval si chiama male?

Perché sta legando il testo al codice in modo dinamico.

In altre parole: converte l'elenco dei suoi argomenti (e / o espansioni di tali argomenti) in una riga eseguita. Se per qualsiasi motivo è stato impostato un argomento da un utente malintenzionato, eseguirai il codice dell'attaccante.

O ancora più semplice, con eval stai dicendo a chiunque abbia definito il valore di uno o più argomenti:

Dai, siediti qui e digita qualsiasi riga di comando, la eseguirò con i miei poteri.

È pericoloso? Dovrebbe essere chiaro per tutti che lo è.

La regola di sicurezza per eval dovrebbe essere:
Esegui eval solo su variabili a cui hai dato il suo valore.

Leggi maggiori dettagli qui .


10

evalè una caratteristica della maggior parte delle lingue interpretati ( TCL, python, ruby...), non solo conchiglie. Viene utilizzato per valutare il codice in modo dinamico.

Nelle shell, è implementato come comando incorporato della shell.

Fondamentalmente, evalprende una stringa come argomento e valuta / interpreta il codice in essa contenuto. Nelle shell, evalpuò accettare più di un argomento, ma evalconcatena semplicemente quelli per formare la stringa da valutare.

È molto potente perché puoi costruire codice in modo dinamico ed eseguirlo, cosa che non puoi fare in linguaggi compilati come C.

Piace:

varname=$1 varvalue=$2
eval "$varname=\$varvalue" # evaluate a string like "foo=$varvalue"
                           # which in Bourne-like shell language
                           # is a variable assignment.

Ma è anche pericoloso in quanto è importante disinfettare le parti dinamiche (fornite esternamente) di ciò che viene passato proprio evalper il motivo per cui viene interpretato come codice shell.

Ad esempio, sopra if $1is evil-command; var, evalfinirebbe per valutare il evil-command; var=$varvaluecodice della shell e quindi eseguirlo evil-command.

La malvagità di evalè spesso esagerata.

OK, è pericoloso, ma almeno sappiamo che è pericoloso.

Un sacco di altri comandi valuterà codice shell nei suoi argomenti, se non sterilizzata, come (a seconda della shell), [alias test, export, printf, GNU sed, awke, naturalmente, sh/ bash/ perle tutti gli interpreti ...

Esempi (qui utilizzando unamecome evil-commande $acome previsto esternamente dati unsanitized):

$ a='$(uname>&2)' sh -c 'eval "echo $a"'
Linux

$ a='x[0$(uname>&2)]' mksh -c 'export "$a=$b"'
Linux
$ a='x[0$(uname>&2)]' ksh93 -c 'printf "%d\n" "$a"'
Linux
0
$ a='x[0$(uname>&2)]' ksh93 -c '[ "$a" -gt 0 ]'
Linux
$ a=$'bar/g;e uname>&2\n;s//'; echo foo | sed "s/foo/$a/g"
Linux
bar
$ a='";system("uname");"'; awk "BEGIN{print \"$a\"}"

Linux
$ a=';uname'; sh -c "echo $a"

Linux

Quelli sed, export... i comandi potrebbero essere considerati più pericolosi perché, sebbene sia ovvio eval "$var", il contenuto di $varverrà valutato come codice shell, non è così ovvio con sed "s/foo/$var/"o export "$var=value"o [ "$var" -gt 0 ]. La pericolosità è la stessa, ma è nascosta in quegli altri comandi.


@BinaryZebra, sedcome evalse fosse passata una stringa contenuta in una variabile, e per entrambi, il contenuto di quella stringa finisce per essere valutato come codice shell, quindi sedè pericoloso come eval, è quello che sto dicendo. sedviene passata una stringa contenente uname(uname non è stato eseguito finora) e tramite l'invocazione di sed, il comando uname finisce per essere eseguito. Come per eval. In sed 's/foo/$a/g', non stai passando dati non autorizzati sed, non è di questo che stiamo parlando qui.
Stéphane Chazelas,

2

Questo esempio potrebbe far luce:

#!/bin/bash
VAR1=25
VAR2='$VAR1'
VAR3='$VAR2'
echo "$VAR3"
eval echo "$VAR3"
eval eval echo "$VAR3"

Output dello script sopra:

$VAR2
$VAR1
25

Grazie per il tuo contributo, ma, per quanto siano divertenti, non siamo davvero interessati a compilare un elenco di esempi (leggermente diversi) di utilizzo eval. Credi che ci sia un aspetto fondamentale e critico della funzionalità evalche non è coperto dalle risposte esistenti? In tal caso, spiegalo e usa l'esempio per illustrare la tua spiegazione. Si prega di non rispondere nei commenti; modifica la  tua risposta per renderla più chiara e completa.
Scott,
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.