La risposta di eplawless risolve in modo semplice ed efficace il suo problema specifico: sostituisce tutte le "istanze dell'intero elenco di argomenti con \", che è il modo in cui Bash richiede che le virgolette doppie all'interno di una stringa tra virgolette siano rappresentate.
Per rispondere in generale alla domanda su come evitare le virgolette doppie all'interno di una stringa tra virgolette utilizzandocmd.exe , l'interprete della riga di comando di Windows (sia sulla riga di comando - spesso ancora erroneamente chiamato "prompt DOS" - o in un file batch): Vedi in basso per uno sguardo a PowerShell .
tl; dr :
È necessario utilizzare"" quando si passa una stringa a un (altro) file batch e si può utilizzare ""con applicazioni create con i compilatori C / C ++ /. NET di Microsoft (che accettano anche\" ), che su Windows include Python e Node.js :
\"è richiesto - come unica opzione - da molti altri programmi , (ad esempio, Ruby, Perl e persino Windows PowerShell di Microsoft (!)), ma IL SUO UTILIZZO NON È SICURO :
\"è ciò che richiedono molti eseguibili e interpreti, incluso Windows PowerShell, quando vengono passate stringhe dall'esterno o, nel caso dei compilatori Microsoft, il supporto come alternativa a "" : in definitiva, però, spetta al programma di destinazione analizzare l'elenco degli argomenti .
- Esempio:
foo.exe "We had 3\" of rain."
- TUTTAVIA, L'USO DI
\"PU RISULTARE IN ESECUZIONE NON DESIDERATA ARBITRARIA DI COMANDI e / o RIDIREZIONI INGRESSO / USCITA :
- I seguenti caratteri presentano questo rischio:
& | < >
- Ad esempio, quanto segue determina l'esecuzione involontaria del
vercomando; vedere più avanti per una spiegazione e il prossimo punto elenco per una soluzione alternativa:
foo.exe "3\" of snow" "& ver."
- Per Windows PowerShell ,
\""e "^""sono robusti, ma le alternative limitate (vedi sezione "Richiamo CLI di PowerShell ..." di seguito).
Se è necessario utilizzare \", ci sono solo 3 approcci sicuri , che sono però abbastanza ingombranti : Punta di cappello a TS per il suo aiuto.
Utilizzando l' espansione ritardata della variabile (possibilmente selettiva ) nel file batch, è possibile memorizzare il valore letterale \"in una variabile e fare riferimento a tale variabile all'interno di una "..."stringa utilizzando la !var!sintassi - vedere la risposta utile di TS .
- L'approccio di cui sopra, nonostante sia ingombrante, ha il vantaggio di poterlo applicare metodicamente e che funziona in modo robusto , con qualsiasi input.
Solo con le stringhe LITERALI - quelle che NON coinvolgono VARIABILI - ottieni un approccio metodico simile: categoricamente ^-escape tutti i cmd.exe metacaratteri: " & | < > e - se vuoi anche sopprimere l'espansione delle variabili - %:
foo.exe ^"3\^" of snow^" ^"^& ver.^"
In caso contrario, è necessario formulare la stringa in base al riconoscimento di quali parti della stringa cmd.execonsiderano non quotate a causa di un'interpretazione errata\" come delimitatori di chiusura:
in porzioni letterali contenenti metacaratteri di shell: ^-escape da loro; usando l'esempio sopra, è &che deve essere ^evitato:
foo.exe "3\" of snow" "^& ver."
in porzioni con %...%riferimenti a variabili in stile : assicurati checmd.exe considerarli parte di una "..."stringa e che i valori delle variabili non abbiano essi stessi virgolette incorporate e sbilanciate, il che non è nemmeno sempre possibile .
Per informazioni di base, continua a leggere.
sfondo
Nota: questo si basa sui miei esperimenti. Fammi sapere se sbaglio.
Shell simili a POSIX come Bash su sistemi Unix tokenizzano l'elenco di argomenti (stringa) prima di passare gli argomenti individualmente al programma di destinazione: tra le altre espansioni, suddividono l'elenco di argomenti in singole parole (suddivisione di parole) e rimuovono parole risultanti (rimozione delle virgolette). Al programma di destinazione viene consegnato un array di singoli argomenti , con le virgolette sintattiche rimosse .
Al contrario, l'interprete dei comandi di Windows apparentemente non tokenizza l'elenco degli argomenti e passa semplicemente la singola stringa che comprende tutti gli argomenti, comprese le virgolette. - al programma target.
Tuttavia, prima che la singola stringa venga passata al programma di destinazione , viene eseguita una preelaborazione: ^caratteri di escape. le stringhe al di fuori delle virgolette doppie vengono rimosse (escono dal carattere seguente) e i riferimenti alle variabili (ad esempio %USERNAME%) vengono interpolati per primi.
Quindi, a differenza di Unix, è responsabilità del programma di destinazione analizzare per analizzare la stringa degli argomenti e scomporla in singoli argomenti senza virgolette. Pertanto, programmi diversi possono ipoteticamente richiedere metodi di escape diversi e non esiste un unico meccanismo di escape garantito per funzionare con tutti i programmi : https://stackoverflow.com/a/4094897/45375 contiene un eccellente background sull'anarchia che è la riga di comando di Windows parsing.
In pratica, \"è molto comune, ma NON SICURO , come detto sopra:
Dal momento che di per cmd.exesé non si riconosce \"come virgolette doppie con escape , può interpretare erroneamente i token successivi sulla riga di comando come non quotati e potenzialmente interpretarli come comandi e / o reindirizzamenti di input / output .
In poche parole: il problema emerge, se uno qualsiasi dei seguenti caratteri segue un'apertura o è sbilanciato \" :& | < > ; per esempio:
foo.exe "3\" of snow" "& ver."
cmd.exevede i seguenti token, risultanti da \"un'interpretazione errata come virgolette doppie regolari:
"3\"
of
snow" "
- riposo:
& ver.
Poiché cmd.exepensa che non& ver. sia quotato , lo interpreta come &(l'operatore di sequenziamento dei comandi), seguito dal nome di un comando da eseguire ( ver.- il .viene ignorato;ver riporta cmd.exele informazioni sulla versione).
L'effetto complessivo è:
- Primo,
foo.exe viene richiamato solo con i primi 3 gettoni.
- Quindi, comando
ver viene eseguito.
Anche nei casi in cui il comando accidentale non danneggia, il tuo comando generale non funzionerà come previsto, dato che non tutti gli argomenti vengono passati ad esso.
Molti compilatori / interpreti riconoscono SOLO\" , ad esempio, il compilatore GNU C / C ++, Python, Perl, Ruby, persino Windows PowerShell di Microsoft quando viene richiamato da cmd.exe- e, tranne (con limitazioni) per Windows PowerShell con \"", per loro non esiste una soluzione semplice a questo problema.
In sostanza, dovresti sapere in anticipo quali parti della tua riga di comando sono interpretate erroneamente come non quotate e selettivamente^ quotate sfuggire a tutte le istanze di & | < >quelle parti.
Al contrario, l' uso di ""è SICURO , ma purtroppo è supportato solo da eseguibili e file batch basati su compilatore Microsoft (nel caso dei file batch, con le stranezze discusse sopra), il che esclude PowerShell - vedere la sezione successiva.
Chiamata della CLI di PowerShell da cmd.exeshell simili a POSIX:
Nota: vedere la sezione inferiore per come vengono gestite le virgolette all'interno di PowerShell.
Quando viene richiamato dall'esterno , ad esempio da cmd.exe, dalla riga di comando o da un file batch:
PowerShell [Core] v6 + ora riconosce correttamente"" (oltre a\"), il che è sia sicuro da usare che preservante gli spazi .
pwsh -c " ""a & c"".length " non si rompe e cede correttamente 6
Windows PowerShell (l'edizione legacy la cui ultima versione è la 5.1) riconosce solo \" e, anche su Windows """e il più robusto \""/"^"" (anche se internamente PowerShell utilizza`come carattere di escape nelle stringhe tra virgolette e accetta anche""- vedere la sezione in basso):
Chiamata a Windows PowerShell dacmd.exe / un file batch:
"" si rompe , perché fondamentalmente non è supportato:
powershell -c " ""ab c"".length " -> errore "Nella stringa manca il terminatore"
\"e """ funzionano in linea di principio , ma non sono sicuri :
powershell -c " \"ab c\".length "funziona come previsto: esce 5(notare i 2 spazi)
- Ma non è sicuro, perché i
cmd.exemetacaratteri interrompono il comando, a meno che non vengano evitati: si
powershell -c " \"a& c\".length " interrompe , a causa di &, che dovrebbe essere evitato come^&
\""è sicuro , ma normalizza gli spazi bianchi interni , il che può essere indesiderato:
powershell -c " \""a& c\"".length "uscite 4(!), perché i 2 spazi sono normalizzati a 1.
"^""è la scelta migliore per Windows PowerShell in particolare , dove è sia sicuro che preserva gli spazi, ma con PowerShell Core (su Windows) è uguale alla \""normalizzazione degli spazi . Il merito va a Venryx per aver scoperto questo approccio.
powershell -c " "^""a& c"^"".length " funziona : non si rompe - nonostante &- e produce 5, cioè, spazi bianchi correttamente conservati.
PowerShell Core : pwsh -c " "^""a& c"^"".length " funziona , ma restituisce 4, cioè normalizza gli spazi , come \""fa.
Su piattaforme simili a Unix (Linux, macOS), quando si chiama la CLI di PowerShell [Core]pwsh , da una shell simile a POSIX comebash :
È necessario utilizzare\" , che, tuttavia, è sia sicuro che preservante gli spazi bianchi :
$ pwsh -c " \"a& c|\".length" # OK: 5
Informazioni correlate
^può essere utilizzato solo come carattere di escape in stringhe non quotate: all'interno di stringhe tra virgolette doppie, ^non è speciale e viene trattato come un valore letterale.
- CAVEAT : l' uso dei
^parametri in passati callall'istruzione è interrotto (questo vale per entrambi gli usi call: invocare un altro file batch o binario e chiamare una subroutine nello stesso file batch):
^le istanze nei valori tra virgolette doppie vengono inspiegabilmente raddoppiate , alterando il valore passato: ad esempio, se la variabile %v%contiene un valore letterale a^b, call :foo "%v%"assegna "a^^b"(!) a %1(il primo parametro) nella subroutine :foo.
- L' uso non quotato di
^with callè del tutto interrotto in quanto ^non può più essere utilizzato per sfuggire a caratteri speciali : ad esempio,call foo.cmd a^&binterrompe silenziosamente (invece di passarea&bancheletteralefoo.cmd, come sarebbe il caso senzacall) -foo.cmdnon viene nemmeno invocato (!), Almeno su Windows 7.
L'escape di un letterale %è un caso speciale , sfortunatamente, che richiede una sintassi distinta a seconda che una stringa sia specificata sulla riga di comando o all'interno di un file batch ; vedere https://stackoverflow.com/a/31420292/45375
- In breve: all'interno di un file batch, usa
%%. Sulla riga di comando, %non è possibile eseguire l'escape, ma se si inserisce un ^all'inizio, alla fine o all'interno del nome di una variabile in una stringa non quotata (ad esempio echo %^foo%), è possibile impedire l'espansione della variabile (interpolazione); %le istanze sulla riga di comando che non fanno parte di un riferimento a una variabile vengono trattate come letterali (ad esempio 100%).
In generale, per lavorare in sicurezza con valori variabili che possono contenere spazi e caratteri speciali :
- Assegnazione : racchiudi sia il nome della variabile che il valore in una singola coppia di virgolette ; ad esempio,
set "v=a & b"assegna un valore letterale a & balla variabile %v%(al contrario, set v="a & b"renderebbe le virgolette doppie parte del valore). Esci dalle %istanze letterali come %%(funziona solo nei file batch - vedi sopra).
- Riferimento : riferimenti a virgolette doppie per assicurarsi che il loro valore non sia interpolato; ad esempio,
echo "%v%"non sottopone il valore di %v%a interpolazione e stampa "a & b"(ma si noti che anche le virgolette doppie vengono invariabilmente stampate). Al contrario, echo %v%passa letterale aa echo, interpreta &come operatore di sequenziamento dei comandi e quindi cerca di eseguire un comando denominato b.
Notare anche l'avvertenza di cui sopra il riutilizzo ^con la calldichiarazione.
- I programmi esterni in genere si occupano di rimuovere racchiudere le virgolette attorno ai parametri, ma, come notato, nei file batch devi farlo da solo (ad esempio,
%~1per rimuovere le virgolette doppie dal primo parametro) e, purtroppo, non c'è modo che io sappia per arrivare echoa stampare fedelmente un valore variabile senza le virgolette .
- Neil offre una
forsoluzione alternativa che funziona fintanto che il valore non ha virgolette doppie incorporate ; per esempio:
set "var=^&')|;,%!"
for /f "delims=" %%v in ("%var%") do echo %%~v
cmd.exenon non riconosce singoli -quotes per delimitare le stringhe - sono trattati come letterali e non possono generalmente essere utilizzate per le stringhe delimitano contenenti spazi; inoltre, ne consegue che i token adiacenti alle virgolette singole e gli eventuali token intermedi vengono trattati come non quotati da cmd.exee interpretati di conseguenza.
- Tuttavia, dato che i programmi di destinazione alla fine eseguono la propria analisi degli argomenti, alcuni programmi come Ruby riconoscono le stringhe con virgolette singole anche su Windows; al contrario, eseguibili C / C ++, Perl e Python non li riconoscono.
Anche se supportato dal programma di destinazione, tuttavia, non è consigliabile utilizzare stringhe con virgolette singole, dato che il loro contenuto non è protetto da interpretazioni potenzialmente indesiderate da parte di cmd.exe.
Citando all'interno di PowerShell:
Windows PowerShell è una shell molto più avanzata di cmd.exee fa parte di Windows da molti anni ormai (e PowerShell Core ha portato l'esperienza di PowerShell anche su macOS e Linux).
PowerShell funziona internamente in modo coerente rispetto alle citazioni:
- all'interno di stringhe tra virgolette, usa
`"o ""per sfuggire alle virgolette
- all'interno di stringhe tra virgolette singole, utilizzare
''per eseguire l'escape di virgolette singole
Funziona sulla riga di comando di PowerShell e quando si passano parametri agli script o alle funzioni di PowerShell dall'interno di PowerShell.
(Come discusso in precedenza, il passaggio di virgolette doppie con escape a PowerShell dall'esterno richiede \"o, in modo più affidabile, \""nient'altro funziona).
Purtroppo, quando si richiama esterni programmi da PowerShell, si è di fronte alla necessità di entrambi accomodate proprie regole di quoting di PowerShell e di fuggire per la destinazione del programma:
Questo comportamento problematico è anche discusso e riassunto in questa risposta
Doppia -quotes all'interno doppie corde -quoted :
Considera la stringa "3`" of rain", che viene tradotta internamente da PowerShell in letterale 3" of rain.
Se vuoi passare questa stringa a un programma esterno, devi applicare l'escape del programma di destinazione oltre a quello di PowerShell ; diciamo che vuoi passare la stringa a un programma C, che si aspetta che le virgolette doppie incorporate siano precedute da caratteri di escape come \":
foo.exe "3\`" of rain"
Si noti come sia `" - per rendere PowerShell felice - e il \- per rendere il felice programma di destinazione - deve essere presente.
La stessa logica si applica al richiamo di un file batch, dove ""deve essere utilizzato:
foo.bat "3`"`" of rain"
Al contrario, l'incorporamento di virgolette singole in una stringa con virgolette doppie non richiede affatto l'escape.
Singolo -quotes all'interno singole stringhe -quoted non non richiedono più la fuga; considera'2'' of snow', che è la rappresentazione di PowerShell di2' of snow.
foo.exe '2'' of snow'
foo.bat '2'' of snow'
PowerShell converte le stringhe tra virgolette singole in virgolette doppie prima di passarle al programma di destinazione.
Tuttavia, le virgolette doppie all'interno di stringhe con virgolette singole , che non richiedono l'escape per PowerShell , devono ancora essere sottoposte a escape per il programma di destinazione :
foo.exe '3\" of rain'
foo.bat '3"" of rain'
PowerShell v3 ha introdotto l' --%opzione magica , chiamata simbolo di interruzione dell'analisi , che allevia parte del problema, passando qualsiasi cosa dopo che non è stata interpretata al programma di destinazione, ad eccezione dei cmd.exeriferimenti alle variabili di ambiente in stile (ad esempio %USERNAME%), che vengono espansi; per esempio:
foo.exe --% "3\" of rain" -u %USERNAME%
Notare come l'escape di embedded "as solo \"per il programma di destinazione (e non anche per PowerShell as \`") sia sufficiente.
Tuttavia, questo approccio:
- non consente l' escape dei
% caratteri per evitare espansioni di variabili d'ambiente.
- preclude l' uso diretto di variabili ed espressioni di PowerShell; invece, la riga di comando deve essere costruita in una variabile stringa in un primo passaggio e quindi richiamata con
Invoke-Expressionin un secondo.
Pertanto, nonostante i suoi numerosi progressi, PowerShell non ha reso la fuga molto più semplice quando si chiamano programmi esterni. Tuttavia, ha introdotto il supporto per le stringhe con virgolette singole.
Mi chiedo se nel mondo Windows sia fondamentalmente possibile passare al modello Unix di lasciare che la shell faccia tutta la tokenizzazione e la rimozione delle quote in modo prevedibile , in anticipo , indipendentemente dal programma di destinazione , e quindi invocare il programma di destinazione passando i token risultanti .