AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79209426
Accepted
Grismar
Grismar
Asked: 2024-11-21 09:47:18 +0800 CST2024-11-21 09:47:18 +0800 CST 2024-11-21 09:47:18 +0800 CST

Passando argumentos de um .cmd para um .exe

  • 772

Tenho um executável que recebe vários argumentos de linha de comando e quero escrever um .cmd que execute algumas verificações e depois passe todos os argumentos para o executável.

Como exemplo, veja o executável echo_args.exeque é este aplicativo Rust:

use std::env;

fn main() {
    // Collect the command line arguments into a vector
    let args: Vec<String> = env::args().collect();

    // Iterate over the arguments and print each one
    for arg in args.iter() {
        println!("{}", arg);
    }
}

O fato de ser Rust não tem relação com a questão, mas dessa forma você pode reproduzir minha situação.

Chamando este aplicativo diretamente do PowerShell:

echo_args.exe "hello `"`"world`"`""

Impressões, como esperado:

echo_args.exe
hello ""world""

Chamando este aplicativo diretamente do prompt de comando:

echo_args.exe "hello """"world"""""

Impressões, como esperado:

echo_args.exe
hello ""world""

Em ambos os casos, é usada uma maneira correta de escapar aspas duplas, apropriada ao processador de comandos.

Entretanto, se eu escrevo um test.cmdarquivo, encontro o problema de que ele se comporta de forma diferente quando chamado pelo PowerShell ou pelo Prompt de Comando.

A solução ingênua é:

@echo off
set exe=echo_args.exe
"%exe%" %*

E embora isso funcione quando chamado do Prompt de Comando:

test "hello """"world"""""

Não vem do PowerShell porque:

test "hello `"`"world`"`""

Agora imprime:

echo_args.exe
hello "world"

Observe que as aspas duplas são "comidas" pelo processador de comando que executa o .cmd (ou seja, cmd.exe). Então, o Powershell passa para o hello ""world""to cmd.exe(como faria para echo_args.exe) e cmd.exededuz as aspas duplas e passa hello "world"para o .exe.

Agora, percebo que posso evitar isso escrevendo um test.ps1que execute a mesma função, para que o .cmd não seja chamado pelo PowerShell, e para fins práticos isso é bom, mas minha pergunta é: existe uma maneira de escrever o .cmd para que ele se comporte corretamente, independentemente de ser iniciado cmd.exeautomaticamente com o PowerShell ou se for encontrado e executado diretamente pelo Prompt de Comando?

Detectar se cmd.exefoi iniciado do PowerShell parece propenso a erros e complicado. E não vejo uma maneira direta de (re)construir os argumentos no .cmd que evite esse problema (já que o problema acontece essencialmente antes mesmo que o código seja executado). O que estou esquecendo? Perguntei a alguns LLMs (ChatGPT 4o e Claude Sonnet 3.5), mas eles insistem em provar sua inutilidade para problemas que exigem alguma nuance e apresentam uma série infinita de não soluções.

windows
  • 1 1 respostas
  • 49 Views

1 respostas

  • Voted
  1. Best Answer
    mklement0
    2024-11-21T21:39:54+08:002024-11-21T21:39:54+08:00

    A triste realidade no Windows PowerShell (a edição legada, fornecida com o Windows e exclusiva para Windows, do PowerShell, cuja versão mais recente é a 5.1), bem como nas versões (agora obsoletas) do PowerShell (Core) 7 (até a v7.2.x), é que uma camada extra e manual\ de escape de caracteres incorporados "é necessária em argumentos passados ​​para programas externos .

    • Isso foi corrigido no PowerShell v7.3+ , com exceções seletivas no Windows. Portanto, no PowerShell 7.3+ , suas chamadas funcionam conforme o esperado , EXCETO se você chamar um arquivo em lote , entre outros programas legados.

    • O comportamento antigo e quebrado ainda está disponível como opt-in e, por padrão, ainda se aplica seletivamente a certos programas , notavelmente arquivos em lote. Você pode evitar isso definindo a $PSNativeCommandArgumentPassingvariável de preferência para'Standard' , mas isso vem com ressalvas ; veja a próxima seção.

    • Veja esta resposta para mais detalhes.

    Portanto, no Windows PowerShell (e nas versões obsoletas do PowerShell 7.2; no PowerShell 7.3+, as chamadas funcionam de forma análoga com as \ instâncias removidas ):

    echo_args.exe "hello \`"\`"world\`"\`""
    

    Como alternativa, como nenhuma interpolação de string é necessária no seu caso, usar uma string literal'...' ( ) elimina a necessidade de escape do PowerShell :

    echo_args.exe 'hello \"\"world\"\"'
    

    Entretanto, mesmo quando uma string expansível"..." (interpoladora) ( ) é necessária, há uma maneira de evitar a necessidade de escape do PowerShell por meio do uso da variante here-string (invariavelmente multilinha)

    $addressee = 'world'
    echo_args.exe @"
    hello \"\"$addressee\"\"
    "@
    

    Finalmente, para fins de completude, ambas as edições do PowerShell (todas as versões) oferecem --%, o chamado stop-parsing token , que essencialmente copia o que o segue verbatim para a linha de comando do processo construída nos bastidores. Isso acarreta limitações severas, no entanto, notavelmente a incapacidade de referenciar variáveis ​​do PowerShell - veja a seção inferior desta resposta para detalhes:

    # Use of the stop-parsing token, --%
    
    # Both variations work, because most CLIs on Windows accept "" and \"
    # interchangeably as an escaped "
    
    echo_args.exe --% "hello """"world""""
    
    echo_args.exe --% "hello \"\"world\"\""
    

    Advertência:

    • Embora o acima também funcione no PowerShell 7, isso não acontece mais na versão 7.4.x, devido a um bug antigo: problema nº 18664 do GitHub .

    Advertência sobre o PowerShell 7.3+ ao invocar um arquivo em lote :

    • Como o comportamento antigo e quebrado por padrão ( $PSNativeCommandArgumentPassingdefaults to 'Windows') ainda se aplica a arquivos em lote (entre outros interpretadores legados), as soluções alternativas acima ainda são necessárias por padrão no PowerShell 7.3+.

      • Conforme observado, a configuração $PSNativeCommandArgumentPassing = 'Standard'desativa esse comportamento e executa \"o escape de "caracteres incorporados para todos os programas externos nos bastidores, incluindo arquivos em lote.

      • No entanto, o uso de \" pode causar problemas em arquivos em lote : como cmd.exenão reconhece a \"como escape , cmd.exemetacaracteres como &inside \"...\"embedded em geral "..."podem resultar em erros de sintaxe; além disso, ao processar argumentos individuais usando a linguagem em lote (em vez de apenas passar todos os argumentos para outro programa, com $*), apenas ""o escape -escaping é reconhecido; a proposta mencionada abaixo teria evitado esse problema usando ""o escape -escaping para arquivos em lote nos bastidores.

      • Há outra armadilha , que não está relacionada $PSNativeCommandArgumentPassinge afeta todas as versões de ambas as edições do PowerShell: como o PowerShell só emprega "..."o encapsulamento se o valor literal a ser passado contiver espaços na linha de comando do processo, uma chamada como batch.cmd 'http://example.org?foo=1&bar=2'from PowerShell quebra o arquivo em lote, porque este último recebe o argumento http://example.org?foo=1&bar=2 unquoted .

        • No entanto, a solução alternativa é, na verdade, mais fácil com o comportamento quebrado em vigor (que, como observado, ainda se aplica no 7.3+ por padrão):
          batch.cmd '"http://example.org?foo=1&bar=2"'.
          Novamente, a proposta mencionada a seguir teria evitado esse problema.
    • O problema #15143 do GitHub é uma proposta detalhada que teria evitado toda essa confusão no futuro, por meio de acomodações seletivas para intérpretes legados como cmd.exee CLIs não padrão como msiexec.exe. Infelizmente, não deu em nada.


    Quanto às suas observações e perguntas :

    existe uma maneira de escrever o .cmd para que ele se comporte corretamente

    Conclui-se do exposto acima que o Windows PowerShell é o culpado, então você deve compensar seu comportamento problemático na invocação .

    as aspas duplas são 'comidas' pelo processador de comando que executa o .cmd (ou seja, cmd.exe). Então, o Powershell passa para o hello ""world""cmd.exe (como faria para echo_args.exe) e o cmd.exe deduplica as aspas duplas e passa hello "world"para o .exe.

    Não, não é cmd.exeisso que "come" as aspas duplas, é, na verdade, a maneira quebrada como o Windows PowerShell coloca o valor literal que ele analisou de acordo com suas regras de sintaxe na linha de comando do processo que ele usa para chamar programas externos nos bastidores. cmd.exeEle passa tudo o que recebe no estado em que se encontra , com apenas uma interpretação mínima (nenhuma no caso em questão).

    Especificamente, a linha de comando do processo - quebrado - que o Windows PowerShell constrói é:

    # Windows PowerShell (and PowerShell 7.2-): BROKEN
    # *Process* command line constructed behind the scenes, if
    # echo_args.exe "hello `"`"world`"`"" is submitted: 
    echo_args.exe "hello ""world"""
    

    Isso está quebrado, porque o (Windows) PowerShell - que necessariamente deve passar uma string com aspas duplas para programas externos, já que somente essa forma de aspas pode ser entendida por CLIs do Windows - negligencia escapar do" .
    Ou seja, para passar verbatim hello ""world""para um programa externo, ou "hello """"world"""""ou, mais tipicamente, "hello \"\"world\"\""deve ser colocado na linha de comando do processo.

    O PowerShell 7.3+ agora executa esse escape usando \"(mas, como observado, por padrão, não para arquivos em lote ).

    # PowerShell 7.3+: OK
    # *Process* command line constructed behind the scenes, if
    # echo_args.exe "hello `"`"world`"`"" is submitted: 
    echo_args.exe "hello \"\"world\"\""
    
    • 3

relate perguntas

  • C++ ReadFile inteiramente, onde tamanho> 2 GB (Win64)

  • BSOD do Windows Palo Alto Cortex XDR com verificação de bug 0x139

  • Não consigo importar pacotes locais no meu projeto go

  • O KERNEL32.DLL é sempre o terceiro módulo carregado em um processo do Windows?

  • Qual é o problema neste código Rust inseguro para que funcione no Windows, mas não no Ubuntu?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve