Espansione della variabile all’interno di virgolette singole in un comando in Bash

Voglio eseguire un comando da uno script di shell bash che ha virgolette singole e alcuni altri comandi all’interno delle virgolette singole e una variabile.

es. repo forall -c '....$variable'

In questo formato, $ viene salvato e la variabile non viene espansa.

Ho provato le seguenti varianti ma sono state rifiutate:

 repo forall -c '...."$variable" ' repo forall -c " '....$variable' " " repo forall -c '....$variable' " repo forall -c "'" ....$variable "'" 

Se sostituisco il valore al posto della variabile, il comando viene eseguito correttamente.

Per favore dimmi dove sto andando male.

All’interno delle virgolette singole tutto è conservato alla lettera, senza eccezioni.

Ciò significa che devi chiudere le virgolette, inserire qualcosa e quindi reinserire di nuovo.

 'before'"$variable"'after' 'before'"'"'after' 'before'\''after' 

Come puoi verificare, ciascuna delle righe precedenti è una singola parola per la shell. La concatenazione delle stringhe è fatta semplicemente dalla giustapposizione. Le virgolette (virgolette singole o doppie, a seconda della situazione) vengono utilizzate per disabilitare l’interpretazione di vari caratteri speciali, come spazi bianchi, $ ; … Per un buon tutorial sulle citazioni vedi la risposta di Mark Reed. Rilevante anche: quali caratteri devono essere sfuggiti in bash?

Non concatenare stringhe interpretate da una shell

Dovresti assolutamente evitare di build comandi di shell concatenando le variabili. Questa è una ctriggers idea simile alla concatenazione di frammenti SQL (SQL injection!).

Di solito è ansible avere segnaposti nel comando e fornire il comando insieme alle variabili in modo che il destinatario possa riceverli dall’elenco degli argomenti di chiamata.

Ad esempio, il seguente è molto pericoloso. NON FARE QUESTO

 script="echo \"Argument 1 is: $myvar\"" /bin/sh -c "$script" 

Se il contenuto di $myvar non è $myvar , ecco un exploit:

 myvar='foo"; echo "you were hacked' 

Invece di invocare sopra, usa argomenti posizionali. La seguente invocazione è migliore – non è sfruttabile:

 script='echo "arg 1 is: $1"' /bin/sh -c "$script" -- "$myvar" 

Nota l’uso delle singole zecche nell’assegnazione allo script , il che significa che è preso alla lettera, senza espansione variabile o qualsiasi altra forma di interpretazione.

Il comando repo non può interessare che tipo di citazioni ottiene. Se è necessaria l’espansione dei parametri, utilizzare le virgolette doppie. Se questo significa che si finisce per dover rovesciare un sacco di roba, usare le virgolette singole per la maggior parte di esso, e poi uscire da esse e andare in doppio per la parte in cui è necessario che l’espansione avvenga.

 repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff' 

La spiegazione segue, se sei interessato.

Quando si esegue un comando dalla shell, ciò che tale comando riceve come argomenti è un array di stringhe con terminazione null. Queste stringhe possono contenere assolutamente qualsiasi carattere non nullo.

Ma quando la shell sta costruendo quella serie di stringhe da una riga di comando, interpreta alcuni caratteri in modo speciale; questo è progettato per rendere i comandi più facili (anzi, possibili) da digitare. Ad esempio, gli spazi normalmente indicano il limite tra le stringhe nella matrice; per questo motivo, gli argomenti individuali sono talvolta chiamati “parole”. Ma un argomento può tuttavia contenere degli spazi; hai solo bisogno di un modo per dire alla shell che è quello che vuoi.

Puoi usare una barra rovesciata davanti a qualsiasi carattere (incluso lo spazio, o un’altra barra rovesciata) per dire alla shell di trattare quel personaggio letteralmente. Ma mentre puoi fare qualcosa del genere:

echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

… può diventare noioso Quindi la shell offre un’alternativa: le virgolette. Questi sono disponibili in due varietà principali.

Le virgolette doppie sono chiamate “virgolette”. Impediscono che i caratteri jolly e gli alias vengano espansi, ma per lo più servono per includere spazi in una parola. Altre cose come l’espansione di parametri e comandi (il tipo di cosa segnalata da un $ ) si verificano ancora. E ovviamente se vuoi una doppia citazione letterale tra virgolette, devi rovesciarla:

echo "\"Thank you. That'll be \$4.96, please,\" said the cashier"

Le virgolette singole sono più draconiane. Tutto tra di loro è preso completamente alla lettera, compresi i backslash. Non c’è assolutamente alcun modo di ottenere una singola citazione letterale tra virgolette singole.

Fortunatamente, le virgolette nella shell non sono delimitatori di parole ; da soli, non terminano una parola. Puoi entrare e uscire tra virgolette (o tra diversi tipi di virgolette) all’interno della stessa parola per ottenere il risultato desiderato:

echo '"Thank you. That'\''ll be $4.96, please," said the cashier'

In questo modo è più semplice – molti meno backslash, anche se la sequenza single-quote, backslashed-letteral-single-quote, open-single-quote richiede un po ‘di tempo per abituarsi.

Le moderne shell hanno aggiunto un altro stile di quotatura non specificato dallo standard POSIX, in cui la virgoletta singola iniziale è preceduta dal simbolo del dollaro. Le stringhe così citate seguono convenzioni simili a stringhe letterali nel linguaggio di programmazione ANSI C e quindi sono talvolta denominate “stringhe ANSI” e $'' coppia” quotazioni ANSI “. All’interno di tali stringhe, i consigli sopra riportati sui backslash non vengono più applicati letteralmente. Invece, diventano di nuovo speciali – non solo puoi includere una singola virgoletta letterale o una barra rovesciata anteponendoti una barra rovesciata, ma la shell espande anche gli escape dei caratteri ANSI C (come \n per una nuova riga, \t per la scheda e \xHH per il personaggio con codice esadecimale HH ). Altrimenti, tuttavia, si comportano come stringhe con quotatura singola: nessun parametro o sostituzione di comando ha luogo:

 echo $'"Thank you. That\'ll be $4.96, please," said the cashier' 

La cosa importante da notare è che la singola stringa ricevuta come argomento del comando echo è esattamente la stessa in tutti questi esempi. Dopo che la shell ha eseguito l’analisi di una riga di comando, non è ansible eseguire il comando per indicare cosa è stato indicato come. Anche se lo volesse.

EDIT: (Come da commenti in questione 🙂

Da allora ho cercato questo. Ho avuto la fortuna di avere un repo in giro. Tuttavia non è chiaro per me se è necessario includere i comandi tra virgolette singole con la forza. Ho esaminato la syntax del repository e non penso che sia necessario. È ansible utilizzare virgolette doppie attorno al comando e quindi utilizzare qualsiasi virgoletta singola e doppia di cui si ha bisogno all’interno, a condizione di sfuggire a quelle doppie.

Di seguito è ciò che ha funzionato per me –

QUOTE="'" hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"

Per te funziona?

 eval repo forall -c '....$variable' 

Le variabili possono contenere virgolette singole.

 myvar=\'....$variable\' repo forall -c $myvar