Cosa puoi fare con il eval
comando? Perché è utile È una specie di funzione integrata in bash? Non esiste una man
pagina per questo ..
help eval
per ottenere la pagina "man" dalla tua shell
Cosa puoi fare con il eval
comando? Perché è utile È una specie di funzione integrata in bash? Non esiste una man
pagina per questo ..
help eval
per ottenere la pagina "man" dalla tua shell
Risposte:
eval
fa 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
$foo
con il valore '10'
e $x
con il valore 'foo'
.$y
, che consiste nella stringa '$foo'
. Il simbolo del dollaro deve essere sfuggito a '$'
.echo $y
.'$foo'
eval
. Per prima cosa valuterà $x
la stringa 'foo'
. Ora abbiamo la dichiarazione y=$foo
che verrà valutata y=10
.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
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
).
Sì, eval
è un comando interno bash, quindi è descritto nella bash
pagina 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
:
andcoz@...:~> $( echo VAR=value )
bash: VAR=value: command not found
andcoz@...:~> echo $VAR
<empty line>
La shell tenta di eseguire echo
e VAR=value
come due comandi separati. Genera un errore sulla seconda stringa. L'incarico rimane inefficace.
andcoz@...:~> eval $( echo VAR=value )
andcoz@...:~> echo $VAR
value
La shell unisce (concatena) le due stringhe echo
e VAR=value
analizza questa singola unità in base alle regole appropriate ed esegue.Ultimo ma non meno importante, eval
può essere un comando molto pericoloso. Qualsiasi input per un eval
comando deve essere attentamente controllato per evitare problemi di sicurezza.
eval
non 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 bash
pagina 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.
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 >
ls | more
. In altre parole: il nome del comando singolo era composto da nove caratteri, inclusi gli spazi e il simbolo della pipe .
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.
In termini semplici: crea una riga di input da analizzare due volte .
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
- La shell legge il suo input ....
- La shell suddivide l'input in token: parole e operatori
- La shell analizza l'input in comandi semplici e composti.
- La shell esegue varie espansioni (separatamente) ...
- La shell esegue il reindirizzamento e rimuove gli operatori di reindirizzamento e i loro operandi dall'elenco dei parametri.
- La shell esegue una funzione, un file eseguibile, un file eseguibile o uno script integrato ...
- 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 .
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.
Come conseguenza di quel primo effetto, parti aggiuntive / diverse della linea vengono esposte all'analisi della shell e a tutti gli altri passaggi.
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 $b
viene 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.
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 è $b
stato 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.
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
Non esiste una pagina man per questo ...
No, non esiste una pagina man indipendente per questo. La ricerca del manuale con man -f eval
o addirittura apropos eval
non 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 eval
vedere l'aiuto per il built-in.
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 .
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, eval
prende una stringa come argomento e valuta / interpreta il codice in essa contenuto. Nelle shell, eval
può accettare più di un argomento, ma eval
concatena 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 eval
per il motivo per cui viene interpretato come codice shell.
Ad esempio, sopra if $1
is evil-command; var
, eval
finirebbe per valutare il evil-command; var=$varvalue
codice 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
, awk
e, naturalmente, sh
/ bash
/ perl
e tutti gli interpreti ...
Esempi (qui utilizzando uname
come evil-command
e $a
come 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 $var
verrà 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.
sed
come eval
se 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. sed
viene 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.
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
eval
. Credi che ci sia un aspetto fondamentale e critico della funzionalità eval
che 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.
type command
per sapere di che tipo è un comando . (type eval
in questo caso)