Eseguire un comando memorizzato in una variabile


11

Ho un comando memorizzato in una variabile. Facciamo finta che la variabile $iabbia il valore:

cat -nT index.php |grep 'someregex'

Quando provo a eseguire la variabile sopra digitandola, $inon riesce perché la shell tenta di eseguire l'intera variabile come un comando. Ho anche provato a usare eval($i)e inserire i $ibacktick.

Come posso fare eseguire la shell $icome se fosse un comando? E perché non funziona come.

$i='echo hi'; $i

È perché ho dovuto fare una specie di hacking tra virgolette singole? (Perché non puoi nidificarli.) Attualmente la mia soluzione è

echo $i > /foo;  . /foo

Ma non voglio creare un file solo per questo, ma solo per cancellarlo in seguito.

Quello che intendo con "Ho hackerato tra virgolette singole è che ho fatto:

$i='cat index.php | grep -P '"'"'MYREGEXHERE'"'"

4
La domanda è: perché il comando è memorizzato in una variabile in primo luogo? Faresti meglio a assemblare il comando quando lo esegui, archiviando gli argomenti delle opzioni in variabili o in un array. Puoi mostrare l'intero script che hai?
slhck,

Risposte:


18

Risposta breve: vedi BashAQ # 50: sto cercando di inserire un comando in una variabile, ma i casi complessi falliscono sempre! .

Risposta lunga: è a causa dell'ordine in cui bash analizza la riga di comando. In particolare, cerca elementi come pipe e reindirizzamenti prima di espandere i valori variabili e non torna indietro e cerca nuovamente pipe ecc. Nei valori espansi. In sostanza, le variabili vengono sostituite a metà del processo di analisi, quindi il valore viene analizzato solo a metà prima di essere eseguito.

Per risolvere questo problema, devi rispondere alla domanda di @ slhck: in primo luogo perché il comando è memorizzato in una variabile? Qual è il problema reale che stai cercando di risolvere? A seconda dell'obiettivo effettivo, esistono diverse soluzioni possibili:

  • Non memorizzarlo in una variabile, eseguilo direttamente. Memorizzare i comandi per un uso successivo è complicato e, se non è necessario, non farlo.

  • Utilizzare una funzione anziché una variabile. Ecco a cosa servono, dopo tutto:

    i() { cat -nT index.php |grep 'someregex'; }
    i

    Lo svantaggio principale di ciò è che non è possibile creare dinamicamente la funzione - non è possibile includere o escludere condizionalmente il codice dalla funzione (sebbene la funzione stessa possa avere elementi condizionali che vengono selezionati quando viene eseguita).

  • Usa eval. Questo dovrebbe essere considerato l'ultima risorsa, dal momento che è facile ottenere comportamenti imprevisti. Esegue essenzialmente il comando attraverso un altro passaggio di analisi completo, quindi tutte le pipe ecc. Ottengono il loro pieno significato - ma significa anche che parti del comando che pensavi fossero solo dati verranno analizzate e forse eseguite. I nomi di file che contengono metacaratteri di shell (pipe, punto e virgola, virgolette / apostrofo, ecc.) Possono avere effetti strani e talvolta pericolosi. Se lo usi eval, almeno doppia virgoletta sulla stringa, altrimenti il ​​suo contenuto viene essenzialmente analizzato una volta e mezza, con risultati ancora più strani.

    i="cat -nT index.php |grep 'someregex'"
    eval "$i"

EDIT: un altro approccio standard store-a-command-in-a-variabile è l'uso di un array invece di una semplice variabile. Ciò consente di memorizzare un comando con argomenti complessi (ad esempio contenenti spazi) senza problemi, ma non memorizzerà cose come pipe e reindirizzamenti. Pertanto, l'approccio array non sarà utile in questo caso particolare.


+1 per il evalmetodo. Questo mi ha impedito di porre la stessa domanda :). Ho qualcosa del genere sudo rsync -aAHXi -n --delete-excluded --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/lost+found","/mnt/DATA/*","/var/log/*","/var/swap","/var/cache/apt/archives/*.deb"} -e ssh root@piac_wireless:/ /home/mrx/Docs/RPi/backup/piac/piac_usb-rootche non stava eseguendo correttamente le esclusioni.
dentice

@dentex Consiglio vivamente di non utilizzarlo evalper questo - ci sono troppi modi per farlo andare storto. Un array sarebbe un modo migliore per farlo. Vedi qui , qui e qui per esempi.
Gordon Davisson,

Grazie per il suggerimento Proverò a implementare la soluzione con l'array, al fine di passare l'elenco di esclusione a rsync.
dentex,

4

Questo dovrebbe funzionare solo:

a="cat -nT index.php |grep 'someregex'"
eval "$a"

Attenzione che evalintroduce potenziali vulnerabilità e situazioni di comportamento inaspettato, quindi deve essere usato, se mai, con particolare attenzione.


Se lo usi eval, devi davvero usare le virgolette doppie ( eval "$i") per evitare effetti extra extra-strani. Ad esempio, prova questo con a="cat -nT index.php |grep ' .* '"(cioè il regex dovrebbe corrispondere a due spazi con qualsiasi sequenza di caratteri tra loro) e vedere cosa succede realmente. Per ulteriore credito, spiega perché succede.
Gordon Davisson,

Aggiunte doppie virgolette @GordonDavisson.
jlliagre,

2

Quando dichiari la variabile, non dovresti usare il simbolo del dollaro come prefisso.

Prova questo:

i='echo hi'; $i

1
È corretto, ma in realtà non è una risposta alla domanda del PO.
slhck,

è per me REALY: P
nwgat
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.