A própria resposta de eplawless resolve seu problema específico de maneira simples e eficaz: substitui todas as "
instâncias em toda a lista de argumentos por \"
, que é como o Bash requer aspas dentro de uma string entre aspas para ser representado.
Para responder geralmente à questão de como escapar as aspas dentro de uma string entre aspas duplas usandocmd.exe
o interpretador de linha de comando do Windows (seja na linha de comando - muitas vezes ainda chamada erroneamente de "prompt do DOS" - ou em um arquivo em lote): Consulte o final para ver o PowerShell .
tl; dr :
Você deve usar""
ao passar uma string para um (outro) arquivo em lote e pode usar ""
com aplicativos criados com os compiladores C / C ++ /. NET da Microsoft (que também aceitam \"
), que no Windows inclui Python e Node.js :
\"
é necessário - como a única opção - por muitos outros programas (por exemplo, Ruby, Perl e até mesmo o próprio Windows PowerShell da Microsoft (!)), mas SEU USO NÃO É SEGURO :
\"
é o que muitos executáveis e intérpretes exigem - incluindo Windows PowerShell - quando strings passadas de fora - ou, no caso dos compiladores ""
da Microsoft, oferecem suporte como uma alternativa para - em última análise, porém, cabe ao programa de destino analisar a lista de argumentos .
- Exemplo:
foo.exe "We had 3\" of rain."
- NO ENTANTO, O USO DE
\"
PODE RESULTAR NA EXECUÇÃO ARBITRÁRIA DE COMANDOS E / ou EM REDIREÇÕES DE ENTRADA / SAÍDA :
- Os seguintes personagens apresentam esse risco:
& | < >
- Por exemplo, o seguinte resulta na execução não intencional do
ver
comando; veja mais abaixo para uma explicação e o próximo ponto para uma solução alternativa:
foo.exe "3\" of snow" "& ver."
- Para Windows PowerShell ,
\""
e "^""
são robustos, mas alternativas limitados (ver secção "Calling CLI do PowerShell ..." abaixo).
Se você tiver que usar \"
, existem apenas 3 abordagens seguras , que são, no entanto, bastante complicadas : Dica do chapéu para TS por sua ajuda.
Usando a expansão de variável atrasada (possivelmente seletiva ) em seu arquivo de lote, você pode armazenar literal \"
em uma variável e fazer referência a essa variável dentro de uma "..."
string usando a !var!
sintaxe - consulte a resposta útil do TS .
- A abordagem acima, apesar de ser complicada, tem a vantagem de poder ser aplicada metodicamente e funcionar de maneira robusta , com qualquer entrada.
Apenas com strings LITERAIS - aquelas que NÃO envolvem VARIÁVEIS - você obtém uma abordagem similarmente metódica: categoricamente - ^
escape de todos os cmd.exe
metacaracteres: " & | < >
e - se você também deseja suprimir a expansão de variável - %
:
foo.exe ^"3\^" of snow^" ^"^& ver.^"
Caso contrário, você deve formular sua string com base no reconhecimento de quais partes da string são cmd.exe
consideradas não citadas devido à interpretação incorreta\"
como delimitadores de fechamento:
em porções literais contendo metacaracteres de shell: ^
-escape-os; usando o exemplo acima, é &
que deve ser ^
evitado:
foo.exe "3\" of snow" "^& ver."
em partes com %...%
referências de variáveis de estilo : certifique-se de que as cmd.exe
considera parte de uma "..."
string e que os valores das variáveis não possuem aspas embutidas e desequilibradas - o que nem sempre é possível .
Para obter informações básicas, continue lendo.
fundo
Nota: Isso é baseado em minhas próprias experiências. Avise-me se estiver errado.
Shells do tipo POSIX, como Bash em sistemas do tipo Unix, tokenizam a lista de argumentos (string) antes de passar argumentos individualmente para o programa de destino: entre outras expansões, eles dividem a lista de argumentos em palavras individuais (divisão de palavras) e removem os caracteres de citação do palavras resultantes (remoção de aspas). O programa de destino recebe uma série de argumentos individuais , com as aspas sintáticas removidas .
Em contraste, o interpretador de comandos do Windows aparentemente não simboliza a lista de argumentos e simplesmente passa a única string contendo todos os argumentos - incluindo os caracteres de citação. - para o programa de destino.
No entanto, algum pré-processamento ocorre antes que a única string seja passada para o programa de destino: ^
caracteres de escape. fora de strings com aspas duplas são removidos (eles escapam do caractere seguinte), e as referências de variáveis (por exemplo, %USERNAME%
) são interpoladas primeiro.
Portanto, ao contrário do Unix, é responsabilidade do programa de destino analisar para analisar a string de argumentos e dividi-la em argumentos individuais com as aspas removidas. Assim, diferentes programas podem hipoteticamente requerem diferentes métodos de fuga e não há único mecanismo de fuga que está garantido para trabalhar com todos os programas - https://stackoverflow.com/a/4094897/45375 contém excelente fundo sobre a anarquia que é de linha de comando do Windows análise.
Na prática, \"
é muito comum, mas NÃO SEGURO , conforme mencionado acima:
Como cmd.exe
ele próprio não reconhece \"
como aspas duplas de escape , ele pode interpretar erroneamente tokens posteriores na linha de comando como não citados e potencialmente interpretá-los como comandos e / ou redirecionamentos de entrada / saída .
Em poucas palavras: o problema superfícies, se qualquer um dos seguintes caracteres seguir uma abertura ou desequilibrada \"
:& | < >
; por exemplo:
foo.exe "3\" of snow" "& ver."
cmd.exe
vê os seguintes tokens, resultantes de interpretação incorreta \"
como aspas duplas regulares:
"3\"
of
snow" "
- descansar:
& ver.
Como cmd.exe
pensa que não& ver.
está entre aspas , ele o interpreta como&
(o operador de sequência de comando), seguido pelo nome de um comando a ser executado ( ver.
- o .
é ignorado; ver
relata cmd.exe
as informações de versão).
O efeito geral é:
- Primeiro,
foo.exe
é invocado apenas com os primeiros 3 tokens.
- Então, o comando
ver
é executado.
Mesmo nos casos em que o comando acidental não causa danos, seu comando geral não funcionará conforme projetado, visto que nem todos os argumentos são passados para ele.
Muitos compiladores / intérpretes reconhecem SOMENTE\"
- por exemplo, o compilador GNU C / C ++, Python, Perl, Ruby, até mesmo o próprio Windows PowerShell da Microsoft quando invocado cmd.exe
- e, exceto (com limitações) pelo Windows PowerShell com \""
, para eles não há uma solução simples para este problema.
Essencialmente, você teria que saber com antecedência quais partes de sua linha de comando são mal interpretadas como não citadas e, seletivamente, ^
escapar de todas as instâncias & | < >
dessas partes.
Por outro lado, o uso de ""
é SEGURO , mas infelizmente só é compatível com executáveis e arquivos em lote baseados no compilador da Microsoft (no caso de arquivos em lote, com as peculiaridades discutidas acima), que exclui o PowerShell - consulte a próxima seção.
Chamando CLI do PowerShell a partir cmd.exe
de shells semelhantes a POSIX:
Observação: consulte a seção inferior para saber como as cotações são tratadas no PowerShell.
Quando invocado de fora - por exemplo, de cmd.exe
, seja da linha de comando ou de um arquivo em lote:
O PowerShell [Core] v6 + agora reconhece corretamente""
(além de\"
), que é seguro de usar e preserva espaços em branco .
pwsh -c " ""a & c"".length "
não quebra e rende corretamente 6
Windows PowerShell (a edição herdada cuja última versão é 5.1) reconhece apenas \"
e, no Windows também """
e o mais robusto \""
/"^""
(embora internamente o PowerShell use`
como caractere de escape em strings entre aspas duplas e também aceite""
- consulte a seção inferior):
Chamando o Windows PowerShell decmd.exe
/ um arquivo em lote:
""
rompe , porque é fundamentalmente sem suporte:
powershell -c " ""ab c"".length "
-> erro "A string está sem o terminador"
\"
e """
funcionam em princípio , mas não são seguros :
powershell -c " \"ab c\".length "
funciona como pretendido: produz 5
(observe os 2 espaços)
- Mas não é seguro, porque os
cmd.exe
metacaracteres quebram o comando, a menos que escapado:
powershell -c " \"a& c\".length "
quebra , devido ao &
, que teria que ser escapado como^&
\""
é seguro , mas normaliza os espaços em branco internos , o que pode ser indesejado:
powershell -c " \""a& c\"".length "
saídas 4
(!), porque os 2 espaços são normalizados para 1.
"^""
é a melhor escolha para o Windows PowerShell especificamente , onde é seguro e preserva os espaços em branco, mas com o PowerShell Core (no Windows) é igual\""
, ou seja, normaliza os espaços em branco . O crédito vai para Venryx por descobrir essa abordagem.
powershell -c " "^""a& c"^"".length "
funciona : não quebra - apesar &
- e saídas5
, ou seja, espaços em branco corretamente preservados.
PowerShell Core : pwsh -c " "^""a& c"^"".length "
funciona , mas gera 4
, ou seja, normaliza os espaços em branco , como \""
faz.
Em plataformas semelhantes ao Unix (Linux, macOS), ao chamar a CLI do PowerShell [Core] ,pwsh
, a partir de um shell semelhante a POSIX, como bash
:
Você deve usar\"
, o que, no entanto, é seguro e preserva os espaços em branco :
$ pwsh -c " \"a& c|\".length" # OK: 5
Informação relacionada
^
só pode ser usado como o caractere de escape em não cotadas cordas - dentro de strings com aspas duplas, ^
não é especial e tratado como um literal.
- CAVEAT : O uso de
^
em parâmetros passados para a call
instrução é interrompido (isso se aplica a ambos os usos de call
: chamar outro arquivo em lote ou binário e chamar uma sub-rotina no mesmo arquivo em lote):
^
instâncias em valores entre aspas duplas são inexplicavelmente duplicados , alterando o valor que está sendo passado: por exemplo, se a variável %v%
contém um valor literal a^b
, call :foo "%v%"
atribui "a^^b"
(!) a %1
(o primeiro parâmetro) na sub-rotina :foo
.
- O uso não indicado de
^
with call
é totalmente interrompido, pois ^
não pode mais ser usado para escapar caracteres especiais : por exemplo,call foo.cmd a^&b
interrompe silenciosamente (em vez de passar literala&b
tambémfoo.cmd
, como seria o caso semcall
) -foo.cmd
nunca é nem mesmo invocado (!), Pelo menos no Windows 7
O escape de um literal %
é um caso especial , infelizmente, que requer sintaxe distinta dependendo se uma string é especificada na linha de comando ou dentro de um arquivo em lote ; consulte https://stackoverflow.com/a/31420292/45375
- Resumindo: dentro de um arquivo em lote, use
%%
. Na linha de comando, %
não pode ser escapado, mas se você colocar um ^
no início, no final ou dentro de um nome de variável em uma string sem aspas (por exemplo, echo %^foo%
), você pode evitar a expansão da variável (interpolação); %
instâncias na linha de comando que não fazem parte de uma referência de variável são tratadas como literais (por exemplo, 100%
).
Geralmente, para trabalhar com segurança com valores de variáveis que podem conter espaços e caracteres especiais :
- Atribuição : Anexar tanto o nome da variável eo valor em um único par de aspas duplas ; por exemplo,
set "v=a & b"
atribui valor literal a & b
à variável %v%
(por contraste, set v="a & b"
faria as aspas duplas parte do valor). Escape de %
instâncias literais como%%
(funciona apenas em arquivos em lote - veja acima).
- Referência : aspas duplas referências de variáveis para garantir que seus valores não sejam interpolados; por exemplo,
echo "%v%"
não sujeita o valor de %v%
à interpolação e impressões "a & b"
(mas observe que as aspas duplas são invariavelmente impressas também). Em contraste, echo %v%
passa literal a
para echo
, interpreta &
como o operador de sequenciamento de comandos e, portanto, tenta executar um comando chamado b
.
Observe também a advertência acima sobre a reutilização de^
com a call
instrução.
- Programas externos normalmente se encarregam de remover aspas duplas em torno dos parâmetros, mas, como observado, em arquivos em lote você tem que fazer isso sozinho (por exemplo,
%~1
para remover aspas duplas do primeiro parâmetro) e, infelizmente, não há maneira que eu conheço de conseguir echo
imprimir um valor de variável fielmente, sem as aspas duplas .
- Neil oferece uma
for
solução alternativa baseada em que funciona desde que o valor não tenha aspas duplas incorporadas ; por exemplo:
set "var=^&')|;,%!"
for /f "delims=" %%v in ("%var%") do echo %%~v
cmd.exe
que não reconhecem individuais -quotes como delimitadores de cadeia - eles são tratados como literais e não podem geralmente ser usadas para cordas delimitam com espaços em branco incorporado; também, segue-se que os tokens adjacentes às aspas simples e quaisquer tokens entre são tratados como não citados porcmd.exe
e interpretados de acordo.
- No entanto, dado que os programas de destino em última análise realizam sua própria análise de argumento, alguns programas como Ruby reconhecem strings entre aspas simples, mesmo no Windows; em contraste, executáveis C / C ++, Perl e Python não os reconhecem.
Mesmo se suportado pelo programa de destino, no entanto, não é aconselhável usar strings entre aspas simples, visto que seu conteúdo não é protegido de interpretações potencialmente indesejadas por cmd.exe
.
Citando de dentro do PowerShell:
O Windows PowerShell é um shell muito mais avançado do que o cmd.exe
Windows e já faz parte do Windows há muitos anos (e o PowerShell Core trouxe a experiência do PowerShell para macOS e Linux também).
O PowerShell funciona de forma consistente internamente em relação à cotação:
- dentro de strings entre aspas, use
`"
ou ""
para escapar aspas duplas
- dentro de strings entre aspas simples, use
''
para escapar aspas simples
Isso funciona na linha de comando do PowerShell e ao passar parâmetros para scripts ou funções do PowerShell de dentro do PowerShell.
(Como discutido acima, passar uma aspa dupla de escape para o PowerShell de fora requer\"
ou, mais fortemente, \""
- nada mais funciona).
Infelizmente, ao invocar programas externos do PowerShell, você se depara com a necessidade de acomodar as próprias regras de cotação do PowerShell e escapar para o programa de destino :
Este comportamento problemático também é discutido e resumido nesta resposta
Aspas duplas dentro de strings aspas duplas :
Considere string "3`" of rain"
, que o PowerShell traduz internamente em literal 3" of rain
.
Se você quiser passar essa string para um programa externo, terá que aplicar o escape do programa de destino além do do PowerShell ; digamos que você queira passar a string para um programa C, que espera que as aspas duplas incorporadas sejam escapadas como \"
:
foo.exe "3\`" of rain"
Observe como ambos `"
- para deixar o PowerShell feliz - e o\
- para tornar o feliz programa de metas - devem estar presentes.
A mesma lógica se aplica à chamada de um arquivo em lote, onde ""
deve ser usado:
foo.bat "3`"`" of rain"
Em contraste, a incorporação de aspas simples em uma string de aspas duplas não requer nenhum escape.
Único -quotes dentro únicas cordas -quoted que não exigem extra de escapar; considere'2'' of snow'
, que é a representação do PowerShell de2' of snow
.
foo.exe '2'' of snow'
foo.bat '2'' of snow'
O PowerShell converte strings entre aspas simples em aspas duplas antes de passá-las para o programa de destino.
No entanto, aspas duplas dentro de strings de aspas simples , que não precisam de escape para PowerShell , ainda precisam ser escapadas para o programa de destino :
foo.exe '3\" of rain'
foo.bat '3"" of rain'
O PowerShell v3 introduziu a --%
opção mágica , chamada de símbolo de parada de análise , que alivia parte da dor, passando qualquer coisa depois de não interpretada para o programa de destino, exceto para cmd.exe
referências de variáveis de ambiente de estilo (por exemplo, %USERNAME%
), que são expandidas; por exemplo:
foo.exe --% "3\" of rain" -u %USERNAME%
Observe como o escape de incorporado "
as \"
para o programa de destino apenas (e não também para PowerShell as \`"
) é suficiente.
No entanto, esta abordagem:
- não permite caracteres de escape
%
para evitar expansões de variáveis de ambiente.
- impede o uso direto de variáveis e expressões do PowerShell; em vez disso, a linha de comando deve ser construída em uma variável de string em uma primeira etapa e, em seguida, chamada com
Invoke-Expression
em uma segunda.
Portanto, apesar de seus muitos avanços, o PowerShell não tornou o escape muito mais fácil ao chamar programas externos. No entanto, ele introduziu suporte para strings entre aspas simples.
Eu me pergunto se é fundamentalmente possível no mundo do Windows mudar para o modelo Unix de permitir que o shell faça toda a tokenização e remoção de cotações previsivelmente , antecipadamente , independentemente do programa de destino , e então invocar o programa de destino passando os tokens resultantes .