2012-10-08 16 views
5

Tutti dicono che eval è malvagio, e dovresti usare $() come sostituto. Ma mi sono imbattuto in una situazione in cui lo smascheramento non è gestito all'interno di $().

Lo sfondo è che sono stato bruciato troppo spesso da percorsi di file con spazi al loro interno, e così mi piace citare tutti questi percorsi. Più paranoia sul voler sapere da dove provengono tutti i miei eseguibili. Ancora più paranoico, non fidandosi di me stesso e così come essere in grado di visualizzare i comandi creati che sto per correre.

Qui di seguito cerco variazioni sul usando eval vs $(), e se il nome del comando è citato (cuz potrebbe contenere spazi)

BIN_LS="/bin/ls" 
    thefile="arf" 
    thecmd="\"${BIN_LS}\" -ld -- \"${thefile}\"" 

    echo -e "\n Running command '${thecmd}'" 
    $($thecmd) 

      Running command '"/bin/ls" -ld -- "arf"' 
     ./foo.sh: line 8: "/bin/ls": No such file or directory 

    echo -e "\n Eval'ing command '${thecmd}'" 
    eval $thecmd 

      Eval'ing command '"/bin/ls" -ld -- "arf"' 
     /bin/ls: cannot access arf: No such file or directory 

    thecmd="${BIN_LS} -ld -- \"${thefile}\"" 

    echo -e "\n Running command '${thecmd}'" 
    $($thecmd) 

      Running command '/bin/ls -ld -- "arf"' 
     /bin/ls: cannot access "arf": No such file or directory 

    echo -e "\n Eval'ing command '${thecmd}'" 
    eval $thecmd 

      Eval'ing command '/bin/ls -ld -- "arf"' 
     /bin/ls: cannot access arf: No such file or directory 

    $("/bin/ls" -ld -- "${thefile}") 

     /bin/ls: cannot access arf: No such file or directory 

Quindi ... questo è fonte di confusione. Un percorso di comando quotato è valido ovunque tranne all'interno di un costrutto $()? A breve, ad esempio più diretto:

$ c="\"/bin/ls\" arf" 
$ $($c) 
-bash: "/bin/ls": No such file or directory 
$ eval $c 
/bin/ls: cannot access arf: No such file or directory 
$ $("/bin/ls" arf) 
/bin/ls: cannot access arf: No such file or directory 
$ "/bin/ls" arf 
/bin/ls: cannot access arf: No such file or directory 

Come si spiega il semplice $($c) caso?

risposta

3

Con il fatto che non ha senso in primo luogo. Use an array instead.

$ c=("/bin/ls" arf) 
$ "${c[@]}" 
/bin/ls: cannot access arf: No such file or directory 
+0

Le stringhe non quotate vengono valutate immediatamente. c = ("/ bin/ls"/bin/l *) fa qualcosa di molto diverso dal voluto. (guarda con echo "'$ {c [@]}'") c = ("/ bin/ls" "/ bin/l *") allora z = $ ("$ {c [@]}") fallisce. Un array di argomenti non può essere usato bene con $()? – Shenme

+1

Tuttavia, c = ("/ bin/ls" "/ bin/l *") allora z = $ ($ {c [@]}) sembra funzionare bene. È questa la forma eseguibile $() che intendevi mostrarmi? – Shenme

5

L'uso di " per citare le parole fa parte della vostra interazione con Bash. Quando si digita

$ "/bin/ls" arf 

al prompt, o in uno script, stai dicendo Bash che il comando è composta dalle parole /bin/ls e arf, e le virgolette sono realmente sottolineando che /bin/ls è una sola parola.

Quando si digita

$ eval '"/bin/ls" arf' 

stai dicendo Bash che il comando è composta dalle parole eval e "/bin/ls" arf. Poiché lo scopo del eval è fingere che il suo argomento è un comando effettivo umano-ingresso, questo è equivalente alla corsa

$ "/bin/ls" arf 

e " viene processato come quando richiesto.

Si noti che questa simulazione è specifica per eval; Bash di solito non fa finta di credere che qualcosa fosse un vero comando umano.

Quando si digita

$ c='"/bin/ls" arf' 
$ $c 

il $c ottiene sostituito, e poi subisce suddivisione delle parole (vedi §3.5.7 "Word Splitting" in the Bash Reference Manual), così le parole del comando sono "/bin/ls" (notare le virgolette!) E arf. Inutile dire che questo non funziona. (Inoltre non è molto sicuro, poiché oltre alla suddivisione in parole, $c subisce anche l'espansione dei nomi dei file e quant'altro. Generalmente le espansioni dei parametri dovrebbero sempre essere tra virgolette doppie e, se non possono essere, dovresti riscrivere le tue codice in modo che possano essere. Le espansioni parametriche non quotate richiedono problemi.)

Quando si digita

$ c='"/bin/ls" arf' 
$ $($c) 

questo è la stessa di prima, solo che ora si sta anche cercando di utilizzare l'output del comando non lavorativo come un nuovo comando . Inutile dire che ciò non fa funzionare improvvisamente il comando non lavorativo.

Come Ignacio Vazquez-Abrams dice nella sua risposta, la soluzione è quella di utilizzare una matrice, e gestire il citare correttamente:

$ c=("/bin/ls" arf) 
$ "${c[@]}" 

che imposta c ad un array con due elementi, /bin/ls e arf, e usa questi due elementi come parola di un comando.

+0

(odio il limite di 5 min) L'ultima riga non mostra l'esecuzione di un comando impacchettato in un modulo in cui è possibile acquisire l'output (che tuttavia non è stato detto nella domanda originale) Prova c = ("/ bin/ls" "/ bin/l * ") e c = ("/bin/ls "/ bin/l *) con echo" '$ {c [@]}' ". Questo metodo non mi permette di costruire un array con argomenti quotati per un uso successivo con $() ...? – Shenme

+1

@Shenme: Per quanto posso dire, quello che stai dicendo è, "Voglio usare' eval', perché voglio elaborare una stringa arbitraria come un comando di Bash. " Quello che dicono tutti gli altri è, "eval' è male, perché elabora una stringa arbitraria come un comando di Bash."Stai cercando di riconciliare questi punti di vista chiedendo:" Esiste un modo * non * -evil per fare questa cosa cattiva? ", Ma ciò non funzionerà.I punti di vista sono fondamentalmente inconciliabili.Per fare una cosa cattiva, usa uno strumento malvagio o, per evitare lo strumento malvagio, riscrivi la tua sceneggiatura per evitare di fare la cosa cattiva – ruakh

+0

No, nessun comando arbitrario, nessuna intenzione malvagia. :-) Volevo solo creare un comando ed eseguirlo in modo controllato Controllerò gli input e sono persino abbastanza paranoico da usare '-'. Le persone vedono le risposte semplicistiche a complesse meta-domande, e ti criticano se tu "usi" la valutazione, quindi volevo evitare le critiche. - (Ad ogni modo, z = $ ($ {c [@]}) funziona bene – Shenme

2

Dal man page for bash, per quanto riguarda eval:

eval [arg ...]: I args sono letti e concatenati insieme in un unico comando. Questo comando viene quindi letto ed eseguito dalla shell e lo stato di uscita viene restituito come valore di eval.

Quando c è definito come "\"/bin/ls\" arf", le quotazioni esterne causeranno l'intera cosa da trattare come primo argomento a eval, che dovrebbe essere un comando o un programma. È necessario passare gli argomenti eval in modo tale che il comando di destinazione e i relativi argomenti siano elencati separatamente.

Il costrutto $(...) si comporta diversamente da eval perché non è un comando che accetta argomenti. Può elaborare l'intero comando contemporaneamente anziché elaborare gli argomenti uno alla volta.

Una nota sulla premessa originale: il motivo principale per cui la gente dice che eval è malvagio era perché è comunemente usato dagli script per eseguire una stringa fornita dall'utente come comando di shell. A volte utile, questo è un problema di sicurezza principale (in genere non esiste un modo pratico per controllare la stringa di sicurezza prima di eseguirla). Il problema di sicurezza non si applica se si utilizza eval su stringhe codificate all'interno dello script, come si sta facendo. Tuttavia, in genere è più semplice e più pulito utilizzare $(...) o `...` all'interno di script per la sostituzione di comando, lasciando nessun caso di utilizzo reale rimasto per eval.

+0

Suggerimento aggiuntivo: quando si prova a vedere come viene visualizzato un comando espanso dalla shell, usando 'set -v' a volte si ottengono risultati più accurati di' echo'. – bta

+0

quindi, in pratica, 'eval' è sicuro se la stringa valutata non è esposta in alcun modo all'input dell'utente; ma se falliamo in qualsiasi momento a impedirlo, può essere sfruttato; quindi usare '' ... '' o '$ (...)' richiede meno precauzioni rispetto a 'eval'; l'uomo! questo era il consiglio specifico che stavo cercando prima di cambiare i miei script hehe! :) –

Problemi correlati