Como eu substituiria todas as aspas duplas nos parâmetros do meu arquivo em lote por aspas duplas de escape? Este é meu arquivo em lote atual, que expande todos os seus parâmetros de linha de comando dentro da string:
@echo off
call bash --verbose -c "g++-linux-4.1 %*"
Em seguida, ele usa essa string para fazer uma chamada para o bash do Cygwin, executando um compilador cruzado do Linux. Infelizmente, estou recebendo parâmetros como esses passados para meu arquivo em lote:
"launch-linux-g++.bat" -ftemplate-depth-128 -O3 -finline-functions
-Wno-inline -Wall -DNDEBUG -c
-o "C:\Users\Me\Documents\Testing\SparseLib\bin\Win32\LinuxRelease\hello.o"
"c:\Users\Me\Documents\Testing\SparseLib\SparseLib\hello.cpp"
Onde a primeira citação em torno do primeiro caminho passado está terminando prematuramente a string que está sendo passada para o GCC e passando o restante dos parâmetros diretamente para o bash (o que falha espetacularmente).
Eu imagino que se eu puder concatenar os parâmetros em uma única string e escapar das aspas, isso deve funcionar bem, mas estou tendo dificuldade em determinar como fazer isso. Alguém sabe?
fonte
^
é o caractere de escape apenas em strings não citadas ; em strings com aspas duplas, ele é tratado como um literal. Ao contrário dos shells Unix (tipo POSIX),cmd.exe
NÃO oferece tratamento de shell padronizado para aspas duplas dentro de uma string entre aspas duplas, e a interpretação é deixada para o programa que está sendo invocado (continuação no próximo comentário)."
caracteres. para ser escapado como\"
dentro de uma string entre aspas (aplica-se pelo menos a: C / C ++, Python, Perl, Ruby). Por outro lado,""
só é reconhecido na minoria dos casos: em parâmetros passados para arquivos em lote,""
é reconhecido como um incorporado aspas duplas, mas é mantido como está no correspondente%<n>
parâmetro, mesmo após a remoção dos encerram aspas duplas com%~<n>
. Python graciosamente também reconhece""
, como uma alternativa para\"
.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 usando
cmd.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 :Exemplo:
foo.bat "We had 3"" of rain."
O seguinte se aplica apenas a arquivos em lote:
""
é a única maneira de fazer com que o interpretador de comandos (cmd.exe
) trate toda a string entre aspas duplas como um único argumento.Infelizmente, no entanto, não apenas as aspas duplas são mantidas (como de costume), mas também as duplas com escape, portanto, obter a string pretendida é um processo de duas etapas; por exemplo, supondo que a string entre aspas duplas seja passada como o primeiro argumento
%1
:set "str=%~1"
remove as aspas duplas;set "str=%str:""="%"
em seguida, converte as aspas duplas em simples.Certifique-se de usar aspas duplas ao redor das partes da atribuição para evitar interpretações indesejadas dos valores.
\"
é 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 .foo.exe "We had 3\" of rain."
\"
PODE RESULTAR NA EXECUÇÃO ARBITRÁRIA DE COMANDOS E / ou EM REDIREÇÕES DE ENTRADA / SAÍDA :& | < >
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."
\""
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 .Apenas com strings LITERAIS - aquelas que NÃO envolvem VARIÁVEIS - você obtém uma abordagem similarmente metódica: categoricamente -
^
escape de todos oscmd.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 ascmd.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:cmd.exe
vê os seguintes tokens, resultantes de interpretação incorreta\"
como aspas duplas regulares:"3\"
of
snow" "
& 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
relatacmd.exe
as informações de versão).O efeito geral é:
foo.exe
é invocado apenas com os primeiros 3 tokens.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 invocadocmd.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 corretamente6
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 de
cmd.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: produz5
(observe os 2 espaç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ídas4
(!), 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 gera4
, 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, comobash
:Você deve usar
\"
, o que, no entanto, é seguro e preserva os espaços em branco :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.^
em parâmetros passados para acall
instrução é interrompido (isso se aplica a ambos os usos decall
: 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 literala^b
,call :foo "%v%"
atribui"a^^b"
(!) a%1
(o primeiro parâmetro) na sub-rotina:foo
.^
withcall
é 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 7O 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%%
. 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 :
set "v=a & b"
atribui valor literala & 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).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 literala
paraecho
, interpreta&
como o operador de sequenciamento de comandos e, portanto, tenta executar um comando chamadob
.Observe também a advertência acima sobre a reutilização de
^
com acall
instrução.%~1
para remover aspas duplas do primeiro parâmetro) e, infelizmente, não há maneira que eu conheço de conseguirecho
imprimir um valor de variável fielmente, sem as aspas duplas .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.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:
`"
ou""
para escapar aspas duplas''
para escapar aspas simplesIsso 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 literal3" 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
\"
: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: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
.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 :
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 paracmd.exe
referências de variáveis de ambiente de estilo (por exemplo,%USERNAME%
), que são expandidas; por exemplo: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:
%
para evitar expansões de variáveis de ambiente.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 .
fonte
Literal use of ^ in double-quoted strings can, be problematic when applying ~ ...
não realmente. O til remove apenas aspas externas quando presentes. A perda dos cursores é um problema do próprio manuseio. Normalmente, umset "param=%~1"
resolve isso.~
- atualizei minha resposta para refletir meu novo entendimento - por favor, comente, se você detectar um problema. Você tem uma explicação para a duplicação^
disso automaticamente quando você passa parâmetros para umacall
instrução?call echo cat^^&dog
se não pudesse ser resolvido apenas com qualquer quantidade de circunflexos^
comcall
, que, como você ressaltou , está muito quebrado. Parece que emcall echo cat^&dog
(único^
que escapa apropriadamente o&
) o comando de destino (echo
) nunca é sequer invocado (!) - o comando inteiro falha silenciosamente. Eu atualizei a resposta de acordo.""
como escape,"
mas sempre\"
(veja minha resposta para uma maneira menos perigosa de usá-lo de dentro do cmd). Eu não sei qualquer documentação oficial que define""
como um escapou citação, mas pelo menos 2 que mencionar\"
: .NET e VS . Embora incorretamente documentado, a API do Win32 também segue essas regras.O Google finalmente encontrou a resposta. A sintaxe para substituição de string em lote é esta:
O que produz "me replicar". Meu script agora se parece com este:
Que substitui todas as instâncias de
"
com\"
, com escape adequado para bash.fonte
Como um complemento à excelente resposta de mklement0 :
Quase todos os executáveis aceitam
\"
como escape"
. O uso seguro em cmd, entretanto, quase só é possível usando DELAYEDEXPANSION.Para enviar explicitamente um literal
"
para algum processo, atribua\"
a uma variável de ambiente e, em seguida, use essa variável, sempre que precisar passar uma cotação. Exemplo:Nota
SETLOCAL ENABLEDELAYEDEXPANSION
parece funcionar apenas em arquivos em lote. Para obter EXPANSÃO ATRASADA em uma sessão interativa, comececmd /V:ON
.Se seu batchfile não funcionar com DELAYEDEXPANSION, você pode habilitá-lo temporariamente:
Se você deseja passar conteúdo dinâmico de uma variável que contém aspas que são escapadas, pois
""
você pode substituir""
por\"
na expansão:Esta substituição não é segura com
%...%
expansão de estilo!No caso de OP
bash -c "g++-linux-4.1 !v_params:"=\"!"
é a versão segura.Se, por algum motivo, ativar o DELAYEDEXPANSION não for uma opção, continue lendo:
Usando
\"
de dentro do cmd é um pouco mais seguro se sempre for necessário escapar caracteres especiais, em vez de apenas algumas vezes. (É menos provável que esqueça um cursor, se for consistente ...)Para conseguir isso, qualquer citação
^"
deve ser precedida por um acento circunflexo ( ), as aspas que devem chegar ao processo filho como literais devem ser escapadas com uma barra invertida (\^"
). TODOS os metacaracteres do shell devem ser escapados^
também, por exemplo,&
=>^&
;|
=>^|
;>
=>^>
; etc.Exemplo:
Fonte: Todos citam argumentos de linha de comando da maneira errada , consulte "Um método melhor de citar"
Para passar conteúdo dinâmico, é necessário garantir o seguinte:
A parte do comando que contém a variável deve ser considerada "entre aspas" por
cmd.exe
(Isso é impossível se a variável pode conter aspas - não escreva%var:""=\"%
). Para conseguir isso, o último"
antes da variável e o primeiro"
depois da variável não têm^
escape. metacaracteres cmd entre esses dois"
não devem ser escapados. Exemplo:Isso não é seguro, se
%dynamic_content%
puder conter citações sem correspondência.fonte
^
escapar de todos os metacaracteres funciona de forma robusta e pode ser aplicado de forma mais metódica (embora seja obviamente uma dor real na parte do corpo de sua escolha). Atualizei minha resposta de acordo (e dei crédito a você).!q!
caminho). Nota: A sua resposta é um pouco inconsistente depois de sua última edição: Perto do topo você diz: "Você não usar^"
". Posteriormente, você o usará^"
como parte da solução alternativa. Talvez você possa explicar as duas maneiras? (1) Escape de todos os metacaracteres (mais metodicamente) / (2) Metacaracteres de escape seletivamente em regiões "não citadas" (às vezes necessário para passar conteúdo dinâmico, por exemplofoo.exe ^"danger ^& bar=\"%dynamic_content%\"^"
- desta forma a variável é citada para cmd)\"
e""
. Vinculei a sua resposta para a abordagem baseada em variável mais envolvida. Deixe-me saber se faz sentido agora.%dynamic_content%
em minha resposta. Você acha que é detalhado o suficiente ou eu tenho que explicar mais?setlocal delayedexpansion
, mas você deve terminar o bloco comendlocal
(sem argumento). Honestamente, minha cabeça começou a girar olhando para o seu Gist. Estamos realmente lidando com casos extremos aqui e acho que os futuros leitores encontrarão tudo o que precisam entre nossas duas respostas.Se a string já estiver entre aspas, use outra aspas para anular sua ação.
fonte
Por exemplo, para a ferramenta Unreal engine Automation executada a partir de um arquivo em lote - funcionou para mim
por exemplo: -cmdline = "-Messaging" -device = device -addcmdline = "- SessionId = session -SessionOwner = 'owner' -SessionName = 'Build' -dataProviderMode = local -LogCmds = 'LogCommodity OFF' -execcmds = 'lista de automação ; testes de execução + separados + por + T1 + T2; sair '"-run
Espero que isso ajude alguém, funcionou para mim.
fonte