Powershell: Ligue para o operador nativo

Criado em 30 jun. 2020  ·  189Comentários  ·  Fonte: PowerShell/PowerShell

Declaração do Problema

Atualmente, há casos em que recortar e colar uma linha de comando nativa falha ao executar como esperado no PowerShell. Isso pode ser devido à análise incorreta de aspas destinadas a serem passadas para o comando nativo ou ao uso da sintaxe do PowerShell que não deve ser interpretada como PowerShell. O PowerShell tem um argumento especial --% quando usado com comandos nativos trata o resto dos argumentos como literais passados ​​para o comando nativo, mas tem vários problemas:

  1. Não é detectável, os usuários precisam saber sobre este parâmetro especial com antecedência
  2. | , && e || têm precedência, então: wsl --% ls | less executaria wsl ls e canalizaria os resultados para less rodando no PowerShell em vez de less rodando no wsl
  3. Se você recortar e colar uma linha de comando, precisará editar a linha para inserir --% no início
  4. Em sistemas Unix, os argumentos após -% são passados ​​literalmente sem globbing, onde os comandos nativos no Unix esperam que o shell execute globbing

Detalhes de implementação técnica proposta

A proposta é introduzir um novo operador --% (Call Native).

Qualquer conteúdo de texto após esse operador chamará o "shell padrão" do sistema operacional para executar. No Windows, seria cmd.exe e em sistemas baseados em Unix seria / bin / sh. Isso resolve o problema de globbing em sistemas baseados em Unix e também permite a expansão de %variable% no Windows. Ao contrário de --% switch, isso também significa que | , && e || são tratados como parte da linha de comando nativa.

Isso significa que esses dois são funcionalmente iguais:

wsl --% ls $foo `&`& echo $PWD
--% wsl ls $foo && echo $PWD

onde $foo e $PWD são avaliados pelo shell dentro do WSL. Observe que, no primeiro exemplo, você teria que saber escapar && para que ele fosse executado dentro do WSL em vez de dentro do PowerShell.

Para canalizar a saída dessa execução de volta para o PowerShell, o usuário deve primeiro armazenar os resultados em uma variável:

$out = --% ls *.txt
$out | select-string hello

Observe que, ao contrário do atual operador de chamada & , você não pode usar nenhuma sintaxe do PowerShell, portanto:

--% $commandline

não resolveria $commandline como uma variável primeiro pelo PowerShell, mas, em vez disso, passaria $commandline para o shell padrão para processar não resolvido.

O problema de recortar e colar é resolvido simplesmente colando depois que --% for digitado.

O exemplo acima para wsl seria semelhante a:

--% wsl ls | less

onde a intenção é que toda a linha seja executada na instância WSL Linux.

Detectabilidade

Os usuários já familiarizados com --% como uma opção podem facilmente fazer a transição para usar este novo operador onde fizer sentido. Para novos usuários, --% é exclusivo para que os mecanismos de pesquisa o encontrem facilmente relacionado ao PowerShell.

Considerações Alternativas

&! e &n foram propostos como sigilo, mas houve recuo porque &! é um operador válido em alguns idiomas, tornando a pesquisa na web por documentação mais difícil. Também havia uma preocupação se um visual semelhante ao de & call operator poderia ser confuso para os usuários.

Há dúvidas sobre o suporte à continuação da linha ao usar este novo operador. Eu sugeriria que não a apoiamos inicialmente.

Uma solução de cmdlet em vez de uma solução de operador foi proposta. Acreditamos que isso não resolve o problema de "recortar e colar", pois agora você precisa saber como colocar o pipeline entre aspas simples e / ou caracteres especiais de escape. Acreditamos que um cmdlet como em Invoke-NativeCommand (substantivo a ser determinado) seria útil como uma opção adicional em vez de substituir a necessidade de um operador.

Assuntos relacionados

Isso também deve resolver estes problemas:

https://github.com/PowerShell/PowerShell/issues/12491
https://github.com/PowerShell/PowerSHell/issues/1761

Committee-Reviewed Issue-Discussion Issue-Enhancement

Comentários muito úteis

Acabei de publicar um módulo, Native , ( Install-Module Native -Scope CurrentUser ) que convido a todos a experimentarem e cujos comandos estarei usando abaixo ; os casos de uso numéricos referenciados são do comentário de acima .

Se quisermos que a névoa conceitual se esclareça, acho que precisamos enquadrar os casos de uso de maneira diferente.
_Nenhum deles requer alterações na análise_.

Caso de uso [chamada de shell nativo]:

Você deseja executar um _comando individual_ (caso de uso 1) ou uma _linha de comandos múltiplos_ inteira (caso de uso 2) _escrito para o shell nativo da plataforma_ (este problema):

  • Já que você está usando uma sintaxe de shell diferente, você está passando o comando (linha) _como uma única string_ para o shell de destino, para que _it_ faça a análise; A interpolação de string do PowerShell oferece a capacidade de incorporar valores do PowerShell à string passada (caso de uso 2a).
  • ins ( Invoke-NativeShell ) cobre esses casos de uso.
# Use case 1: Single executable call with line continuation, on Unix.
@'
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 
'@ | ins

# Use case 2: Entire Bash command line (also with line continuation)
@'
ps -o pid,args | awk \
  '/pwsh/ { print $1 }'
'@ | ins

# Use case 2a: Entire Bash command line (also with line continuation) with string interpolation.
# Note the double-quoted here-string and the need to escape the $ that is for Bash as `$
$fields = 'pid,args'
@"
ps -o $fields | awk \
  '/pwsh/ { print `$1 }'
"@ | ins

# Alternative to use case 2a: pass the PowerShell value *as a pass-through argument*,
# which allows passing the script verbatim.
# Bash sees the pass-through arguments as $1, ... (note that the `awk` $1 is unrelated).
@'
ps -o $1 | awk \
  '/pwsh/ { print $1 }'
'@ | ins - 'pid,args'

A sintaxe here-string não é a mais conveniente (daí a sugestão de implementar uma variante in-line - veja # 13204), mas você não _tem_ de usá-la; para comandos verbatim, você pode usar '...' , que requer apenas _doubling_ incorporado ' , se presente.

Além disso, aqui está um substituto razoável para o "operador StackOverflow" :

Se você colocar o seguinte manipulador de teclas PSReadLine em seu arquivo $PROFILE , poderá usar Alt-v para criar uma chamada para ins com uma string here literalmente no qual o texto da área de transferência atual é colado.Enter submete a chamada.

# Scaffolds an ins (Invoke-NativeShell) call with a verbatim here-string
# and pastes the text on the clipboard into the here-string.
Set-PSReadLineKeyHandler 'alt+v' -ScriptBlock {
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert("@'`n`n'@ | ins ")
  foreach ($i in 1..10) { [Microsoft.PowerShell.PSConsoleReadLine]::BackwardChar() }
  # Comment the following statement out if you don't want to paste from the clipboard.
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert((Get-Clipboard))
}

Caso de uso [chamada executável direta]:

Você deseja chamar um _único executável externo_ usando a sintaxe do _PowerShell_, com _ argumentos individuais_ (casos de uso 1a e 3).

  • Este é um mandato básico de qualquer shell e é de vital importância que funcione de forma robusta.

    • Você precisa contar com o PowerShell capaz de passar por quaisquer argumentos que resultem da _its_ análise _as-is_ para o executável de destino. Atualmente, este não é o caso - ver # 1995 .
  • Ele requer que você compreenda a sintaxe do _PowerShell_ e os _seus_ metacaracteres de modo de argumento.

    • Você precisa estar ciente de que ` é usado como o caractere de escape e para a continuação da linha.
    • Você precisa estar ciente de que o PowerShell tem metacaracteres adicionais que requerem aspas / escape para uso literal; estes são (observe que @ só é problemático como o primeiro caractere de um argumento.):

      • para shells semelhantes a POSIX (por exemplo, Bash): @ { } ` (e $ , se você quiser evitar a expansão inicial pelo PowerShell)
      • por cmd.exe : ( ) @ { } # `
      • Individualmente ` escapando de tais caracteres. é suficiente (por exemplo, printf %s `@list.txt ).

Enquanto esperamos que # 1995 seja consertado, a função ie (para i nvoke (externo) e xecutável) preenche a lacuna, simplesmente adicionando uma chamada direta; por exemplo, em vez da seguinte chamada:

# This command is currently broken, because the '{ "name": "foo" }' argument isn't properly passed.
curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

você usaria o seguinte:

# OK, thanks to `ie`
ie curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

O módulo Native vem com algo semelhante a echoArgs.exe , o comando dbea ( Debug-ExecutableArguments ); exemplo de saída no Windows:

# Note the missing first argument, the missing " chars., and the erroneous argument boundaries.
PS> dbea '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
7 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <3 of snow Nat>
  <King>
  <Cole c:\temp>
  <1\ a>
  <">
  <b>

Command line (helper executable omitted):

  a&b 3" of snow "Nat "King" Cole" "c:\temp 1\\" "a \" b"

Usando a opção -UseIe ( -ie ), você pode dizer a dbea para passar os argumentos via ie , o que demonstra que corrige os problemas:

# OK, thanks to -UseIe
PS> dbea -UseIe '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
6 argument(s) received (enclosed in <...> for delineation):

  <>
  <a&b>
  <3" of snow>
  <Nat "King" Cole>
  <c:\temp 1\>
  <a \" b>

Command line (helper executable omitted):

  "" a&b "3\" of snow" "Nat \"King\" Cole" "c:\temp 1\\" "a \\\" b"

Nota: Uma vez que # 1995 é corrigido, ie não é mais necessário; no interesse da compatibilidade futura, ie foi projetado para detectar e adiar automaticamente para uma correção; ou seja, uma vez que a correção esteja implementada, ie irá parar de aplicar suas soluções alternativas e irá efetivamente agir como uma chamada direta / & .

Caso de uso [chamada direta executável do Windows não autorizado]:

Existem dois problemas principais, que surgem no _Windows only_:

  • Você está invocando um executável "não autorizado" que possui requisitos de cotação que diferem da convenção mais amplamente usada ; por exemplo, msiexec.exe e msdeploy.exe exigem que prop="<value with spaces>" seja citado precisamente dessa forma - a citação apenas em torno da parte _valor_ - embora "prop=<value with spaces>" - citação de todo argumento - _deve_ ser equivalente (o último é o que o PowerShell - justificadamente - faz nos bastidores).

  • Você está invocando _um arquivo em lote_ com _um argumento que não contém espaços, mas contém cmd.exe metacaracteres_ como & , ^ ou | ; por exemplo:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell - justificadamente - passa http://example.org/a&b _unquoted_ (já que não há nenhum espaço em branco incorporado), mas isso quebra a invocação do arquivo em lote, porque cmd.exe - irracionalmente - sujeita os argumentos passados ​​para um arquivo em lote ao mesma regra de análise que se aplicaria dentro de cmd.exe vez de aceitá-los como literais.

    • Observação: embora o uso de arquivos em lote para implementar _diretamente_ a funcionalidade esteja provavelmente diminuindo, usá-los como _pontos de entrada CLI_ ainda é muito comum, como o az CLI do Azure, que é implementado como arquivo em lote az.cmd .

Nota: ie lida automaticamente com esses cenários para arquivos em lote e para msiexec.exe e msdeploy.exe , portanto, para _most_ chamadas, nenhum esforço extra deve ser necessário ; entretanto, é impossível antecipar _todos_ os executáveis ​​"invasores".

Existem duas maneiras de resolver isso:

  • Dado que cmd.exe , após aplicar sua própria interpretação aos argumentos em uma linha de comando, _does_ preserva as aspas conforme especificado, você pode delegar a uma chamada ins no Windows:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Se você usar uma string _expandable_, isso novamente permitirá que você incorpore valores do PowerShell na string de comando.

      • $url = 'http://example.org/a&b'; ins ".\someBatchFile.cmd `"$url`""

  • Como alternativa, use a implementação --% atual, mas tome cuidado com suas limitações:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Dado como --% é implementado, a única maneira de incorporar valores do PowerShell é - desajeitadamente - definir um _aux. variável de ambiente_ e referenciá-la com a sintaxe %...% :

      • $env:url = 'http://example.org/a&b'; .\someBatchFile.cmd --% "%url%"


Manipulação de erros

O https://github.com/PowerShell/PowerShell-RFC/pull/88 pendente trará melhor integração de tratamento de erros com executáveis ​​externos (nativos).

Nesse ínterim, o módulo Native vem com dois recursos para ad-hoc opt-in para tratar um código de saída diferente de zero como um erro de encerramento de script :

  • ins suporta a opção -e / -ErrorOnFailure , que lança um erro se $LASTEXITCODE for diferente de zero após a chamada ao shell nativo.

  • iee é uma função de empacotamento para ie que analogamente lança um erro se $LASTEXITCODE for diferente de zero após a chamada para o executável externo.

Existem muitas sutilezas nos comandos que acompanham o módulo.
A ajuda baseada em comentários bastante extensa, esperançosamente, cobre-os suficientemente.

Todos 189 comentários

Gosto da ideia e acho que é útil, mas acho que seria muito mais útil (estou pensando especificamente no caso de uma DSL) se eu puder obter uma expansão de cadeia única. Então, se eu construí uma string compatível com cmd.exe em meu PS, tenho uma maneira de string literalmente com cmd.exe. Possivelmente

&n -command $string

Isso também me permitiria especificar meu intérprete:

&n -shell /bin/sh -command $string
ou
&n -shell cmd.exe -command $string

em termos de nome / operador, é "&!" em uso? Isso tem semelhança com shbang ("#!").

$! é uma boa sugestão!

O shell pode ser especificado simplesmente especificando-o como o comando:

&! /bin/sh -c blah

A expansão $var cria um problema em que $ pode precisar ser literal para ser passado para o comando / shell nativo. Um dos problemas que isso tenta evitar é quando as pessoas precisam descobrir como escapar de tudo adequadamente. Se você precisar de expansão variável, você sempre pode fazer:

$mycommand = "/bin/sh ls"
Invoke-Expression "&! $mycommand"

No entanto, nos casos em que você deseja misturar o PowerShell com o comando nativo, provavelmente é melhor usar a sintaxe atual e apenas estar ciente do que precisa ser escapado corretamente, pois eu consideraria esse uso avançado.

$mycommand = "/bin/sh ls"
Invoke-Expression "&! $mycommand"

Eu não posso acreditar que você acabou de sugerir iex. :-)

@essentialexch , algumas vezes é apropriado :)

Observe que no exemplo, a expectativa é que o usuário valide corretamente o conteúdo de $mycommand antes de executar!

Uma boa redação e uma solução para problemas como o que você descreveu são definitivamente necessários.
Eu mesmo não sabia sobre --% e não me lembro de ter visto isso, infelizmente. Portanto, o operador &n proposto pode sofrer de problemas de descoberta semelhantes e não parece natural para mim combinar um símbolo com um caractere (e isso me lembra% f em declarações C printf). Já que é uma alteração importante. Existe uma razão pela qual não poderia ser, por exemplo && ?
Talvez seja mais intuitivo usar algo como colchetes (ou colchetes?) Para marcar a área que você deseja executar. Exemplo: & [ wsl ls ] | less

Eu concordo que &! é a maneira mais intuitiva de ir para aqueles que vêm de outros shells, no entanto, no super-duper-linter geralmente estou executando comandos nativos construindo um array de parâmetros e, em seguida, dividindo-o para o comando.

$command = 'linter'
$myargs = @(
    '-config'
    'linterconfig.path'
)
if ($Test) {$myargs += 'test'}
& $command <strong i="7">@myargs</strong>

Portanto, esta é a melhor maneira no caso de condicional e outros enfeites para mim

@bergmeister Eu originalmente pensei em && porque visualmente é semelhante a & que muitas pessoas conhecem hoje. No entanto, && sendo um operador de cadeia de pipeline pode causar confusão sobre o que ele deve fazer. Eu gosto da sugestão &! atualmente.

@JustinGrote , não há razão para não continuar a usar isso. a sintaxe &! é realmente para casos em que você deseja apenas recortar e colar ou tem alguns argumentos que entram em conflito com a sintaxe do PowerShell e não deseja ou não sabe como escapar corretamente

Oh cara, eu me lembro de ter conversado com Bruce e Jason sobre isso há uma década. Minha intuição é que inventar outro operador parece desnecessário aqui. Eu sei que algumas pessoas neste tópico não ouviram sobre --% mas aposto que há mais do que você pensa. O que há de errado com:

# run ls in wsl, return results to powershell, then pipe to wsl again, to grep.
& wsl ls | wsl grep -i "foo"  

#  the entire pipeline right of wsl will run in wsl. 
& --% wsl ls | grep -i "foo"

Por que ninguém sugeriu isso? É logicamente consistente com o uso de --% outro lugar dizer "tudo depois disso deve ser passado sem tratamento especial com PowerShell".

@oising bem para um, esse comando não funciona porque você tem que precedê-lo com o comando que deseja executar, você está sugerindo adicionar a funcionalidade?
image

Isso meio que faz o que é esperado:
function Invoke-LiteralCommand ($command) {Invoke-Expression "& $command --% $args"}

Invoke-LiteralCommand ping -W 200 www.google.com | grep icmp

@JustinGrote Sim, eu sei que não funciona agora :) Estou sugerindo que _em vez_ de &n ou &! ou qualquer outra coisa que esteja sendo falada. Simplesmente faz mais sentido para mim: & é chamada e --% é suprimir a análise do PowerShell; juntos, eles são coerentes e mais detectáveis ​​do que adicionar algo novo. Não gosto da ideia de ter uma operadora "chamada" e uma "chamada nativa".

Ampliei meu exemplo para mostrar as diferenças.

@oising Eu ficaria bem com isso em relação a um novo operador, embora seja muita digitação obtusa para "novo usuário do PowerShell que conhece o bash", que é o que eu presumo que isso serve.

@oising Eu ficaria bem com isso em relação a um novo operador, embora seja muita digitação obtusa para "novo usuário do PowerShell que conhece o bash", que é o que eu presumo que isso serve.

De modo nenhum. É para o usuário do PowerShell que não entende as regras complexas de cotação entre PowerShell / cmd.exe / bash. Como o título do problema diz "chamar nativos", para chamar executáveis ​​nativos.

@oising Eu ficaria bem com isso em relação a um novo operador, embora seja muita digitação obtusa para "novo usuário do PowerShell que conhece o bash", que é o que eu presumo que isso serve.

De modo nenhum. É para o usuário do PowerShell que não entende as regras complexas de cotação entre PowerShell / cmd.exe / bash. Como o título do problema diz "chamar nativos", para chamar executáveis ​​nativos.

Verdade, ou para aqueles de nós que preferem não ter que pensar sobre eles.

Sim, estou com @oising. Esse conceito existe, é lamentavelmente insuficiente. Se vamos implementar algo completamente novo, é melhor descontinuar / remover a sintaxe antiga.

Sinto que, muitas vezes, essas ideias são expressas, mas não é dado peso suficiente ao seu público-alvo real. Se os usuários _já_ estão lutando para encontrar os métodos existentes e encontrando os métodos existentes que faltam quando são encontrados, é uma chamada para a) melhorar a documentação eb) melhorar a funcionalidade real dos operadores existentes.

Em vez disso, obtemos uma opção estranha c) que supostamente aborda de alguma forma os problemas anteriores, enquanto introduz ainda _outro_ operador que depende de documentação que não é acessível o suficiente para os usuários a encontrarem naturalmente.

Concordo que as mensagens de erro e / ou o sistema de sugestões devem ser usados ​​para ajudar a introduzir esses conceitos. Não concordo que precisamos de um terceiro? quarto? quinto? (Eu literalmente perdi a conta, alguém me ajude aqui) maneira de invocar comandos. Os métodos existentes são insuficientes - isso significa que devemos _melhorá-los_, não deixá-los para trás como teias de aranha para confundir ainda mais os usuários quando eles começarem a explorá-los.

A primeira pergunta que costumo fazer quando as pessoas percebem que há 5 maneiras de fazer algo é "por que há 2 ou 3 maneiras que literalmente fazem a mesma coisa, mas não funcionam tão bem", e minha resposta é a mesma de sempre - - a equipe PS está _muito_ muito focada em garantir que tudo da última década ++ ainda funcione, ao tentar adicionar / melhorar a funcionalidade. Aqui vemos novamente, temos implementações existentes, mas insuficientes que precisam de revisão e melhoria, e a solução proposta é turvar ainda mais o pool, adicionando outra implementação potencialmente incompleta.

Devíamos terminar o que começamos, não começar outra implementação de novo, IMO.

Acredito que se formos usar e para mais do que alguns casos de uso exclusivos, devemos começar a pensar em tornar isso considerado um verbo ou observar a padronização em toda a linguagem.

A última coisa que eu quero é confusão sobre o que e devo fazer. Na sua totalidade.

Eu consideraria três cenários:

  1. Expansão total - comportamento atual do PowerShell.
  2. Literal completo - isso é proposto no OP.
  3. Expansão parcial - wsl ls $path ainda funciona

Eu me pergunto onde @ mklement0 comentários? :-)

No início da leitura deste tópico, pensei; boa ideia! Mas depois de ler os comentários, comecei a pensar. Sempre achei o comando & 'não vale a pena usar os padrões do PS "e algo que me lembra os' dias de PERL '(ugh). A introdução de outro comando não padrão do PS (não sendo substantivo-verbo) não ajudará. Certamente, não os menos experientes PS.

Eu também não sabia sobre o parâmetro -%. Eu uso o mesmo princípio de @JustinGrote como solução.

Cortar / colar comandos no shell PS nunca foi minha principal 'coisa'.
Não seria melhor substituir esses comandos nativos por um do PowerShell?
Crie caminho para substituir cmd.exe inteiramente ...

Eu voto por explorar a melhoria dos comandos existentes. E - de fato - melhorando alguma documentação sobre -% e & uso

@iSazonov, o caso de uso que @ SteveL-MSFT está propondo é o cenário 'cortar e colar'. A expansão parcial tornaria as coisas muito mais difíceis, eu acho (do lado AST das coisas)

Imediatamente embora de “#!” e depois "!#". Gostando do “&!”.

Provavelmente, podemos examinar a adição de aceleradores de token às chamadas de função no tokenizer. Isso permitiria o uso do seguinte.

Iex -l
Ou Invoke-Expression -literal que interrompe a análise antes de passar.

Acho que adicionar mais tokens exclusivos no nível de análise aumenta a barreira de entrada e diminui a capacidade de descoberta desse recurso.

Get-Help &, por exemplo, não aparece com nada. E temos que procurar em about_Operators.

No entanto, existem casos únicos para o call_operator que não são documentados.
& modulename {comando}
permite que você escopo em um módulo. E tenho certeza de que há mais. Mas a capacidade de descoberta é tão baixa que se torna difícil encontrá-la na documentação nativa. E a comunidade só sabe disso através do JSnover e de uma palestra que ele deu mostrando coisas legais.

Acredito que tudo o que for decidido precisa levar totalmente em conta o quão detectável esse "recurso" é, de uma nova perspectiva de usuário e tenha em mente que os usuários mais novos tentarão usar isso no Powershell 5, não sem saber seu novo núcleo do pwsh se a descoberta é muito baixa.

Eu adoraria que mudássemos o comportamento existente, mas mudar uma API tão fundamental no PowerShell simplesmente quebraria muitas coisas. Podemos considerar a migração com uma configuração.

Quebrar as coisas mais diretamente pode ter sido possível antes do PS 6 virar GA, mas honestamente, mesmo assim, teria sido uma séria ameaça à compatibilidade com versões anteriores (não muito diferente da função print() do Python).

Em termos de passagem de argumentos para subprocessos, acho que tanto a invocação síncrona quanto a invocação Start-Process já têm problemas históricos discutindo uma revisão, e minha impressão é que ambas precisam investir ao mesmo tempo para consistência. O Start-Process em particular precisa atualizar seu suporte para passar uma série de argumentos.

Para invocação síncrona com passagem de argumento de cmdline, vejo quatro tipos de passagem de argumento possíveis:

  • Comportamento atual, que se tornaria legado, que está sujeito a CommandLineToArgvW no Windows e tem resultados inesperados
  • Comportamento padrão, que deve passar argumentos por seu valor de expressão, mas aplicar as regras usuais de tokens de palavras de barra, de forma que coisas como > e | separem comandos. Neste modo, o valor que um subprocesso obtém da expressão deve ser o que é exibido por Write-Host $val
  • Comportamento literal, em que todos os tokens são interpretados como strings de bareword até que o token de escape seja visto. Isso é o que --% deve fazer hoje, mas idealmente teria apenas um token de extremidade que pode ser incorporado na mesma linha como --% ... %--
  • Comportamento de linha de leitura Bash, em que uma lógica de escape alternativa orientada para sh é aplicada, permitindo uma compatibilidade mais simples

Acho que o primeiro comportamento deve ser gradualmente eliminado, introduzindo o segundo como um recurso experimental e, em seguida, trocando-os.

O segundo e o terceiro comportamentos podem ter seus próprios sigilos como &! e &# ou talvez +% ... -% ou algo assim. Mas, para o modo literal, acho que uma faceta importante seria simplificar o token de escape para que mais tokens sejam obtidos literalmente. Semelhante a um heredoc.

Se a solicitação for apenas para abordar o cenário de copiar e colar como "Qualquer conteúdo de texto após este operador chamará o" shell padrão "do sistema operacional para executar." por que não dizer isso explicitamente por shell ?
`` `powerhell
PS> shell wsl ls | Menos
PS> (shell wsl ls * .txt) | Select-String Hello

É mais detectável e legível do que os operadores criptografados no estilo Forth / APL.

Eu absolutamente não vejo como isso resolveria # 1995: consulte https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653174261

Além disso, o problema com WSL é um problema de comportamento de wsl.exe (consulte https://github.com/PowerShell/PowerShell/issues/12975#issuecomment-650353021) e não de powershell.
(Ok, há um problema com o PowerShell, mas isso é # 1995)

Ah, e isso não é quase uma duplicata de https://github.com/PowerShell/PowerShell/issues/12975 ? Porque posso basicamente repetir o que mencionei lá:

@bitcrazed
O problema é que a falta de capacidade de delimitar uma parte de uma linha de comando a ser passada varbatim para o comando / script receptor é algo que confunde os usuários o tempo todo.
O que você quer dizer com "parte de uma linha de comando a ser passada varbatim"? Você quer dizer 1. passar alguma sequência de caracteres literalmente para o executável chamado, de modo que o executável chamado tenha esta sequência de caracteres em um elemento de sua matriz de argumento (por exemplo, esses caracteres estão então disponíveis em `argv [1]` em [principal ] (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019#command-line-arguments)) Ou você quer dizer 2. inserir alguma sequência de caracteres literalmente no parâmetro [`lpCommandLine` de` CreateProcess`] (https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#parameters) - - Se você quer dizer (1.), então essencialmente já funciona:
PS /home/User> /bin/echo 'cd / && ls . | cowsay'
cd / && ls . | cowsay
PS /home/User>
(Exceto para o problema de citações incorporadas, conforme discutido em https://github.com/PowerShell/PowerShell/issues/1995) Pode-se argumentar que adicionar uma string de uma linha aqui melhoraria alguns casos de uso, mas eu acho, esse não é realmente o ponto da questão. Como isso já funciona mais ou menos, suponho que você quis dizer (2.) --- Se você quis dizer (2.), deixe-me expressar minha opinião sobre isso de uma forma um tanto dramática: Por favor, por favor, não adicione sintaxe especial para isso. Isso é basicamente o que `-%` tentou fazer, o que também nunca deveria ter sido implementado. Por que sou tão fortemente contra isso? 1. É um problema apenas do Windows, portanto, adicionar sintaxe significaria que o PowerShell no Windows tem uma sintaxe diferente do Linux. (Ou a sintaxe seria suportada, mas é totalmente sem sentido, como é atualmente o caso para `-%`) 2. Se o shell de linha de comando principal no Windows publicado pela Microsoft adicionar um recurso de primeira classe (via sintaxe especial em oposição a via um cmdlet) para chamar executáveis ​​que não seguem as [regras de análise de linha de comando] típicas (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs -2019 #parsing-c-command-line-arguments) (se a ferramenta segue as regras típicas, você não precisa de (2.), você pode usar melhor (1.)), então isso incentiva os autores de linha de comando ferramenta, para não seguir essas regras, o que só piora a ["anarquia de linha de comando do Windows"] (https://stackoverflow.com/a/4094897/2770331). Quanto menos as pessoas seguirem as regras típicas, mais difícil será chamar executáveis ​​externos de maneira programática ou, em geral, escrever código de plataforma cruzada, então definitivamente acho que os autores de programas devem ser encorajados a seguir essas regras típicas. 3. Duvido muito que este seja um caso de uso comum> E este não é apenas um problema que afeta WSL: também afeta as chamadas de linha de comando wt do Terminal do Windows e muitas outras ferramentas. Você poderia adicionar alguns exemplos de onde esses problemas ocorrem? Porque no caso do WSL, eu diria que a análise do WSL da linha de comando está simplesmente [quebrada] (https://github.com/Microsoft/WSL/issues/1746) (o problema era sobre `bash.exe`, mas a situação é por padrão não é melhor com `wsl.exe`) no caso padrão - eu consideraria todas as ferramentas que não seguem as [regras de análise de linha de comando] típicas (https://docs.microsoft.com/en-us/ cpp / cpp / main-function-command-line-args? view = vs-2019 # parsing-c-command-line-arguments) quebrado, mas o comportamento padrão do WSL é IMHO nem mesmo devidamente documentado ... Eu disse o "padrão "o comportamento de` wsl.exe` está quebrado - ao escrever esta resposta, percebi que `wsl.exe` realmente parece se comportar como esperado, ao usar` -e`:
PS C:\> wsl -e bash -c 'cd / && ls . | cowsay'
 _______________________________________
/ acct bin boot cache cygdrive data dev \
| etc home init lib lib64 lost+found    |
| media mnt opt proc root run sbin snap |
\ srv sys tmp usr var                   /
 ---------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
PS C:\>
Portanto, a única coisa que falta para este caso de uso explícito é um parâmetro IMO para `wsl.exe` para chamar o shell padrão com os argumentos da linha de comando analisados ​​como em qualquer outro exe normal.

A única coisa a respeito de "parte de uma linha de comando a ser passada varbatim para o comando / script receptor", que seria melhorada com este operador seria o significado (2.) acima, e como mencionado, acho que desenvolver nessa direção é ruim.

Se tudo o que você deseja fazer é um atalho para copiar e colar as linhas de comando existentes, por que não adiciona um cmdlet (por exemplo, Invoke-Shell , que chama bash -c no linux e cmd /c no janelas?
Então você poderia apenas fazer

Invoke-Shell @'
whatever existing comandline containg '"quotes"' or whatnot
'@

Se for uma linha, adicione a sintaxe de uma linha aqui, mas não implemente um operador especial, que não pode ser encontrado corretamente e só complica tudo.

E, por favor, não abandone # 1995 por isso!

+10 pontos por usar a palavra "sigilo".

O que é bom sobre &! versus shell é que "shell" é uma palavra comum em inglês, portanto, as pessoas podem ter um comando existente chamado shell. (Na verdade, tenho uma função chamada "shell" e uma função chamada "n" (portanto, não gosto de &n ).)

@oising : & --% é bom, exceto que um dos propósitos expressos do recurso proposto é significar que | se torna algo que é passado para o shell nativo; não é algo que o traz de volta à análise do PowerShell. E se tornarmos a precedência do pipeline diferente para & --% e para foo $blah --% more args , eu acharia isso desconcertante ... e se você fizesse & $foo --% more args | blah ? Portanto, em minha opinião, o &! proposto é diferente o suficiente para justificar ter um operador diferente. (Colocado de outra forma: a funcionalidade proposta está relacionada, mas é diferente de combinar & e --% .)

Esse recurso é mais do que apenas para "novo usuário do PowerShell que conhece o bash" e "usuário que não conhece todas as regras complexas de cotação / escape". É também para os usuários mais experientes, que desejam apenas copiar / colar um comando do StackOverflow sem ter que entrar e mexer em tudo. Isso acontece comigo um pouco - um produto no qual trabalho tem muita documentação sobre como fazer as coisas, e a língua franca são os comandos cmd.exe - então eu tenho que tentar consertar% VARIABLE_REFERENCES% etc. .; seria muito mais agradável digitar &! e depois colar. Na verdade, se isso for implementado, provavelmente irei me referir a ele como "o operador StackOverflow". : D

@ vexx32 : Este não é o operador de chamada "um toque para controlar todos"; apenas mais uma ferramenta na caixa de ferramentas para resolver um problema comum. Não acho que devemos nos livrar das ferramentas existentes para abrir espaço para esta; eles fazem coisas diferentes. Eu entendo que os novos usuários podem se sentir sobrecarregados quando a caixa de ferramentas tem um martelo, uma chave de fenda, uma broca, uma broca de impacto e uma pistola de pregos ... mas apesar do fato de que todos eles são para "cutucar uma coisa minúscula em um ou mais coisas ", são inestimáveis ​​para um profissional; todos usados ​​em situações diferentes (embora relacionadas).

@TSlivede : Concordo que # 1995 é separado. Mas eu discordo de "É um problema apenas do Windows": copiar e colar comandos específicos da plataforma do StackOverflow se aplica igualmente a qualquer sistema operacional. (Na verdade, isso seria muito útil para muitas pessoas como eu, que são mais fortes em uma plataforma do que em outra - sou mais proficiente com as tecnologias do Windows, portanto, quando estou no Linux, tenho a tendência de copiar e colar MUITAS do SO.)

Seja por meio de um novo operador &! ou algum comando Invoke-Shell , eu não tenho sentimentos tão fortes quanto os outros ... Eu simpatizo com as pessoas reclamando que os operadores são difíceis de pesquisar, etc. mas Eu realmente gosto da concisão de &! .

@jazzdelightsme esta não é "outra ferramenta na caixa de ferramentas" tanto quanto é "outro acessório de chave de fenda incrivelmente específico para uma chave de fenda que já tem cerca de 3-5 que já estão _okay_ e podem ser melhoradas" na minha opinião.

@jazzdelightsme A parte "somente Windows" talvez esteja mais relacionada ao problema em que eu postei originalmente essa declaração - sim, OK, esta não é uma duplicata exata desse problema.

Se # 1995 não for abandonado e o recurso solicitado aqui não for resolvido por meio de sintaxe especial, mas por meio de um commandlet (ou talvez uma nova sintaxe para uma string de uma linha que funcione em todos os lugares, não apenas neste caso especial), então sou realmente a favor de adicionando esta funcionalidade. (Ou talvez não "a favor", mas "Isso é totalmente aceitável para mim")

Outra ideia: se o problema é realmente sobre copiar e colar comandos completos destinados a shells nativos, talvez o PsReadLine pudesse adicionar um recurso para traduzir aproximadamente um comando do estilo bash ou cmd para o PowerShell. A tradução pode ser disparada por uma combinação especial de teclas ou automaticamente se ocorrer um erro de análise ao tentar executar um comando.

Isso teria a vantagem adicional de ensinar aos usuários a sintaxe do PowerShell.

Com relação à descoberta: PsReadLine sempre pode exibir uma mensagem por um curto período de tempo que a combinação de teclas específica pode ser usada, sempre que um conteúdo sintaticamente incorreto (que seria válido após a tradução) é colado.

Mas eu poderia entender se isso fosse um pouco complexo para perceber.

@jazzdelightsme você falou com @joeyaiello? Achei que só ele o chamou de StackOverflow operator que me fez estremecer, embora seja muito verdadeiro

@ SteveL-MSFT Não, não falei com ninguém sobre isso; Cheguei ao "operador StackOverflow" de forma independente. xD

@jazzdelightsme

... exceto um dos propósitos expressos do recurso proposto significa que | torna-se algo que é passado para o shell nativo; não é algo que o traz de volta à análise do PowerShell.

Não tenho certeza se você entendeu minha sugestão; o que você diz é _precisamente_ o que estou dizendo. Deixe-me ser mais prolixo com algumas dicas estrategicamente capitalizadas: D

# NON-OPTIMAL SOLUTION: run "ls" in wsl, return results to powershell, then pipe to wsl again, to "grep."
& wsl ls | wsl grep -i "foo"  

# NEW SOLUTION: the entire pipeline/string, INCLUDING THE PIPE SYMBOL, right of wsl will be executed in wsl. 
& --% wsl ls | grep -i "foo"

# NEW SOLUTION: the entire pipeline AS ABOVE is sent to WSL, then the results are returned to powershell
# and piped, in powershell, to foreach-object
(& --% wsl ls | grep -i "foo") | % { "match {0}" -f $_ }

# NEW SOLUTION: allow placing --% beyond first argument
# pass everything right of --% to the command in $wsl 
$wsl = gcm wsl
& $wsl --% ls | grep -i "foo"

# or

& wsl --% ls | grep -i "foo"

# NEW SOLUTION: execute multiline pasted code without powershell parsing.
& --% @'
(pasted multiline code)
@'

O que estou perdendo aqui? Se o tubo aparecer depois de --% , é apenas outro caractere idiota: um argumento. Não há como alterar a precedência do operador. Se estiver contido em um pipeline ou subexpressão aninhada, comece a analisar o PowerShell novamente.

e se você fizesse & $ foo -% more args | blá?

Bem, isso tentaria executar o argumento em $foo e passar "more" , "args" , "|" e "blah" para ser tratado como qualquer coisa em $foo deseja. As regras parecem bastante simples para mim, mas admito que tenho usado o Powershell / Mônada há muito tempo. Esta não é uma mudança importante para & pois & $foo --% ... trata --% como qualquer outro argumento. Então, a menos que estejamos usando este sigilo em outro lugar nefastamente, devemos estar seguros.

Pensamentos?

Fiz várias edições acima, para cobrir a avaliação de linha parcial, avaliação de várias linhas e posicionamento de variável de --% . Tudo isso sem introduzir novos sigilos, operadores ou sintaxe estranha. Eu acredito que isso é suficiente. A única coisa é: o analisador pode lidar com isso? O que você acha @lzybkr @JamesWTruher @BrucePay ?

Ouça, ouça, @TSlivede.

Perdoe-me por ser franco, mas já falamos sobre essas questões há muuuuito tempo:

O que esta edição propõe equivale a pensamento mágico:
Ele está relacionado ao malfadado e inerentemente problemático "símbolo de parada de análise", --% , que em sua forma atual é de uso limitado _ no Windows_ (consulte https://github.com/MicrosoftDocs/PowerShell- Docs / issues / 6149) e praticamente inútil em _plataformas semelhantes ao Unix_ (consulte https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4963).
--% nunca deveria ter sido implementado, e nem esta proposta deve ser implementada como apresentada.

O pensamento mágico é uma fusão conceitualmente confusa de duas idéias:

  • (a) "Quero passar tokens separados por espaço para algum programa externo, permitindo quaisquer barewords, sem precisar me preocupar com metacaracteres de shell, como & ou | ." - consulte https://github.com/PowerShell/PowerShell/issues/12975#issuecomment -646276628

    • Claro, tal recurso está em desacordo com o uso de tais comandos _como parte de um pipeline_ ou com _operadores de encadeamento de pipeline_ ( && e || ), e também colocando comandos adicionais na mesma linha, após ; - e, não menos importante, ser capaz de usar quaisquer variáveis ​​e expressões _PowerShell_ na linha de comando.
  • (b) "Quero que minha linha de comando seja tratada da maneira que estou acostumado a _do shell que normalmente uso_ (ou do shell para o qual a linha de comando foi originalmente escrita)", que, é claro, _está_ sujeito ao uso não mencionado de caracteres como & ou | sendo interpretados como metacaracteres e, portanto, tendo um significado _sintático_.

    • Caso em questão: nem cmd nem /bin/sh (ou qualquer shell do tipo POSIX, como bash ) aceitaria wsl ls | less de uma forma que passa ls , | e less _verbatim_ até wsl - todos interpretariam _não cotados_ | como _their_ símbolo de barra vertical.

Essas ideias estão em desacordo e requerem soluções distintas - nenhuma das quais requer sintaxe especial per se:

  • A solução para (a) é _abandonar_ a ideia e, em vez disso, usar a sintaxe existente do _PowerShell_ para garantir que os metacaracteres sejam usados ​​_verbatim_ - o que invariavelmente necessita de _quoting_ ou _escaping_ ,:

    • Ou: aspas barewords (tokens não entre aspas) que contêm metacaracteres _como um todo_: wsl ls '|' less
    • Ou: ` -escape metacaracteres _individualmente_ em barewords: wsl ls `| less
    • Ou: use um _array [variável] _ para passar os argumentos _verbatim_: $wslArgs = 'ls', '|', 'less'; wsl $wslArgs
    • Claro, # 1995 demonstra que, lamentavelmente, essa abordagem é e sempre foi quebrada quando se trata de argumentos que _incorporaram_ " _quando chamar programas externos_ - _isso precisa ser resolvido_: veja abaixo.
  • A solução para (b) é realmente _chamar o shell que eu normalmente uso_, passando a linha de comando _como uma única string_.

    • Como @TSlivede sugere, um Invoke-Shell cmdlet para o qual você passa a linha de comando _como uma única string_ e que chama o _shell nativo da plataforma_ é a solução - e já temos sintaxe para strings textuais ( '...' ou sua variante here-string, @'<newline>...<newline>'@
    • Minha recomendação é chamá-lo de Invoke-NativeShell , e de /bin/sh - não bash - em plataformas do tipo Unix.

      • Além disso, sugiro definir ins como um alias sucinto.

    • Esta abordagem resolve todos os problemas conceituais com --% e com a proposta em mãos:

      • Torna muito óbvio onde termina o comando passado para o shell nativo (em virtude de ser _uma única string_) e permite usar uma chamada Invoke-NativeShell / ins em um pipeline regular do PowerShell / com Redirecionamentos do PowerShell sem ambigüidade.

      • Você pode opcionalmente usar uma string _expandable_ ( "...." ou sua variante here-string) para incorporar valores de variáveis ​​_PowerShell_ na linha de comando (algo que --% não pode fazer).


Uma vez que os poderes concluíram que # 1995 não pode ser corrigido para que a compatibilidade com versões anteriores seja quebrada - afinal, todas as soluções alternativas existentes seriam interrompidas, consulte https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 - o a melhor coisa que podemos fazer é apoiar o opt-in no comportamento adequado com uma sintaxe que seja o mais sucinta possível, de modo a minimizar a dor de ter que sair do seu caminho para obter o comportamento que deveria sempre ter sido o padrão.

Portanto: sintaxe especial como &! só deve ser fornecida como opção para obter a transmissão adequada de argumentos para programas externos a fim de abordar # 1995 , com base no comportamento proposto no RFC de @TSlivede - consulte https://github.com/PowerShell/PowerShell-RFC/pull/90. Se uma versão futura do PowerShell tiver permissão para quebrar a compatibilidade com versões anteriores, então é a invocação / invocação direta com & que deve apresentar esse comportamento.

Da perspectiva do usuário, isso significa: Tudo o que preciso fazer é satisfazer os requisitos de sintaxe do _PowerShell_ - consulte https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -640711192 - e posso confiar no PowerShell para passe de forma robusta os tokens verbatim resultantes para o programa de destino - _este é um mandato principal de qualquer shell, que o PowerShell infelizmente nunca satisfez com relação a programas externos_.

Por exemplo, o seguinte comando - que atualmente _não_ funciona como planejado (quando invocado diretamente ou com & - veja este problema com a documentação ) - deve funcionar:

PS> &! bash -c 'echo "one two"'
one two   # Currently (invoked with & instead of &! or with neither) prints only 'one', 
          # because PowerShell passes the entire argument (brokenly) as  "echo "one two"",
          # which the target program sees as *2* verbatim arguments `echo one` and `two`

Aqueles que procuram uma solução alternativa nas versões atuais / anteriores do PowerShell podem encontrar uma função simples que cobrirá a maioria dos casos de uso em https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -303345059 (limitações discutidas em https: // github.com/PowerShell/PowerShell/issues/1995#issuecomment-649560892), e uma versão mais elaborada em https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606

PS: O problema de descoberta de operadores (não apenas baseados em símbolos) é um problema separado que vale a pena abordar por si só: a solução é _melhorar Get-Help para permitir a pesquisa de operador_ - consulte # 11339, que também links para uma solução provisória.

Uma vez que os poderes concluíram que # 1995 não pode ser corrigido para que a compatibilidade com versões anteriores não seja quebrada - afinal, todas as soluções alternativas existentes seriam quebradas, consulte # 1995 (comentário) - a melhor coisa que podemos fazer é apoiar o opt-in no comportamento adequado com uma sintaxe tão sucinta quanto possível, de modo a minimizar a dor de ter que sair do seu caminho para obter o comportamento que sempre deveria ter sido o padrão.

@ mklement0 Acho que a adesão fazia parte de https://github.com/PowerShell/PowerShell/issues/1995 e foi rejeitada. Então, para mim, parece um pensamento positivo. Estou errado?

@AndrewSav , originalmente apenas discutimos um opt-in por meio de configuração ou variável de preferência.

No entanto, @joeyaiello disse o seguinte em https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653164987 quando fechou o problema (ênfase adicionada):

Qualquer coisa que tocarmos aqui é uma mudança significativa, e devemos resolver o problema com um novo operador

Um novo operador _é_ uma espécie de opt-in, mas sem problemas, espero (embora a necessidade dele seja lamentável, mas esse é o preço da compatibilidade com versões anteriores).

Embora esta proposta supostamente também resolva # 1995, não resolve, visto que visa (principalmente) acomodar a sintaxe de _outros_ shells. Visto que vale a pena resolver ambos os problemas, de forma independente, minha proposta foi esta:

  • Vamos apresentar um novo operador, mas usá-lo apenas para consertar # 1995 (via https://github.com/PowerShell/PowerShell-RFC/pull/90), ou seja, para consertar _own_ (não emulação de outro shell) do PowerShell quebrado tratamento da passagem de argumentos para programas externos.

    • O novo código deve sempre usar &! (ou qualquer forma que decidirmos) em vez de & ao chamar programas externos (você pode considerá-lo obsoleto & para programas externos) .
    • Ao chamar comandos _PowerShell_, &! deve agir como & agora (nunca houve um problema), permitindo assim o uso consistente de &! .
  • Para resolver o problema de reutilização de linhas de comando de outros shells, vamos implementar um cmdlet Invoke-NativeShell / ins que chama o shell nativo apropriado para a plataforma ( cmd /c no Windows , /bin/sh -c em plataformas do tipo Unix) com a linha de comando passada como uma _cadeia única_.

    • No caso mais simples, sem ' presente na linha de comando, uma string regular entre aspas funcionará; por exemplo:

      ins 'ls | cat -n '

    • Com ' presentes, eles podem ser _dobrados_; por exemplo:

      ins 'echo' 'hi' '| cat -n '

    • ou, se o desejo é não ter que tocar na linha de comando, uma string here pode ser usada (como já mostrado por @TSlivede) - embora esta sintaxe seja um pouco mais complicada, ela sempre deve funcionar:

      ins @ '
      echo 'oi' | cat -n
      '@

    • além disso, usando uma string _expandable_ (interpolando) ( "..." ou @"<newline>...<newline>"@ ), você tem a opção de incorporar variáveis ​​_PowerShell_ na string passada para o shell nativo (algo que --% não suporta):

      $ foo = 'oi'
      ins @ "
      echo '$ foo' | cat -n
      "@

    • Nota:

      • Invoke-NativeShell cmdlet seria um invólucro bastante fino em torno de cmd /c / /bin/sh -c chamadas, que você poderia obviamente fazer diretamente por si mesmo, mas tem a vantagem de que você não precisa conhecer o nome do executável específico e a sintaxe de passagem de linha de comando.
      • Como um efeito colateral potencialmente benéfico, você será capaz de usar parâmetros comuns, como -OutVariable , e pode pensar se deve deixar os parâmetros -Error* agirem em _stderr_ (embora, para simetria, Invoke-Command deveria, o que seria uma alteração significativa).

        • Uma vez que shells do tipo POSIX suportam _múltiplos_ argumentos com -c - o primeiro constituindo o código a ser executado e os subsequentes os argumentos a serem passados ​​para esse código, começando com $0 (!) - por exemplo, bash -c 'echo $@' _ 1 2 3 - argumentos adicionais opcionais também devem ser suportados.

        • Uma vez que o shell nativo da plataforma está sendo chamado, tais chamadas são por definição _não_ portáteis, devido às diferenças fundamentais entre cmd e sh .É por isso que é tão importante corrigir a passagem de argumento _own_ do PowerShell (# 1995), pois ele fornece um método confiável e verdadeiramente multiplataforma de invocar programas externos.


A peça final do quebra-cabeça é a _documentação_.

https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152 é uma proposta de longa data para escrever um tópico de ajuda conceitual sobre como chamar programas externos (por exemplo, about_Native_Calls ), que deve, em um único local, cobrir _todos_ os aspectos que são relevantes para chamar programas externos; além do que já foi abordado aqui (armadilhas de sintaxe devido a metacaracteres adicionais, ausência de um pipeline de bytes bruto, não integração com o tratamento de erros do PowerShell, ...), os problemas em questão também devem ser discutidos:

  • Como a passagem de argumentos com " incorporados (e argumentos _empty_) é interrompida (espero que em breve) versões anteriores (atualmente discutidas em uma proposta separada, https://github.com/MicrosoftDocs/PowerShell-Docs / issues / 2361), e como contornar isso (escape \ manual ou uso de uma das funções auxiliares de # 1995, uma das quais é sucinta o suficiente para ser incluída diretamente no tópico).

  • Como apenas &! deve ser usado a partir de agora para chamar executáveis ​​nativos (externos), de modo a garantir a passagem de argumento adequada.

  • Como Invoke-NativeShell / ins pode ser usado para executar linhas de comando _escritas para o shell nativo da plataforma_ no estado em que se encontram.

Interessante, hoje os usuários podem:

  1. invocação direta
  2. & operador
  3. Processo inicial

Não está claro que os usuários devam usar.

Depois de corrigir o comportamento quebrado, os usuários terão os seguintes recursos:

  1. invocação direta
  2. & operador
  3. Processo inicial
  4. &! operador
  5. Start-Process V2

Não é demais para o shell executar ping 127.0.0.1 ?

Sim, essa é minha principal preocupação em adicionar outra operadora também @iSazonov. Já é confuso e isso não vai ajudar. A documentação é apenas uma parte do quebra-cabeça. Mesmo se tivermos uma documentação decente, o mero fato de que existem _ cinco maneiras diferentes_ de fazer isso, cada uma das quais se comportando de maneira um pouco diferente, sempre causará confusão.

Se realmente nos preocupamos com a UX, devemos optar por melhorar os casos existentes em vez de adicionar outros.

Se realmente nos preocupamos com a UX, devemos optar por melhorar os casos existentes em vez de adicionar outros.

O que você não pode, porque você deve ser compatível com versões anteriores. Eu sinto que estamos andando em círculos. https://github.com/PowerShell/PowerShell/issues/1995 foi isso e foi fechado por esse motivo.

@iSazonov :

A falta de orientação existente precisa ser resolvida, mesmo se não apresentarmos um novo operador:

Para resumir brevemente:

  • Não use Start-Process para invocar programas _console_ (terminal) (a menos que - que está disponível apenas no Windows - você queira executá-los _em uma nova janela_).

  • A invocação direta e a invocação via & são _equivalentes_; (deixando de lado os blocos de script), & só é necessário por razões _sintáticas_, sempre que o nome ou caminho do comando for _quoted_ e / ou contém _referências de variáveis_.


Depois de corrigir o comportamento quebrado, os usuários terão os seguintes recursos:
invocação direta

  • & operador
  • Processo inicial
  • &! operador
  • Start-Process V2
  • Não, não haverá um Start-ProcessV2 , apenas um _novo parâmetro_, -IndividualArguments - consulte https://github.com/PowerShell/PowerShell/issues/5576#issuecomment -552124719

  • Sim, haverá &! (ou qualquer símbolo que decidirmos), mas efetivamente _substitui & e invocação direta_ (pelo menos para _programas externos_, mas você será capaz de usá-lo com _todos_ formulários de comando) - que devem ser fáceis de comunicar.

Não é demais para o shell executar ping 127.0.0.1 ?

ping 127.0.0.1 continuará a funcionar, mas se você quiser que bash -c 'echo "one two"' funcione, você terá que usar
&! bash -c 'echo "one two"'

Isso é lamentável, mas - dado que o comportamento de invocação direta / & _não pode ser corrigido_ - parece-me a melhor solução.

Felizmente, é uma regra simples de memorizar: _Se você quiser passar argumentos previsíveis para programas externos, use &! _

Formas alternativas de opt-in - configuração (via powershell.config.json ) ou uma variável de preferência - são muito mais problemáticas:

  • O escopo dinâmico de variáveis ​​de preferência torna muito fácil aplicar inadvertidamente o opt-in ao código legado que não foi projetado para ele - consulte https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -650262164

  • A configuração se aplicaria às sessões como um todo, tornando o cancelamento totalmente impossível.

  • Em ambos os casos, é necessária uma ação _separada_ de uma chamada específica, e olhar para uma determinada chamada não dirá como ela se comportará - por outro lado, &! não deixa dúvidas sobre qual comportamento ocorrerá.

Para o usuário final, parece tão horrível quanto se lançássemos uma calculadora e encontrássemos 5 botões para a operação de adição. 😄

Se quisermos melhorá-lo a ponto de estarmos prontos para tais complicações, então talvez isso seja o suficiente para aceitar uma alteração significativa (para invocação direta e & ) de modo a simplificar o comportamento e lançar a V8.0.

Se o nosso desejo não for tão forte, é possível melhorar suficientemente o diagnóstico ( prog.exe param1 param2 -Whatif e o mesmo para Start-Process -WhatIf) de modo que exiba o que o programa testargs exibe e recomendações de como para executar corretamente o escape.

O desenvolvedor de script pode gastar tempo testando e usando o depurador. Mas em uma sessão interativa, qualquer usuário deseja simplicidade, brevidade e clareza, e isso supera tudo.

@iSazonov :

então, talvez isso seja o suficiente para aceitar uma alteração significativa (para invocação direta e &) de modo a simplificar o comportamento e liberar V8.0

Você certamente tem _meu_ voto - mas os poderes instituídos
Se aceitarmos uma mudança dessa magnitude, podemos finalmente lidar com toda a bagagem histórica - ver # 6745

Se quisermos melhorar tanto

Existem duas boas razões para querer tanto:

  • Para acabar com anos de dor devido à troca de argumentos interrompida - como evidenciado por # 1995, muitos problemas relacionados e incontáveis ​​questões Stack Overflow.

  • Porque - como foi declarado várias vezes antes - chamar programas externos com argumentos é um _digito central de qualquer shell_.

Historicamente, na época apenas do Windows, a escassez de CLIs externos capazes tornava a incapacidade de chamá-los adequadamente menos problemática, e os usuários que ficavam no jardim murado do PowerShell (chamando apenas comandos nativos do PowerShell) não sentiam dor. Mas os tempos mudaram:

Vivemos na era de uma riqueza de CLIs de plataforma cruzada que são cruciais para o desenvolvimento, CI e tarefas de implantação.
Se o PowerShell quiser ser levado a sério como um shell, esse problema deve ser resolvido.


Mesmo se ocorrer uma compatibilidade com versões anteriores da versão 8.0 (dedos cruzados), seu lançamento pode demorar muito e nós também precisamos de uma solução provisória.

Por mais que as melhorias propostas para a documentação sejam necessárias, eu não acho que elas sozinhas aliviem o problema o suficiente, por falta de uma solução de tempo de execução prontamente disponível.

Eu entendo que o operador é feio, mas uma solução ininterrupta _necessita_ sintaxe adicional (mais para digitar), qualquer que seja sua forma.

Uma alternativa para &! que é menos feia, mais fácil de digitar e não "polui" a linguagem é enviar uma _função_ embutida com um nome conciso, como a função iep (I nvoke- E xterna P rogram) que mascara todos os problemas, mostrado aqui .
(Observe que já enviamos funções que envolvem outras funcionalidades (por exemplo, pause ) ou acomodam cmd.exe usuários (por exemplo, cd.. [sic]).)

Assim, para fazer bash -c 'echo "one two"' funcionar corretamente, você chamaria iep bash -c 'echo "one two"'
Ou, para usar talvez um cenário mais realista:

# Pass JSON to an external utility (Unix example)
# Without `iep`, the embedded double quotes are lost and *broken JSON* is passed.
iep /bin/echo '{ "foo": "bar" }'

Novamente, estamos falando em última análise sobre uma _compromisso_ no serviço para preservar a compatibilidade com versões anteriores - isso é inevitavelmente um incômodo para os usuários finais.


melhore o diagnóstico (prog.exe param1 param2 -Whatif e o mesmo para Start-Process -WhatIf) para que exiba o que o programa testargs exibe e recomendações sobre como executar corretamente o escape.

Uma vez que a invocação direta / & baseada não é uma chamada de cmdlet e todos os argumentos são presumidos como argumentos de passagem, tratar -WhatIf como um parâmetro comum equivale a uma alteração significativa (embora seja improvável que está na prática).

Se você estiver disposto a aceitar tal mudança, podemos também (também) apoiar uma opção -DoItRight (ou baseada em símbolos, semelhante a --% ) como uma opção para o comportamento fixo - e isso me parece pior do que a abordagem iep -prefix / &! .

(Uma opção de diagnóstico ininterrupta, suponho, seria aprimorar Invoke-Command , que atualmente não oferece suporte a -WhatIf - no entanto, se fornecermos um opt-in bem documentado para o correto comportamento, não acho que vale a pena fazer isso.)

Eu concordo com @iSazonov que a situação não é ideal para introduzir mais uma coisa para fazer algo que deveria funcionar, mas como mencionado por @ mklement0, esperar por um lançamento que corrige adequadamente isso pode levar muito, muito, muito tempo, e muitos de nós precisam desesperadamente de uma solução o mais rápido possível. Por tudo que posso ver, a melhor solução atualmente é apenas usar
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 certo?

@ mklement0 Não tenho certeza de como um programa iep / invoke-external poderia funcionar, já que um cmdlet está sujeito ao mesmo parâmetro munging que estamos tentando corrigir. Este é um problema do analisador, certo? Se tentássemos ocultar tudo dentro de um cmdlet / função, eu imaginaria que seria terrivelmente complexo, pois teria que desfazer a intenção de munging / reconstruir. Ou eu estou esquecendo de alguma coisa?

@oising :

Para ser claro: a função vinculada iep corrige # 1995, não sobre o que _esta_ proposta é principalmente (o que eu acho que _não_ vale a pena prosseguir, como argumentado acima ), embora observe que a intenção declarada no OP é _também_ resolve # 1995.

1995 é sobre ser capaz de contar com a sintaxe do _PowerShell_ (não de outro shell) para passar argumentos para programas externos _fielmente, como visto literalmente pelo PowerShell após sua própria análise_.

A própria análise do PowerShell é adequada e não há problemas, desde que você passe argumentos para os comandos _PowerShell_.

Por outro lado, passar argumentos para _programas externos_ está muito errado, e é isso que iep corrige.

Ele é quebrado devido a falhas _re-citação e escape_ dos argumentos _por trás das cenas_, quando a linha de comando finalmente usada é construída.
https://github.com/PowerShell/PowerShell-RFC/pull/90 propõe uma correção adequada (sem acordo sobre como lidar com a compatibilidade com versões anteriores e com algumas questões menores ainda a serem respondidas), que em seu núcleo depende do recentemente introduziu System.Diagnostics.ProcessStartInfo.ArgumentList para executar a recitação e escape adequados para nós (apenas passamos a coleção de argumentos que resultou da análise do próprio PowerShell) - que, aliás, só é necessária _no Windows_; No Unix, sensatamente, _não há linha de comando_ (fora de um shell) quando você passa argumentos na inicialização do processo: você apenas passa os argumentos literalmente, como um array.

Em resumo:

  • Fornecer iep como uma função incorporada seria uma opção temporária e temporária para oferecer a passagem de argumentos adequados para programas externos, até que uma correção adequada - no contexto de uma alteração significativa - possa ser implementada

  • Um cmdlet Invoke-NativeShell ( ins ) é a solução adequada - não um paliativo - para fornecer uma maneira de chamar _um shell's_ diferente (do shell nativo) linhas de comando, usando a sintaxe _its_ - veja acima .

Atualmente, há casos em que recortar e colar um comando PERL não funciona como esperado em Python. Vai saber. Esta deve ser uma grande desvantagem para todos os cortadores e passadores por aí. Acho que Python deve ter um operador especial para isso.

Não tenho certeza se você entendeu minha sugestão; o que você diz é _precisamente_ o que estou dizendo. Deixe-me ser mais prolixo com algumas dicas estrategicamente capitalizadas: D

# NEW SOLUTION: the entire pipeline AS ABOVE is sent to WSL, then the results are returned to powershell
# and piped, in powershell, to foreach-object
(& --% wsl ls | grep -i "foo") | % { "match {0}" -f $_ }

O que estou perdendo aqui? Se o tubo aparecer depois de --% , é apenas outro caractere idiota: um argumento. Não há como alterar a precedência do operador. Se estiver contido em um pipeline ou subexpressão aninhada, comece a analisar o PowerShell novamente.

Pensamentos?

O que está faltando é a necessidade de passar um parêntese de fechamento como um argumento para um programa externo. É uma abordagem típica de DWIM - mas PowerShell não é HAL.

Obrigado por todos os comentários detalhados, a todos. Falando como eu aqui. Alguns uber-points antes de entrar mais em detalhes:

  • Ainda não estou a bordo de um vNext / 8.0 que decide quebrar uma nova ladainha de coisas. A compatibilidade com versões anteriores é importante. Eu sei que é doloroso e prefiro viver em um mundo onde possamos voltar e corrigir todos os erros que o PowerShell já cometeu, muitos não intencionais. Mas a grande maioria dos usuários do PS não está perdendo tempo aqui em questões, atolados em casos extremos de SO. Eles copiaram / colaram a mesma solução alternativa em algo por 4 a 10 anos, não sabem necessariamente por que funciona e não se importam que estivéssemos "corrigindo erros" quando seu script quebrar na atualização. Eles apenas sabem que "atualizar o PS me quebrou e não posso confiar nas atualizações", e então terminamos com ainda mais usuários retardando suas atualizações e adoção.
  • Acho que algumas pessoas neste problema podem estar projetando muitas esperanças e desejos diferentes neste problema, ou vê-lo como um ou / ou com outras correções. Eu percebo que exacerbou isso ao fechar # 1995, e sinto muito por isso (embora eu vá dizer, ainda não tenho certeza se um especificamente pode realmente ser consertado, mas vamos dar uma olhada nisso), mas isso é realmente uma solução de "alçapão" que tira usuários novos e experientes de uma situação difícil rapidamente, sem deixar a frustração aumentar. Ainda podemos ter conversas de longo prazo sobre correções para o lado da análise PS da casa, para que possamos oferecer uma solução verdadeiramente multiplataforma para tudo.

Agora, comentários enquanto leio:

  • Estou desconfiado sobre o desejo proposto de alinhar com #! (com &! ou $! visto que, no caso padrão, os usuários provavelmente não especificarão o intérprete . Se eu quiser fazer algo como $! net <stuff> , net.exe não é meu intérprete. E em não Windows, a coisa "nativa" a fazer ainda é passar para sh / bash / qualquer coisa antes passando para o binário que está sendo executado. Por exemplo, não quero que o Python analise *.py na string $! /usr/bin/python *.py ou mesmo $! python *.py . Quero que o Bash faça o globbing e depois entregue a lista de correspondências com python .
  • Como sugeriu @oising , sou a favor de --% como operador, sobrecarregado para ser usado na frente. A descoberta é um problema, e como já estamos há 10 anos trabalhando na descoberta deste ( agora é possível para o Google ! , digo que vamos continuar com isso. (Fui eu que fiz @ SteveL-MSFT coloque-o em sua proposta como uma consideração alternativa). Pergunta, porém: será que precisamos da operadora de chamada & aqui? Por que não começar a linha com --% native.exe do /some $stuff ?
  • @rjmholt : Não entendi totalmente o seu comentário aqui , mas acho que provavelmente não devemos mudar nada com o comportamento --% quando vem depois do executável, mas acho que podemos fazer o seu "novo comportamento "com onde vem antes do executável.
  • Leia isso de @jazzdelightsme e eu concordo enfaticamente:

    É também para os usuários mais experientes, que desejam apenas copiar / colar um comando do StackOverflow sem ter que entrar e mexer em tudo.

    Na minha opinião, esse é um aspecto enorme do cenário, possivelmente o maior. Não é apenas ASSIM, também, são documentos de toda a web que pressupõem que você está em um shell do tipo Bash.

  • Independentemente de coisas como # 1995 serem consertadas, o ponto é que a) há uma longa cauda de casos extremos estranhos como esse eb) eles são realmente difíceis de consertar sem quebrar as pessoas. Não é necessariamente verdade para todos eles, mas temos acertado o suficiente nos últimos dois anos que é evidente para mim que precisamos de um "alçapão" como este para tirar as pessoas de situações complicadas onde não querem mergulhar no buraco do coelho da nossa aninhada escapar (apenas para descobrir que há um bug como # 1995, que os impede de trabalhar, mesmo quando eles não sabem o que estão sabendo).

Ao revisar os comentários, acho que há duas questões em aberto que ainda precisamos responder:

  1. Continuamos a analisar os separadores de comando e tudo o que vem depois deles no PowerShell ou o separador de comando também é passado literalmente para o shell nativo?
    Eu digo que devemos passar tudo literalmente para o shell nativo, pois isso atualmente é algo que você não pode fazer, e que a solução do @oising de um parêntese principal pode ser a solução em que ainda podemos suportar tubos, cadeias de pipeline, etc. . se você deseja misturar / combinar a análise nativa e PS (embora eu queira obter algumas opiniões daqueles mais próximos do analisador).
  2. Qual é a operadora?
    Como mencionado acima, gosto de --% porque ele já tem a reputação de operador "apenas faça o que costumava fazer" e teremos que redefinir a descoberta com qualquer coisa nova. Eu entendo argumentos como @ mklement0 de que, se fizermos isso, estaremos mudando o significado técnico de --% (e @ SteveL-MSFT também apresentou esse argumento para mim), mas eu não acho a maioria das pessoas está pensando em --% em termos de "faz o que costumava fazer no cmd". Não acho que seja exagero codificar essa suposição na definição do operador como "faça o que o shell nativo faz aqui".

Ortogonalmente, apenas pensei em outro cenário que podemos ou não querer apoiar.

No Windows, cmd (efetivamente) inclui o diretório atual em sua pesquisa PATH . No entanto, o PowerShell exige que o usuário antecipe um ./ para executar qualquer EXE (ou qualquer coisa em PATHEXT ) se o diretório atual não estiver em PATH .

Portanto, minha pergunta em aberto é: queremos realmente passar a string de comando inteira para cmd? Eu proporia que, se realmente quisermos iluminar o cenário de copiar / colar com este operador, a resposta seja sim, já que alguns documentos / tutoriais de terceiros não detalham necessariamente como colocar as ferramentas que esperam que você use no PATH . Provavelmente não importa muito de qualquer maneira, mas eu queria enumerá-lo aqui.

  • Ainda não estou a bordo de um vNext / 8.0 que decide quebrar uma nova ladainha de coisas.

Sim, eu não sei como você conserta esses problemas sem quebrar em maneiras incrivelmente difíceis de solucionar. Além disso, não sei sobre vocês, mas mesmo eu não quero ter que lidar com todas essas diferenças complicadas de fichário / analisador ao escrever para várias versões.

Normalmente, se estou argumentando contra a interrupção das alterações, é para outras pessoas. No entanto, parece que provavelmente seria um PITA de lidar, mesmo para as pessoas presentes no ITT.

  • Como sugeriu @oising , sou a favor de --% como operadora, sobrecarregada para ser usada na frente. (...) Por que não começar a linha com --% native.exe do /some $stuff ?

Eu realmente gosto disso. Idealmente, o mais próximo possível de chamar CreateProcess / exec (mais manipulação de nix env var e redirecionamento de entrada / saída ou anexo de console). Ou talvez voltando para cmd.exe / bash completamente.

Eu também não estou preso em manter & na frente de --% . Posso justificar a ideia de tratar --% bruto como uma diretiva / operador, pelo menos na minha cabeça. Que tal multilinha? Permitir --% com @" e "@ para substituições e subexpressões de var e aspas simples @' e '@ para passar literal? Sem as strings aqui, é apenas uma linha?

Como sugerido por @oising , sou a favor de -% como operadora

Funciona para mim.

No entanto, pergunta: precisamos mesmo da operadora & call aqui?

Contanto que eu ainda possa fazer --% & $computedPathToNativeExe foo;@workspace , tudo bem para mim. Precisamos ser capazes de usar a operadora de chamadas para isso.

Eu digo que devemos passar tudo literalmente para o shell nativo, pois isso é algo que você não pode fazer atualmente

Acordado.

Não acho que seja exagero codificar essa suposição na definição do operador (-%) como "faça o que o shell nativo faz aqui".

Também concordou!

Além disso, talvez esta seja uma maneira de entrar no recurso para definir env vars antes do executável que são definidos apenas para a sessão do executável (# 3316):

--% JEKYLL_ENV=production jekyll build

Apenas um pensamento.

@rkeithhill , a ideia com o call native operator é que a linha não é processada pelo PowerShell e enviada literalmente para o "shell padrão" do sistema operacional. Portanto, neste caso, seu primeiro exemplo --% & $computerPathToNativeExe foo;@workspace não funcionaria. Você teria que usar o método atual e escapar conforme apropriado. O segundo caso --% JEKYLL_ENV=productoin jekyll build deve funcionar.

a linha não é processada pelo PowerShell e enviada literalmente para o "shell padrão" do sistema operacional

OK, isso parece razoável. No entanto, este --% JEKYLL_ENV=productoin jekyll build funcionaria apenas no Linux / macOS, mas não no Windows - pelo menos não sem alguma ajuda do PowerShell, certo?

@rkeithhill, conforme proposto atualmente, nunca funcionará no Windows, pois depende do shell "padrão" do sistema operacional. Para PowerShell que declara env var para um processo, temos um problema diferente aberto para discutir isso.

@ PowerShell / powershell-Committee discutiu isso hoje, atualizei a proposta original para refletir o resultado dessa discussão. Continuaremos com uma implementação de recurso experimental e obteremos mais comentários dos usuários com o código funcional. Ainda estamos abertos no sigilo do operador, mas usando --% por enquanto.

onde $foo é um nome de arquivo literal e $PWD é o diretório atual dentro do WSL. Observe que no primeiro exemplo, você teria que saber escapar && para que ele fosse executado dentro do WSL em vez de dentro do PowerShell.

Ambos $foo e $PWD são referências de variáveis ​​a serem avaliadas por sh .

Para canalizar a saída dessa execução de volta para o PowerShell, o usuário deve primeiro armazenar os resultados em uma variável:

$out = --% ls *.txt
$out | select-string hello

Observe que, ao contrário do atual operador de chamada & , você não pode usar nenhuma sintaxe do PowerShell, então:

--% $commandline

tentará executar $commandline literalmente (em cmd ) ou após a expansão do shell (em sh ). O PowerShell não tentará resolver essa variável.

O problema de recortar e colar é resolvido simplesmente colando depois que &n for digitado.

simplesmente colando após --%

O exemplo acima para wsl seria parecido com:

Qual?

Como todos os argumentos após --% são passados ​​para o shell "padrão", para dar suporte ao pipelining no PowerShell, você precisaria usar uma variável:

Esta é uma repetição.

Se vamos adicionar algo útil, proponho um Pivot.

Por que não podemos ter algo como um literal herestring

@! ""! @

Portanto, podemos ter uma maneira real de fazer isso em vez de um -%, que também tem seus próprios problemas.

Pelo menos assim podemos enterrar o problema com táticas de design "melhores".
E a descoberta desses casos extremos para ficar em About_Strings onde deve ser resolvido.

Apenas um pensamento.

Não consigo ver como @!"…"!@ é melhor do que Invoke-NativeShell @"…"@ . Estamos tentando tornar o PowerShell mais parecido com o Brainfuck?

@ yecril71pl

Não consigo ver como @!"…"!@ é melhor do que Invoke-NativeShell @"…"@ . Estamos tentando tornar o PowerShell mais parecido com o Brainfuck?

Eu acredito que @ romero126 pretendia sugerir um literal herestring on-line, de modo que se pudesse escrever

Invoke-NativeShell @!"complicated native command with "quotes" and | pipe"!@

ao invés de

Invoke-NativeShell @"
complicated native command with "quotes" and | pipe
"@

Se tal cadeia de caracteres oneline fosse considerada, eu sugeriria usar sintaxe como @"""…"""@ e @'''…'''@ , isso seria IMO mais fácil de digitar do que @!"…"!@ .


@ romero126 Você poderia esclarecer o que quis dizer?

@ yecril71pl obrigado por detectar esses erros, atualizou a mensagem principal com correções.

Não é especificado se --% será executado como linha de comando ou como arquivo em lote.

Eu acredito que @ romero126 pretendia sugerir um literal herestring on-line, de modo que se pudesse escrever

Invoke-NativeShell @!"complicated native command with "quotes" and | pipe"!@

ao invés de

Invoke-NativeShell @"
complicated native command with "quotes" and | pipe
"@

Se tal cadeia de caracteres oneline fosse considerada, eu sugeriria o uso de sintaxe como @"""…"""@ e @'''…'''@ , isso seria IMO mais fácil de digitar do que @!"…"!@ .

@ romero126 Você poderia esclarecer o que quis dizer?

Acredito que se adicionarmos flexibilidade a Strings para lidar com dados não analisados ​​"quase brutos", não teríamos problemas com esse pop-up.
@TSlivede Concordo quase totalmente com o que você disse. Apenas com uma ligeira variação. Deve ser capaz de lidar com AMBOS uma linha única e Multiline sem interpolar nada dentro das aspas. Qualquer que seja a sintaxe em que pousarmos para que isso aconteça, estou aberto. Estou mais focado no conceito ..

Com esses exemplos, ele adiciona muita flexibilidade, onde não preciso "Invoke-NativeShell", pois apenas retornaria uma string. O que significa que somos livres para manipulá-lo como uma corda. Para que isso potencialmente pudesse se tornar um grampo de sua aparência.

Um Pivô como este também significaria que não teríamos que lidar com os problemas acima, onde giramos nossas rodas em uma quase solução quando podemos lidar com um caso extremo, vs aborda o problema como um todo.

Exemplos rápidos:

# For this scenario let @!"<text>"!@ be our HereString Verbatim

Invoke-NativeShell @!"complicated native command with "quotes" and | pipe"!@
# or
Invoke-NativeShell @!"
Complicated native command with "quotes" and pipe and <tabs>
"!@

#or 
$r = @!"
complicated native command with custom arguments: {0}
"!@ -format "foo"
Invoke-NativeShell $r

Isso não resolve o problema da capa deste comentário . O fato é que não podemos simplesmente passar uma única string para cada comando; em muitos casos, eles precisam ser separados.

Acredito que se adicionarmos flexibilidade a Strings para lidar com dados não analisados ​​"quase brutos", não teríamos problemas com esse pop-up.

É para isso que servem ' e @' .

Com esses exemplos, ele adiciona muita flexibilidade, onde não preciso "Invoke-NativeShell", pois apenas retornaria uma string. O que significa que somos livres para manipulá-lo como uma corda. Para que isso potencialmente pudesse se tornar um grampo de sua aparência.

Os comandos nativos retornam uma sequência de strings. @!" tem o potencial de se tornar um grampo daquilo que é ininteligível.

Acredito que se adicionarmos flexibilidade a Strings para lidar com dados não analisados ​​"quase brutos", não teríamos problemas com esse pop-up.

É para isso que servem ' e @' .

Com esses exemplos, ele adiciona muita flexibilidade, onde não preciso "Invoke-NativeShell", pois apenas retornaria uma string. O que significa que somos livres para manipulá-lo como uma corda. Para que isso potencialmente pudesse se tornar um grampo de sua aparência.

Os comandos nativos retornam uma sequência de strings. @!" tem o potencial de se tornar um grampo daquilo que é ininteligível.

Acho que você está confuso. Com o que eu queria dizer.
Aspas simples e aspas duplas apenas expandem $ variável ou não. Ele não faz escape automaticamente dos caracteres. Também me limita, então não posso mais usar seu caráter definidor. Então, em vez disso, para que funcione corretamente como está agora. Eu ainda teria que escapar caracteres, o que explica por que precisamos incluir um &! Comando.

Se você tiver uma solução melhor, sou todo ouvidos. Eu acredito que isso atinge mais casos extremos do que adicionar um &!

Aspas simples e aspas duplas apenas expandem $ variável ou não. Ele não faz escape automaticamente dos caracteres. Também me limita, então não posso mais usar seu caráter definidor. Então, em vez disso, para que funcione corretamente como está agora. Eu ainda teria que escapar caracteres, o que explica por que precisamos incluir um &! Comando.

Quais personagens você teria que escapar e por quê?

@ romero126 Agora estou confuso com o que você está dizendo. Você está ciente de que o PowerShell tem uma string aqui ?

Eu pensei anteriormente que você pode considerar a sintaxe de PowerShells aqui strings irritante (pelo menos às vezes fico irritado, que deve haver uma nova linha no início e no final ...), mas agora não tenho tanta certeza do que você está dizendo, você poderia esclarecer?

@TSlivede , concordou que as strings here serem sintaticamente um tanto complicadas.

Há uma proposta existente, # 2337, que propõe melhorias e, embora esteja focada em permitir que o delimitador de fechamento seja indentado, @lzybkr também propõe uma variação _single-line _ (- também) semelhante a Rust em https: // github. com / PowerShell / PowerShell / issues / 2337 # issuecomment -391152107, o que nos permite fazer o seguinte, por exemplo:

# `ins` is `Invoke-NativeShell`
ins @' python -c 'print("hi")' | cat -n '@

A necessidade de escapar seria evitada combinando _múltiplos_ ' com @ , conforme necessário (por exemplo, @'' can use @' here ''@ .

Em outras palavras: este seria um aprimoramento geral dos literais de string do PowerShell, utilizáveis ​​em qualquer lugar, do qual Invoke-NativeShell se beneficiaria.

Dado que o nº 2337 está focado principalmente em outra coisa, criei uma proposta focada no nº 13204 e sugiro que continuemos a conversa lá.

A necessidade de escapar seria evitada

Eu vi isso algumas vezes neste tópico.

Qualquer indicador para o tokeniser começar um novo modo precisaria de um mecanismo de escape, uma vez que você precisa diferenciar o indicador para o tokeniser de que você está saindo desse modo da forma literal desse caractere.

A forma mais natural disso hoje são strings entre aspas simples:

  • ' : inicia a tokenização de aspas simples
  • ' : fim da tokenização de aspas simples
  • '' : escreva uma aspa simples literal dentro de uma string entre aspas simples

Acho que o motivo pelo qual estamos falando sobre escapar, apesar disso, é:

  • O operador --% define muitas maneiras de sair de seu modo ( | , && , || , ; , nova linha) e permite strings dentro dele (removendo seus ' caracteres)
  • Herestrings e co resolvem para um argumento de string em vez de uma matriz deles

Querer passar argumentos para uma invocação executável significa que estamos falando sobre uma sintaxe de array:

  • Como iniciar a matriz (iniciar o modo literal)
  • Como separar a matriz (passar um único argumento para o novo executável)
  • Como finalizar a matriz (modo textual final)

(Este é um conceito nativo em * nix, já que exec espera um array. No Windows, acho que somos forçados a pegar um array, serializá-lo para uma sintaxe de string específica e passá-lo por CommandLineToArgvW . No entanto, o .NET fornece uma abstração baseada em array para ambos - portanto, arrays ainda fazem mais sentido)

Isso significa que precisamos de duas fugas:

  • O literal separador de matriz
  • O literal final da matriz

Por exemplo, digamos:

  • --% antes de o comando se tornar a nova sintaxe literal
  • Como terminamos a sintaxe? newline e ; talvez?
  • Como separamos os argumentos? talvez?

As perguntas são:

  • Como passamos um ; literal ou uma nova linha?
  • Como passamos um literal?

Uma linha de comando nativa não é um array. É uma string ou um AST. Como o PowerShell não sabe nada sobre ASTs em shells nativos, apenas uma string permanece.

Uma linha de comando nativa não é um array. É uma string ou um AST

Você não forneceu nenhum fundamento para esta afirmação.

É aqui que o PowerShell constrói o AST para um comando (de qualquer tipo, já que ele não sabe até o tempo de execução quando o comando resolve que tipo de comando é):

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/parser/Parser.cs#L6391 -L6634

Observe que os elementos de comando (incluindo parâmetros e argumentos) são adicionados a uma matriz.

Você pode ver isso em ação no PowerShell assim:

> { ping 192.168.0.1 }.Ast.EndBlock.Statements[0].PipelineElements[0].CommandElements

StringConstantType : BareWord
Value              : ping
StaticType         : System.String
Extent             : ping
Parent             : ping 192.168.0.1

StringConstantType : BareWord
Value              : 192.168.0.1
StaticType         : System.String
Extent             : 192.168.0.1
Parent             : ping 192.168.0.1

Essa matriz é compilada em expressões aqui:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/parser/Compiler.cs#L4127 -L4160

E então isso é passado para uma invocação de pipeline aqui (observe que Expression.NewArrayInit(typeof(CommandParameterInternal[]), pipelineExprs) é onde os argumentos são passados):

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/parser/Compiler.cs#L3798 -L3805

Em tempo de execução, quando as expressões compiladas são executadas, isso se torna esta chamada de método , que passa por meio desta chamada que decide qual processador de comando usar:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs#L29 -L312

Eventualmente, quando o pipeline é chamado, essa matriz de argumento é vinculada ao comando nativo usando o NativeCommandParameterBinder :

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/NativeCommandParameterBinder.cs#L69 -L137

Isso pega a matriz de argumento, que inclui metadados AST, como o tipo de string usado para cada argumento, e cria uma nova string para invocação de argumento no NativeCommandProcessor aqui:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L1094 -L1166

Isso é feito porque a API .NET que usamos atualmente para a invocação do processo é a antiga versão "todos os argumentos em uma única string", em vez da nova no .NET Core que permite que você passe uma matriz e que o .NET reconstrua a invocação conforme necessário de uma maneira específica da plataforma.

Mas usar essa API é um detalhe de implementação. Por exemplo, poderíamos chamar fork / exec diretamente em * nix, como fazemos aqui:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/powershell/Program.cs#L268 -L330

Observe que exec aqui pega um array - fizemos isso porque é a maneira mais simples e direta de passar argumentos para o syscall, que é o objetivo final de uma sintaxe de argumento literal.

@rjmholt :

_Funcionalmente_, todas as necessidades já estão cobertas pelos formatos de string literal existentes, _se você passar a linha de comando nativa como uma única string_, o que eu recomendo fortemente, por meio de um novo cmdlet Invoke-NativeShell / ins .

O ponto de partida aqui é como _single string_, não uma matriz: é uma _linha de comando inteira_ - a ser analisada _pelo shell de destino_ - e faz mais sentido refletir isso no PowerShell como tal - em oposição a tentar desajeitadamente calçar um sintaxe de shell diferente em PowerShell com algo como --%

Tudo o que Invoke-NativeShell precisa fazer é passar a única string que contém toda a linha de comando do shell nativo para o shell nativo por meio de sua CLI:

  • no Unix , isso significa: passar a string como está, _como inteiro_, como o segundo elemento do argumento _array_ passado para exec (com -c sendo o primeiro elemento do vetor de argumentos, e /bin/sh o executável); expresso em termos .NET com um exemplo simples, onde printf "'%s'" foo | cat -n é o conteúdo literal da única string contendo a linha de comando a ser passada (observe o uso de .ArgumentList , não .Arguments ; a duplicação de ' s, um artefato apenas deste exemplo):

    • $psi = [Diagnostics.ProcessStartInfo]::new('/bin/sh'); $psi.ArgumentList.Add('-c'); $psi.ArgumentList.Add('printf "''%s''" foo | cat -n'); [Diagnostics.Process]::start($psi).WaitForExit()
  • no Windows , isso significa: para acomodar as peculiaridades de cmd , preencha o argumento lpCommandLine de CreateProcess como segue (com lpApplicationName assumido como o valor de variável de ambiente ComSpec , que aponta para cmd.exe ): "/c " + cmdLine , onde cmdLine é toda a string da linha de comando como está; expresso em termos .NET com um exemplo simples, em que echo a^&b & ver é o conteúdo literal da única string contendo a linha de comando a ser passada:

    • $psi = [Diagnostics.ProcessStartInfo]::new($env:ComSpec); $psi.Arguments = '/c ' + 'echo a^&b & ver'; [Diagnostics.Process]::start($psi).WaitForExit()

      • @ yecril71pl , isso implica a resposta à sua pergunta se a semântica do prompt de comando ou do arquivo em lote deve ser usada: é a primeira, como também é usada por outras linguagens de script, como Python com os.system() ; a (talvez infeliz) implicação é que você não será capaz de escapar de % caracteres, e que for variáveis ​​de loop devem usar um único % (por exemplo, %i vez de %%i ); e que você deve prefixar comandos de corpo de loop com @ para evitar que eles sejam ecoados (por exemplo, for %i in (*.txt) do <strong i="56">@echo</strong> %i ).

Este processo será _transparente para o usuário final_, de forma que tudo o que eles precisam se concentrar é em passar a linha de comando do shell nativo _verbatim_, apenas precisando satisfazer os requisitos de sintaxe literal de string do _PowerShell_:

Com a forma ins @'<newline>...<newline>'@ , você já _pode_ usar suas linhas de comando do shell nativo como estão: veja acima como as várias formas de string literal existentes podem ser usadas, que - devido à disponibilidade do _interpolando_ aqui- forma de string ( @"<newline>...<newline>"@ ) também - inclui a capacidade de usar variáveis ​​e expressões _PowerShell_ na linha de comando (a incapacidade de fazer isso é uma das principais deficiências de --% ).

O que meu comentário anterior propõe é uma _nova forma geral de literal de string bruta_, que é _separada desta proposta_ (e sua lógica de análise é (espero) bem definida); embora, como declarado, Invoke-NativeShell chamadas seriam então _mais convenientes_.

Em vez do que você já pode fazer (assumindo um comando Invoke-NativeShell / ins ):

# ALREADY WORKS: Literal here-string
ins @'
python -c 'print("hi")' | cat -n
'@

com o novo literal bruto de linha única proposto, você pode simplificar para:

# PROPOSED, more convenient syntax: new raw literal.
ins @' python -c 'print("hi")' | cat -n '@

Se os detalhes dessa nova sintaxe de string literal bruta precisam ser debatidos, vamos fazê-lo em # 13204, não aqui.

Então, relendo a proposta para entender a intenção original, vejo que na verdade está apenas pedindo uma sintaxe melhor para /bin/sh -c '<command>' + a versão cmd, junto com uma lógica de escape de string especial para cada shell de destino. Eu baseei minha concepção inicial em uma sintaxe especial e na necessidade de trabalhar com o analisador e o processador de comandos nativo, e na propriedade ProcessStartInfo.Arguments do .NET sendo muito difícil de usar corretamente em termos de passagem de cotação .

Mas, na verdade, a string Arguments é transmitida literalmente . Portanto, tudo o que precisamos fazer é passar essa string diretamente.

Nesse caso, acho que qualquer nova sintaxe é totalmente desnecessária (só vai complicar a lógica de tokenização e suspeito que confunda as pessoas) e, em vez disso, este é apenas um novo comando, ins como você diz. .NET tem alguma lógica para isso aqui, que seremos forçados a reimplementar, mas tudo bem, pois precisamos ser um pouco mais inteligentes de qualquer maneira.

Alguns comentários sobre @rjmholt é comentário https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -660231641

Qualquer indicador para o tokeniser começar um novo modo precisaria de um mecanismo de escape, uma vez que você precisa diferenciar o indicador para o tokeniser de que você está saindo desse modo da forma literal desse caractere.

Eu concordo e não acho que uma sintaxe de string in-line aqui possa evitar completamente a necessidade de escape.

O operador -% define muitas maneiras de sair do modo (|, &&, ||,;, nova linha) e permite strings dentro dele (removendo seus 'caracteres)

Ao falar de --% , você está se referindo ao switch --% ? Nesse caso, é melhor deixar isso claro em seu comentário, já que agora --% foi proposto para ser uma operadora de chamadas.

Querer passar argumentos para uma invocação executável significa que estamos falando sobre uma sintaxe de array:

A proposta é chamar o shell nativo (sh ou cmd) e passar tudo literalmente após o operador de chamada --% , como sh -c ... . Portanto, não é o PowerShell que passa os argumentos para o executável de destino. Nesse caso, acho que não precisamos de _uma sintaxe de array_, certo?

Como terminamos a sintaxe? newline e; talvez?
Como separamos os argumentos? 'espaço' talvez?

Acho que a análise literal termina em nova linha. Para passar uma nova linha como parte do argumento, use \n eu acho, exatamente como o que você faz no bash diretamente.

Uma linha de comando nativa não é um array. É uma string ou um AST

Você não forneceu nenhum fundamento para esta afirmação.

É aqui que o PowerShell constrói o AST para um comando (de qualquer tipo, já que ele não sabe até o tempo de execução quando o comando resolve que tipo de comando é):

Se esta proposta for implementada, ela apresentará uma construção especial em que o PowerShell não construirá o AST.

Fico feliz em ouvir isso, @rjmholt.

.NET tem alguma lógica para isso aqui que seremos forçados a reimplementar

Não acho que realmente precisamos reimplementar nada, como mostram os comandos em meu comentário anterior: no Windows, use .Arguments para passar a string de linha de comando inteira para o WinAPI; no Unix, use .ArgumentList para construir o argumento _array_ que é usado diretamente com exec .

Se esta proposta for implementada, ela apresentará uma construção especial em que o PowerShell não construirá o AST.

Bem, ele construiria um tipo de AST diferente, projetado para esse propósito. Se não fizer parte do AST, é basicamente o mesmo que um comentário, nada acontece.

_Funcionalmente_, todas as necessidades já estão cobertas pelos formatos de string literal existentes, _se você passar a linha de comando nativa como uma única string_, o que eu recomendo fortemente, por meio de um novo cmdlet Invoke-NativeShell / ins .

Eu pessoalmente gosto da ideia do cmdlet Invoke-NativeShell , em alguns aspectos melhor do que o operador de chamada --% . A principal preocupação que ouvi sobre esta ideia é que é mais digitar do que --% + Ctrl + v e diferenciar here-string literal de interpolar here-string é outro fardo para o usuário (_Eu pessoalmente acho ins é muito mais fácil de digitar do que --% _).

A principal preocupação que ouvi sobre esta ideia é que é mais digitação do que --% + Ctrl + v e diferenciar here-string literal de interpolar here-string é outro fardo para o usuário (_Eu pessoalmente acho que ins é mais fácil de digitar do que --% _).

Talvez o PSRL possa ajudar. Semelhante ao problema sobre um manipulador de chaves para circundar caminhos colados, o PSRL poderia ler CommandAst , ver que é ins e escapar apropriadamente da string colada.

Bem, ele construiria um tipo de AST diferente, projetado para esse propósito. Se não fizer parte do AST, é basicamente o mesmo que um comentário, nada acontece.

Claro, mas a árvore será trivial. Não refletirá o significado dos comandos que devem ser executados.

Inicie o processo cmd.exe redirecionando stdin / stdio e podemos literalmente usar uma string para invocar-NativeCommand mesmo com bash

Fico feliz em ouvir isso, @ daxian-dbw.
Adorei a ideia,

diferenciar here-string literal da interpolação here-string é outro fardo para o usuário

Eu acho que é um _plus_, porque - ao contrário de --% - o uso de @"<newline>...<newline>"@ permite que você incorpore diretamente os valores de variáveis ​​e expressões _PowerShell_ na linha de comando nativa.

Claro, isso pressupõe que os usuários saibam a diferença entre strings de aspas simples (literalmente) e aspas duplas (interpolação) e sobre ` -escapamento seletivo de $ , mas acho que é importante opção (avançada) para ter.

Para ajudar os usuários de recortar e colar, o recurso PSReadLine proposto deve ser @'<newline>...<newline>'@ (literalmente) por padrão.


Em última análise, não queremos promover uma mentalidade em que os usuários não precisam saber e compreender os requisitos de sintaxe exclusivos do PowerShell - em vez disso, queremos promover a compreensão deles, à luz do _ poder adicionado que eles trazem_.

Mas para incentivar os usuários a usar a sintaxe _own_ do PowerShell, que é por definição _cross-platform_, ela deve funcionar de maneira previsível .
Para esse fim, # 1995 deve ser corrigido , de preferência diretamente, permitindo uma alteração de interrupção ou, se essa não for uma opção, por meio de uma cerimônia mínima _opt-in_.

Para resumir:

  • Algo como --% (a nova forma de operador proposta aqui), por definição um recurso _específico da plataforma_, não é de forma alguma um substituto para a fixação de # 1995 .

  • Invoke-NativeShell ( ins ) evita todas as limitações do operador --% proposto e fornece a solução de "alçapão" que @joeyaiello mencionou: uma maneira rápida de executar uma linha de comando escrita para o shell nativo, sem precisar adaptá-lo à sintaxe do PowerShell.

  • --% deve permanecer em sua forma original de _argumento_ apenas, e deve permanecer um recurso _somente do Windows_ (que _efetivamente_, embora não _técnicamente_ seja) - em outras palavras: nenhuma alteração necessária.

    • Uma vez que # 1995 for corrigido, o único propósito de --% será acomodar casos _edge no Windows_: permitindo que você controle a citação explícita da linha de comando passada para programas desonestos, como msiexec que requerem formas muito específicas de citação intra-argumento.

Um dos outros recursos que eu realmente quero no PS v7.x é a capacidade de fazer com que um comando nativo saia com um código de erro para interromper meu script, como set -e conforme discutido neste RFC . Se a solução para isso for algo como Invoke-NativeCommand , ficaria confuso com Invoke-NativeShell ?

Só estou tentando reproduzir o filme um pouco para a frente porque, por mais que eu queira o recurso de chamada nativa, eu realmente quero que meus scripts de construção e teste apresentem erros ao chamar msbuild, cl, cmake, Conan, npm, etc. e esses comandos nativos terminam com um código de saída diferente de zero sem ter que se lembrar de verificar $ LASTEXITCODE o tempo todo. Se a implementação for apenas por meio de outra variável de preferência, por exemplo, $PSNativeCommandInErrorActionPreference então suponho que isso teria impacto na invocação do shell nativo - sendo um "comando" nativo e tudo mais?

A principal preocupação que ouvi sobre esta ideia é que é mais digitação do que --% + Ctrl + v e diferenciar here-string literal de interpolar here-string é outro fardo para o usuário (_Eu pessoalmente acho que ins é mais fácil de digitar do que --% _).

Talvez o PSRL possa ajudar. Semelhante ao problema sobre um manipulador de chaves para circundar caminhos colados, o PSRL poderia ler CommandAst , ver que é ins e escapar apropriadamente da string colada.

Mas e quanto a:

$c = gcm ins
& $c ...

O PSRL terá dificuldade em reconhecer o que está acontecendo lá. Teria que ser stateful, resolver o alias ins para invoke-nativeshell, então descobrir que você deseja chamar o commandinfo e então demunge o args? Eu realmente não gosto da ideia de criar uma caixa especial de um comando falso.

diferenciar here-string literal da interpolação here-string é outro fardo para o usuário

Eu acho que é um _plus_, porque - ao contrário de --% - o uso de @"<newline>...<newline>"@ permite que você incorpore diretamente os valores de variáveis ​​e expressões _PowerShell_ na linha de comando nativa.

Não estou seguindo você @ mklement0. Por que --% ser ensinado a fazer isso? Já dei isso como um caso de uso.

Eu realmente não gosto da ideia de criar uma caixa especial de um comando falso.

Não há comando falso aqui, apenas um cmdlet genuíno, Invoke-NativeShell , com o alias de ins .

gcm ins ( Get-Command ins ) fornecerá informações sobre o ins alias na forma de uma instância [System.Management.Automation.AliasInfo] , que você pode posteriormente passar para & para invocação, como com qualquer outro comando ou alias.

Portanto, seu exemplo funcionará exatamente como o pretendido - só que se você usar o desvio gcm ( Get-Command ), terá que soletrar a sintaxe literal de string apropriada _yourself_, sem o _suporte de comando de conveniência opcional_ que PSRL _pode_ fornecer, se a sugestão de @SeeminglyScience for implementada:

$c = gcm ins
& $c @'
python -c 'print("hi")' | cat -n
'@

Ou, se a nova sintaxe literal de string bruta proposta em # 13204 for implementada:

$c = gcm ins
& $c @' python -c 'print("hi")' | cat -n '@

Claro, se você estiver familiarizado com a sintaxe dos literais de string do PowerShell, uma string regular entre aspas simples também servirá: simplesmente duplique a ' incorporada dentro da string '...' :

$c = gcm ins
& $c 'python -c ''print("hi")'' | cat -n'

Por que --% ser ensinado a fazer isso?

  • --% não tem um delimitador de fechamento, o que o impede de usar em pipelines Powershell, o que é uma restrição inaceitável (observe que (...) invólucro _não_ é uma opção, porque isso implica a coleta inicial de todas as saídas linhas na memória em vez de _streaming_ processamento).

  • No Unix, --% não pode _both_ suportar (sem escape) $ como um metacaractere PowerShell _and_ como um metacaractere POSIX-shell.

Novamente: não há _nenhuma_ justificativa e nenhum benefício em colocar uma _sintaxe de shell diferente_ no PowerShell - passe a linha de comando do shell nativo _como uma string_ para ins e todos os problemas conceituais desaparecem.

Invoke-NativeShell implica que PowerShell não é o shell nativo. Essa suposição é sempre correta e sempre será?

@oising o caso de uso é alguém copiar e colar um comando nativo de um README ou algo assim. Se eles estiverem fazendo algo mais complicado do que digitar ins SPACE CTRL + v, então eles simplesmente construirão e escaparão de sua própria string normalmente.

@ mklement0

Mas para incentivar os usuários a usar a sintaxe _own_ do PowerShell, que é por definição _cross-platform_, ela deve funcionar de maneira previsível .
Para esse fim, # 1995 deve ser corrigido , de preferência diretamente, permitindo uma alteração de interrupção ou, se essa não for uma opção, por meio de uma cerimônia mínima _opt-in_.

Isso é separado da discussão sobre ins / --% certo? Se você discordar, pode esclarecer por que esse problema afetaria o novo comando / sintaxe?

@SeeminglyScience , sim, é separado, mas isso não ficou claro neste tópico.
A única razão pela qual # 1995 apareceu aqui é que ele foi originalmente fechado em favor deste problema (embora felizmente reaberto desde então), e o OP aqui ainda afirma que esse problema irá corrigi-lo ("Isso também deve resolver esses problemas:") .
Portanto, eu queria enfatizar que ins / --% _não_ endereçaria # 1995, o que requer sua própria correção com urgência.
(Somente se a passagem de argumento do próprio PowerShell funcionar corretamente, podemos, em sã consciência, recomendar que os usuários aprendam totalmente e adotem a sintaxe do PowerShell - o que devemos fazer.

@ SP3269 , podemos cruzar essa ponte quando chegarmos a ela 😁.

Mas seriamente:

Se você pensar em "nativo" como "escrito especificamente para a plataforma host [família]", o nome ainda caberia, mesmo que o PowerShell se tornasse o shell do sistema padrão em uma determinada plataforma (espero, mas não acho isso é realista, pelo menos não no futuro previsível).

Quanto às alternativas:

  • Invoke-[System]DefaultShell seria o nome mais preciso (embora inequivocamente não se aplicasse mais se o PowerShell se tornasse um shell padrão do sistema), mas "nativo" parece ser o termo mais comumente usado no mundo do PowerShell.

  • Invoke-LegacyShell tem alguma justificativa no Windows, mas não acho que os usuários do Unix aceitariam esse termo com muita gentileza.

Acho que estou começando a desvendar a confusão total que estou experimentando ao tentar acompanhar os motivos, ideias e problemas de todos aqui. Parece que estamos ( @ mklement0 e eu, pelo menos) olhando para isso como duas soluções diferentes para duas visões diferentes do mesmo problema conceitual. O que eu estava sugerindo era usar --% como uma dica para o _parser_ mudar a maneira como as coisas são analisadas em conjunto com a chamada & operador (não necessariamente usando --% autônomo.) Por outro lado, Michael parece estar olhando para ins como um cmdlet drop-in para substituir o próprio broker de comando nativo e analisador de argumento do PowerShell, e não está procurando alterar o analisador do PowerShell, em vez disso, usando strings / aqui -strings para capturar os parâmetros pretendidos (que estou dizendo que também podem ser usados ​​com --% .)

-% está sem um delimitador de fechamento, o que o impede de usar em pipelines Powershell

Este ponto eu não entendo. Não falta um delimitador de fechamento mais do que o comando ins / invoke-nativecommand. Se você precisar delimitar ou alimentar a partir do LHS, use here-strings, caso contrário, será considerado como uma única instrução.

Independentemente da forma escolhida, parece que haverá a necessidade de escolher um shell nativo para despachar a linha de comando. Sugiro que usemos as variáveis ​​de ambiente COMSPEC no Windows (o padrão é %systemroot%\system32\cmd.exe ) e SHELL no Linux (o padrão é /bin/bash ) - se SHELL não t existir, devemos ter como alvo /bin/sh (que na maioria dos casos é um link simbólico para bash de qualquer maneira).

-% está sem um delimitador de fechamento, o que o impede de usar em pipelines Powershell

Este ponto eu não entendo. Não falta um delimitador de fechamento mais do que o comando ins / invoke-nativecommand. Se você precisar delimitar ou alimentar a partir do LHS, use here-strings, caso contrário, será considerado como uma única instrução.

Basicamente, você não pode chamar coisas como cmd --% /c someapp | someOtherApp e ter cmd lidar com o pipeline; O PowerShell ainda interpreta um pipeline (e algumas outras coisas como && e || que de outra forma seria útil para o pessoal do Unix) como sendo um token do PowerShell em vez de passá-lo para o comando nativo como parte de a string do argumento.

e SHELL no Linux (o padrão é /bin/bash )

Não: o shell _system_ em plataformas do tipo Unix é _invariably_ /bin/sh .
Isso é diferente de um determinado shell interativo de _usuário_ (refletido em $env:SHELL - mas, infelizmente, atualmente não se _PowerShell_ for o shell do usuário, consulte # 12150), que é (a) configurável e (b) frequentemente _padrão para_ /bin/bash , mas mesmo isso não é dado, como evidenciado pela transição recente do macOS para /bin/zsh como o shell interativo padrão para novos usuários.

Sugiro que usemos as variáveis ​​de ambiente COMSPEC no Windows

Bom ponto, essa é a melhor maneira de se referir ao shell do sistema (interpretador de comandos) no Windows - atualizei o comando de exemplo acima de acordo.

Um dos outros recursos que eu realmente quero no PS v7.x é a capacidade de fazer com que um comando nativo saia com um código de erro para interromper meu script, como set -e conforme discutido neste RFC . Se a solução para isso for algo como Invoke-NativeCommand , ficaria confuso com Invoke-NativeShell ?

Só estou tentando reproduzir o filme um pouco para a frente porque, por mais que eu queira o recurso de chamada nativa, eu realmente quero que meus scripts de construção e teste apresentem erros ao chamar msbuild, cl, cmake, Conan, npm, etc. e esses comandos nativos terminam com um código de saída diferente de zero sem ter que se lembrar de verificar $ LASTEXITCODE o tempo todo. Se a implementação for apenas por meio de outra variável de preferência, por exemplo, $PSNativeCommandInErrorActionPreference então suponho que isso teria impacto na invocação do shell nativo - sendo um "comando" nativo e tudo mais?

Temos Start-NativeExecution em nosso módulo build para esse propósito.

Eu vi isso Como exatamente você combinaria Start-NativeExecution com Invoke-NativeShell se quiser que o último lance efetivamente em um código de saída diferente de zero? Eu realmente espero que algo como PR # 3523 chegue junto com este recurso nativo de chamada, porque eu quero que os dois funcionem juntos. Suponho que se a chamada nativa for implementada como um cmdlet (Invoke-NativeShell vs -%), então -ErrorAction Stop pode ser implementado para que seja lançada (erro de finalização) em um código de saída diferente de zero.

-% está sem um delimitador de fechamento, o que o impede de usar em pipelines Powershell

Este ponto eu não entendo. Não falta um delimitador de fechamento mais do que o comando ins / invoke-nativecommand. Se você precisar delimitar ou alimentar a partir do LHS, use here-strings, caso contrário, será considerado como uma única instrução.

Basicamente, você não pode chamar coisas como cmd --% /c someapp | someOtherApp e ter cmd lidar com o pipeline; O PowerShell ainda interpreta um pipeline (e algumas outras coisas como && e || que de outra forma seria útil para o pessoal do Unix) como sendo um token do PowerShell em vez de passá-lo para o comando nativo como parte de a string do argumento.

Sim, eu entendo o comportamento atual @ vexx32 . Mas não estamos (pelo menos não estou) falando sobre o comportamento atual de --% - estamos falando sobre aprimorá-lo, não? Por que tenho a sensação de que estamos todos conversando aqui? :)

Como eu disse, bem acima, poderíamos aprimorar & para trabalhar com --% então isso é possível . Eu também acho que precisamos ser claros sobre exec nativo implícito versus invocação explícita de um shell com argumentos. Esta é uma fonte constante de confusão - mesmo para usuários experientes do Windows - que de alguma forma cmd.exe (um shell) é necessário para "executar" outros executáveis; o mesmo se aplica ao linux / osx.

Sim, eu entendo o comportamento _current_ @ vexx32 . Mas não estamos (pelo menos não estou) falando sobre o comportamento _corrente_ de --% - estamos falando sobre aprimorá-lo, não? Por que tenho a sensação de que estamos todos conversando aqui? :)

Portanto, a sugestão atual para ajustar --% é que, se for onde o nome do comando normalmente estaria, tudo à direita dele é analisado como está (até uma nova linha) e enviado diretamente para bash / cmd. Não há espaço para qualquer sintaxe PowerShell como here-strings e quais não, porque então ela tem todos os mesmos problemas que --% e o processador de comando nativo têm atualmente.

Eu vi isso Como exatamente você combinaria Start-NativeExecution com Invoke-NativeShell se quiser que o último lance efetivamente em um código de saída diferente de zero? Eu realmente espero que algo como PR # 3523 chegue junto com este recurso nativo de chamada, porque eu quero que os dois funcionem juntos. Suponho que se a chamada nativa for implementada como um cmdlet (Invoke-NativeShell vs -%), então -ErrorAction Stop pode ser implementado para que seja lançada (erro de finalização) em um código de saída diferente de zero.

Atualmente será Start-NativeExecution { Invoke-NativeShell <strong i="10">@whatever</strong> } .

Este ponto eu não entendo. Não falta um delimitador de fechamento mais do que o comando ins / invoke-nativecommand. Se você precisar delimitar ou alimentar a partir do LHS, use here-strings, caso contrário, será considerado como uma única instrução.

Invoke-NativeShell não _ precisa_ de nenhum delimitador de fechamento porque é um comando regular, enquanto o horror de --% não é.

e SHELL no Linux (o padrão é /bin/bash )

Não: o shell _system_ em plataformas do tipo Unix é _invariably_ /bin/sh .

Acho que a coisa deveria ser equivalente a fazer um arquivo de script executável e executá-lo, o que delega a tarefa de escolher o shell certo para o sistema operacional e não devemos ser incomodados. Incluindo, se começar com #! , que seja.

A ideia por trás de Invoke-NativeShell é fornecer um invólucro conveniente em torno do _CLI_ do shell nativo.

  • No Unix, isso é totalmente suficiente, e criar um arquivo de script auxiliar não apenas introduz uma sobrecarga extra, mas também relata um valor aleatório em $0 (o caminho do arquivo de script), enquanto a invocação via CLI previsivelmente contém /bin/sh e pode até ser definido explicitamente seguindo o argumento-código com outro argumento (por exemplo, sh -c 'echo $0' foo )

    • (Como um aparte: criar um script de shell com a linha shebang #!/bin/sh significa direcionar o shell do sistema pelo caminho completo tão explicitamente quanto invocar /bin/sh diretamente).
  • No Windows, a execução por meio de um arquivo batch _traria_ vantagens (mas não para delegar a tarefa de localizar o shell do sistema, o que $env:ComSpec previsivelmente faz), pois evitaria os problemas de escape de % e for comportamento mencionado acima .

Para abordar o último como uma cortesia (para que os usuários não tenham que escrever seus próprios arquivos auxiliares em lote), para maior clareza conceitual, poderíamos oferecer um
-AsBatchFile switch como um _opt-in_.

Dito isso, se houver consenso de que a maioria das linhas de comando públicas cmd usadas para cortar e colar são escritas com a semântica de _batch-file_ em mente ( %%i vez de %i como uma variável de loop, capacidade de escapar textualmente % as %% ), talvez invariavelmente usando um aux. O arquivo em lote _no Windows_ (ao mesmo tempo que mantém a CLI no Unix) é a melhor solução - não tenho muita convicção sobre isso, mas teria que ser claramente documentado, especialmente porque significa exibir um comportamento diferente de outras linguagens de script.

@rkeithhill

Para mim, a RFC de integração de erros nativos que você vincula vale a pena abordar com a mesma urgência que a # 1995:

Ambos representam as etapas necessárias para tornar os programas externos (utilitários nativos) cidadãos de primeira classe no PowerShell (tanto quanto conceitualmente possível), _que deveriam sempre ter sido_.

Ser um cidadão de primeira classe significa: invocação _direta_ (com & necessária apenas por razões _sintáticas_, situacionalmente), não _por um cmdlet_.
Em outras palavras: nunca deve haver um cmdlet Invoke-NativeCommand ou um cmdlet Start-NativeExecution .

(À parte: Start-NativeExecution tem um nome incorreto, pois muitas funções no módulo build são; Start sinaliza operação _assíncrona_, enquanto a maioria dessas funções são _síncronas_ - consulte a discussão sobre Start-Sleep em https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4474).

Portanto, Invoke-NativeShell não requer consideração especial: o PowerShell, como já faz, definirá $LASTEXITCODE base no código de saída relatado pelo processo executável de shell nativo ( cmd / sh ) invocado.

Em seguida, o mecanismo proposto no RFC atuará sobre ele, como atuaria em executáveis ​​chamados diretamente (por exemplo, se $PSNativeCommandErrorAction = 'Stop' for definido, um erro de encerramento de script ocorrerá se $LASTEXITCODE for diferente de zero. )

(Um rápido aparte - esta discussão não pertence aqui: Quanto a um mecanismo _per-call_: algo como
/bin/ls nosuch || $(throw 'ls failed) funciona, assim como /bin/ls nosuch || $(exit $LASTEXITCODE) (o equivalente a /bin/ls nosuch || exit um shell POSIX); # 10967 discute porque infelizmente não podemos evitar $(...) (sem grandes mudanças na gramática); da mesma forma, a necessidade de se referir a $LASTEXITCODE explicitamente provavelmente não pode ser evitada.)

(À parte: criar um script de shell com a linha shebang #!/bin/sh significa direcionar o shell do sistema pelo caminho completo tão explicitamente quanto chamar /bin/sh diretamente).

Não era isso que eu queria dizer. Eu queria dizer que o PowerShell não precisa saber qual é o shell padrão porque os sistemas operacionais têm esse recurso integrado. O Linux sabe como executar um script executável no caso de não começar com #! ou começar com algo mais, como #!/usr/bin/env python e o Windows sabe o que fazer para chamar um script .CMD , então não devemos espiar %COMSPEC% 'n'stuff nem IMHO. O Windows também pode executar outros scripts, mas é baseado na extensão do arquivo de script, portanto, obviamente, não se aplica a este caso.

Novamente, o mandato de Invoke-NativeShell deve chamar o _CLI_ do shell nativo, não criar arquivos de script auxiliares (com efeitos colaterais) nos bastidores (exceto por diferentes razões, conforme discutido acima ).

Com relação à determinação robusta do shell do sistema, não há problema a ser resolvido aqui: codificar /bin/sh no Unix é perfeitamente apropriado, assim como consultar $env:ComSpec no Windows.

Não, as plataformas Unix no nível de chamada do sistema _não_ sabem como executar um arquivo de texto simples executável _sem uma linha shebang_; que você ainda _pode_ invocar tais arquivos é um recurso conveniente dos shells do tipo POSIX: eles tentam exec , então _ voltam_ para interpretar tais arquivos como escritos _para eles_; ou seja, é qualquer shell semelhante ao POSIX que está sendo executado que acaba executando o arquivo - enquanto o PowerShell simplesmente _falha_, porque não implementa esse fallback de cortesia (você obterá Program 'foo' failed to run: Exec format error ).

Desnecessário dizer que, portanto, não é aconselhável criar tais scripts.

Não, as plataformas Unix no nível de chamada do sistema _não_ sabem como executar um arquivo de texto simples executável _sem uma linha shebang_; que você ainda _pode_ invocar tais arquivos é um recurso conveniente dos shells do tipo POSIX: eles tentam exec , então _ voltam_ para interpretar tais arquivos como escritos _para eles_; ou seja, é qualquer shell semelhante ao POSIX que está sendo executado que acaba executando o arquivo - enquanto o PowerShell simplesmente _falha_, porque não implementa esse fallback de cortesia (você obterá Program 'foo' failed to run: Exec format error ).

csh não é semelhante ao POSIX e não falha em exec um script simples. Ele invoca sh . O mesmo acontece com perl .

Espero que seja óbvio que isso seja o que quero dizer: _ cabe a cada shell_ decidir o que fazer, e shells / linguagens de script diferentes fazem coisas diferentes; O PowerShell atualmente falha - se ele tivesse que fazer uma escolha e você quisesse que essa escolha fosse /bin/sh , estamos de volta à estaca /bin/sh deve ser assumido.

Espero que seja óbvio que isso seja o que quero dizer: _ cabe a cada shell_ decidir o que fazer, e shells / linguagens de script diferentes fazem coisas diferentes; O PowerShell atualmente falha - se ele tivesse que fazer uma escolha e você quisesse que essa escolha fosse /bin/sh , estamos de volta à estaca /bin/sh deve ser assumido.

exec em perl é uma chamada de sistema direta, perl não a modifica de forma alguma. Em particular, não _escolhe_ o intérprete.

Claro, isso pressupõe que os usuários saibam a diferença entre strings com aspas simples (literalmente) e aspas duplas (interpolação) e sobre ` -escapeamento seletivo de $ , mas acho que é importante opção (avançada) para ter.

Talvez seja só eu, mas eu trocaria mil " por um -f .

@ yecril71pl

exec em perl é uma chamada de sistema direta, perl não o modifica de forma alguma. Em particular, não escolhe o intérprete.

Isso não pode ser verdade se funcionar com scripts sem a linha #! . O exec syscall no linux não funciona sem #! , como se pode testar facilmente:

user@Programming-PC:/tmp/exec_test$ ls
test.c  testscript
user@Programming-PC:/tmp/exec_test$ cat test.c
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv) {
  char *binaryPath = "./testscript";

  execl(binaryPath, binaryPath, NULL);

  perror("Error");

  return 0;
}
user@Programming-PC:/tmp/exec_test$ gcc test.c -o testexecutable
user@Programming-PC:/tmp/exec_test$ chmod +x testscript 
user@Programming-PC:/tmp/exec_test$ cat testscript 
echo test from script
user@Programming-PC:/tmp/exec_test$ #works from shell:
user@Programming-PC:/tmp/exec_test$ ./testscript 
test from script
user@Programming-PC:/tmp/exec_test$ #doesn't work via exec syscall:
user@Programming-PC:/tmp/exec_test$ ./testexecutable 
Error: Exec format error
user@Programming-PC:/tmp/exec_test$ vim testscript 
user@Programming-PC:/tmp/exec_test$ cat testscript 
#!/bin/sh
echo test from script
user@Programming-PC:/tmp/exec_test$ #exec syscall works with #! line:
user@Programming-PC:/tmp/exec_test$ ./testexecutable 
test from script
user@Programming-PC:/tmp/exec_test$ uname -a
Linux Programming-PC 4.4.0-176-generic #206-Ubuntu SMP Fri Feb 28 05:02:04 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
user@Programming-PC:/tmp/exec_test$ 

Edit: acabei de notar que se você usar a função de biblioteca execlp , de acordo com a página de manual:

Se o cabeçalho de um arquivo não for reconhecido (a tentativa de execve (2) falhou com o erro ENOEXEC), essas funções irão executar o shell (/ bin / sh) com o caminho do arquivo como seu primeiro argumento. (Se essa tentativa falhar, nenhuma pesquisa adicional será realizada.)

No entanto, este não é um recurso do "linux", mas da biblioteca gnu c - e novamente de acordo com a página de manual, ele usa explicitamente / bin / sh e não algum shell definido pelo usuário. (Codificado na fonte de posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) e sysdeps / unix / sysv / linux / paths.h (ou sysdeps / generic / paths.h - não sei qual cabeçalho é usado ...) ( #define _PATH_BSHELL "/bin/sh" ))

No entanto, este não é um recurso do "linux", mas da biblioteca gnu c - e novamente de acordo com a página de manual, ele usa explicitamente / bin / sh e não algum shell definido pelo usuário. (Codificado na fonte de posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) e sysdeps / unix / sysv / linux / paths.h (ou sysdeps / generic / paths.h - não sei qual cabeçalho é usado ...) ( #define _PATH_BSHELL "/bin/sh" ))

Já que vem de sysdeps/**/linux , é uma coisa do Linux. Também pode ser genérico, mas é substituível - obviamente, não localmente, mas por sistema operacional.

Sim, eu entendo o comportamento _current_ @ vexx32 . Mas não estamos (pelo menos não estou) falando sobre o comportamento _corrente_ de --% - estamos falando sobre aprimorá-lo, não? Por que tenho a sensação de que estamos todos conversando aqui? :)

Portanto, a sugestão atual para ajustar --% é que se for onde o nome do comando normalmente estaria, tudo à direita dele é analisado _como está_ (até uma nova linha) e enviado diretamente para bash / cmd. Não há espaço para qualquer sintaxe PowerShell como here-strings e quais não, porque então ela tem todos os mesmos problemas que --% e o processador de comando nativo têm atualmente.

Oh senhor ... Estão todos tentando me enganar? 😄 Não, Patrick - se você quiser usar expressões do PowerShell, use strings here com --% . Se você quiser várias linhas, use here-strings. Se você quiser múltiplas linhas sem substituição de variável, você pode usar here-strings com aspas simples.

De qualquer forma, não tenho nenhuma pele neste jogo e acho que já disse o suficiente. Ainda acho que há algo muito errado em usar um cmdlet para adiar para outro shell de dentro de um shell. É simplesmente desajeitado. É especialmente desajeitado quando você está usando um cmdlet para executar outro shell para executar um comando nativo que não precisa desse shell secundário em primeiro lugar. A coisa toda cheira mal.

editar: última palavra

O título deste item é "chamar operador nativo". Vejo uma oportunidade perdida quando já temos um operador de chamada & e já temos um token que foi projetado para lidar com a passagem de argumentos para comandos nativos, --% . No momento, o uso de & com --% NÃO é reconhecido e, como tal, NÃO introduzirá uma alteração importante se decidirmos ativar este cenário e dar a ele um comportamento específico para corrigir os problemas discutidos aqui.

se você quiser usar expressões PowerShell, então use strings here com --% . Se você quiser várias linhas, use here-strings.

O objetivo do operador é que ele não analisa literalmente nenhuma sintaxe do PowerShell. Não existem strings aqui. Não existem constantes de string entre aspas simples. É "enviar o texto à direita como está" não "avaliar a expressão à direita, converter em string e enviar seu valor"

Por exemplo, se você tivesse este:

--% @'
echo my native command
'@

seria traduzido para:

cmd.exe /c "@'"
echo my native command
'@

Exceto que você obteria um erro do analisador porque '@ seria apenas o início de uma constante de string entre aspas contendo @ .

Eu acho que talvez você esteja imaginando a implementação de forma diferente, de modo que seja mais ou menos a mesma coisa que o argumento de venda de ins , mas o acima é o argumento de venda atual sendo discutido por --% .

Também vale a pena notar que é assim que a funcionalidade existente funciona. Este exemplo funciona basicamente da mesma maneira:

cmd /c --% @'
echo my native command
'@

Eu repito:

No momento, o uso de & com -% NÃO é reconhecido / indefinido e, como tal, NÃO introduzirá uma alteração significativa se decidirmos habilitar este cenário e fornecer a ele um comportamento específico para corrigir os problemas discutidos aqui .

Deixe-me esclarecer ainda mais: permitir que os caracteres aqui sigam --% quando usados ​​com &

Hah isso é um grande oof - sim, eu perdi isso, desculpe @oising.

Sobre essa ideia, acho que seria um pouco confuso se --% parasse de analisar em alguns contextos e "fizesse um trabalho melhor na vinculação de argumento nativo" em outros (da mesma forma com fazê-lo funcionar como Invoke-NativeShell se é isso que você quer dizer).

Obrigado por cavar mais fundo re exec , @TSlivede. Deixe-me tentar resumir e, portanto, espero fechar esta tangente; aplica-se a Linux e macOS:

Não há função de sistema (ou biblioteca) chamada exec no Unix, há apenas uma _família_ de funções relacionadas com exec em seu nome (a maioria delas como um prefixo).

  • As funções do _system_ ( man section 2 ), execve em seu centro, _fail_ quando você tenta executar um script shebang-less.

  • Entre as funções da _library_ que _construem nas funções do sistema_ ( man section 3 - man 3 exec ), apenas aquelas que têm p (para "caminho", I presume) ter o fallback de shell pass-para-o-sistema (por exemplo, execlp ).

    • As implementações da biblioteca GNU dessas funções em código /bin/sh , como @TSlivede mostrou, enquanto as baseadas em BSD usam apenas sh e contam com sh para estar em $env:PATH (embora, curiosamente, a página man indique /bin/sh ).

Conforme declarado, para _execução direta_ os principais shells semelhantes a POSIX ( bash , dash , ksh e zsh ) voltam a executar scripts sem shebang _seelf_, o que implica que eles _não_ usam as variantes de fallback entre as funções de biblioteca exec ; em contraste, a escolha de perl para sua própria função exec foi contar com o fallback; da mesma forma, os exec _builtins_ das principais conchas do tipo POSIX contam com o fallback, exceto em bash .

Já que no Unix o uso de um arquivo de script nos bastidores é desnecessário, podemos fazer as mesmas suposições sobre o caminho do shell do sistema que as funções da biblioteca de fallback, e eu recomendo /bin/sh acima de sh , para segurança e previsibilidade:

Apesar da especificação POSIX _não_ obrigar a localização de sh (ou seja, estritamente falando, as funções da biblioteca BSD são as compatíveis):

  • /bin/sh é seguro assumir, porque é o local padrão _de facto_, não menos porque escrever shell Unix portátil _necessita_ referindo-se a sh por seu caminho completo, dado que as linhas shebang suportam apenas _full, literal_ caminhos ( #!/bin/sh ).

  • Por outro lado, confiar na localização de sh por meio de $env:PATH pode ser um risco de segurança, visto que $env:PATH pode ser manipulado para invocar um sh .

A propósito, o mesmo risco se aplica à localização de cmd.exe por meio de $env:ComSpec , então talvez a melhor maneira seja chamar a função GetSystemDirectory WinAPI e anexar \cmd.exe a seu resultado (precisamos de um fallback de qualquer maneira, se $env:ComSpec não for definido).

/bin/sh é seguro assumir, porque é o local padrão _de facto_, não menos porque escrever shell Unix portátil _necessita_ referindo-se a sh por seu caminho completo, dado que as linhas shebang suportam apenas _full, literal_ caminhos ( #!/bin/sh ).

Normalmente você diz #!/usr/bin/env sh (exceto em scripts de sistema).

  • Normalmente, você _não_: pesquisando "#! / Bin / sh" resulta em cerca de 3.900.000 correspondências, "#! / Usr / bin / env sh" produz cerca de 34.100 correspondências.

  • Normalmente, você _não deve_: você deseja visar previsivelmente _the_ shell do sistema, /bin/sh , e não qualquer utilitário sh que venha primeiro em $env:PATH .

A única razão para direcionar um executável denominado sh é direcionar portably o menor denominador comum-assume-POSIX-features-only _system_ shell, ou seja, /bin/sh .

Normalmente, você _não_: pesquisando "#! / Bin / sh" resulta em cerca de 3.900.000 correspondências, "#! / Usr / bin / env sh" produz cerca de 34.100 correspondências.

Não há como limitar uma pesquisa na Web a scripts de usuário, especialmente porque muitos scripts de usuário não são marcados como executáveis ​​e, mesmo que sejam, podem contar com execlp ; mas mesmo que a maioria dos scripts de usuário diga isso, não devemos tomar _customary_ por _normal_. Os scripts a serem executados pelo PowerShell são scripts de usuário; quando os usuários querem um shell, eles chamam sh , não /bin/sh , a menos que sejam paranóicos.

@oising

Passar a linha de comando do shell nativo _como uma única string_ (em qualquer forma de string (-literal) que seja mais fácil) e _apenas_ como uma única string (exceto para os argumentos adicionais de passagem para a linha de comando suportados no Unix ( apenas) discutido acima ) - assumindo que você concorda com isso - soa como um terreno comum.

Passar a linha de comando como uma _cadeia única_ é o pré-requisito para:

  • ser capaz de incorporar tal chamada em um pipeline _PowerShell_.
  • ser capaz de incorporar variáveis ​​e expressão _PowerShell_ na linha de comando do shell nativo.

Passar uma única string é uma _expressão conceitual direta_ do que a chamada fará; por outro lado, tentar incorporar de alguma forma a sintaxe de um shell diferente diretamente no PowerShell com argumentos _individual_ (opcionalmente palavra-simples) traz problemas insolúveis que resultam em um compromisso confuso com limitações fundamentais, que o --% já incorpora.


Embora você possa argumentar que não é tão importante escolhermos um _operador_ ou um _cmdlet_ para passar essa linha de comando de string única (exceto por questões de _descoberta_), tenho preocupações conceituais sobre o uso de & e --% (precedido por & ):

  • A preocupação é & : se precisarmos passar uma string - entre aspas - contendo a linha de comando, estamos efetivamente operando no modo _expressão_, enquanto & atualmente implica no modo _argumento_ (o que significa: _individual_ argumentos, citando apenas quando necessário). Portanto, o envolvimento de & é confuso.

  • A preocupação re --% é sobre a origem somente do Windows e a semântica confusa de --% como o símbolo de parada de análise; seria especialmente confuso se --% como o símbolo de parada de análise operasse no modo _argumento_ (por exemplo
    & /bin/ls --% dir1 dir2 ), enquanto & --% operava no modo _expressão_ (por exemplo, & --% '/bin/ls dir1 dir2 >out.txt' )


Deixe-me dar um passo atrás e examinar --% , o símbolo de parada de análise, conforme implementado atualmente:

Foi uma tentativa de resolver dois problemas não relacionados em última análise (apenas Windows na época):

  • (a) Um caso de uso que também se aplica a outras plataformas: Deixe-me reutilizar _linhas de comando_ escritas para cmd.exe / o shell nativo no estado em que se encontra, portanto, não preciso adaptá-los à sintaxe do PowerShell.

  • (b) Um problema sempre apenas do Windows: deixe-me chamar um _rogue CLI_ (ou seja, uma chamada para um aplicativo de console externo _único_) que requer um estilo muito específico de citação que a recitação genérica dos bastidores do PowerShell não pode fornecer , permitindo-me criar a linha de comando passada ao WinAPI para tais CLIs _verbatim_. (Para limitar a declaração do problema a isso, pressupõe-se que a recitação do PowerShell geralmente funcione corretamente, o que nunca aconteceu - esse é o assunto de # 1995).

--% foi implementado como uma _conflação_ confusa de tentativas de solução para ambos (a) e (b), e acabou não resolvendo nenhum dos problemas adequadamente:

  • Foi anunciado como a solução para (a), mas na realidade tem limitações severas, porque _o único recurso cmd exe que ele emula é a expansão de referências de variáveis ​​de ambiente de %...% -style_:

    • Ele só suporta um comando _único_ cmd , dado que um | (e
      && e || , mesmo que não tenham sido implementados no momento) encerram implicitamente a parte de passagem para cmd .

    • Um _único_ & - que é cmd s separador de vários comandos - _is_ passou, mas também _não_ resultou em suporte a vários comandos, porque - desde cmd isn ' t realmente envolvido - o & é passado _verbatim para o programa de destino_; por exemplo
      echoArgs.exe --% a & b não ecoa a então invoca b , ele passa argumentos verbatim a , & e b para echoArgs .

    • cmd caractere de escape de ^ ) em argumentos não citados não é respeitado.

    • Como corolário, %...% tokens que se referem a variáveis ​​de ambiente existentes são _invariably_ expandidos; uma tentativa de evitar que via ^ não funciona corretamente (por exemplo
      echoArgs.exe Don't expand %^USERNAME% , que em cmd evita a expansão _e remove ^ _, retém ^ quando chamado do PowerShell como echoArgs.exe --% Don't expand %^USERNAME%

  • Como solução para (b), também ficou aquém:

    • Assim como no uso (a), não há capacidade de incorporar variáveis ​​e expressões _PowerShell_ ao comando - exceto se você primeiro definir um _aux de maneira desajeitada. ambiente_ variável_ que você então referencia por meio de %...% após --% ; por exemplo
      $env:FOO='foo'; echoArgs.exe --% %FOO%!

A solução adequada para (a) teria sido Invoke-NativeShell / ins , conforme discutido aqui.

A solução adequada para (b) teria sido:

  • suporte --% como especial apenas como o argumento _first_
  • e, assim como ins , exigem que seja seguido _por uma única string_ que constitui a linha de comando de passagem _como um todo_, novamente com a capacidade de incorporar variáveis ​​e expressões _PowerShell_ por meio de interpolação de string (ou outras maneiras de construir a corda)
  • por exemplo, msiexec --% "/i installer.msi INSTALLLOCATION=`"$loc`"" , com $loc contendo 'c:\foo\bar none' , resultaria na linha de comando literal /i installer.msi INSTALLLOCATION="c:\foo\bar none" sendo passada, satisfazendo o requisito não padrão de msiexec que em <prop>=<value> argumentos apenas <value> parte entre aspas.

Embora isso não seja _conveniente_, é o preço a pagar por uma solução robusta para a anarquia que é a transmissão de argumentos baseados em linha de comando no Windows.

@ mklement0 Obrigado pela resposta paciente e atenciosa (como de costume).

Então, eu concordo de todo o coração;

A solução adequada para (b) teria sido:

  • suporte -% tão especial apenas como o primeiro argumento
  • e, assim como com ins, exigem que seja seguido por uma única string que constitui a linha de comando de passagem como um todo,> - novamente com a capacidade de incorporar variáveis ​​e expressões do PowerShell por meio de interpolação de string (ou outras formas de construindo a corda)

    • por exemplo, msiexec -% "/ i installer.msi INSTALLLOCATION = "$loc " ", com $ loc contendo 'c: foobar none', resultaria em linha de comando textual / i installer.msi INSTALLLOCATION =" c: foobar nenhum "passando, satisfazendo o requisito não padrão da msiexec de que em=argumentos apenas oparte estar com aspas duplas.

Isso é o que venho tentando expor como uma solução generalizada para (a) e (b), exceto que ainda não entendi que (a) deveria existir como um problema independente para resolver. O que estou realmente lutando é se (b) é implementado _especificamente_ para & , por que não pode cobrir (a) também? por exemplo, o que ins ... dá que & cmd --% ... não pode? E não será uma mudança significativa, a não ser não permitir que & passe --% como um argumento (o que parece ridiculamente improvável.) Bônus que você não precisa se preocupar com comspec, shells como queiras. Deixe o chamador decidir.

@ PowerShell / powershell-Committee revisou isso:

  • Este será um recurso experimental para obter feedback de uso real; ainda estamos abertos ao sigilo real; nós adiamos /bin/sh vs /bin/env sh para a revisão de PR
  • Avançaremos com --% como um novo operador especificamente para o cenário "stackoverflow", onde o usuário corta e cola uma linha de comando no PowerShell e ela deve funcionar (com, é claro, o novo operador antes dela )
  • Um cmdlet Invoke-NativeShell é separado desta proposta e pode ser publicado pela comunidade em PowerShellGallery; Além disso, exigir uma string here não resolve o problema de experiência do usuário sem saber com antecedência como envolvê-la como uma string here
  • Não queremos fazer uma alteração significativa no switch --% onde os usuários existentes podem depender desse comportamento
  • Os usuários que precisam de comportamento de streaming como --% switch devem continuar a usar esse switch e escapar dos caracteres especiais conforme necessário
  • Ainda há um problema de descoberta para os usuários saberem sobre --% (ou qualquer solução)
  • No momento, não estamos considerando uma alteração no PSReadLine para modificar o conteúdo colado como uma string here tentando prever a intenção do usuário
  • Não estamos procurando soluções múltiplas que permitem cenários here-string vs non-here-string (expansão); por exemplo, & --% vs --%

Também quero acrescentar que, apesar de @ SteveL-MSFT e eu fecharmos o # 1995 ao mesmo tempo que abrimos este, realmente não era nossa intenção comunicar isso como uma solução definitiva para aquele problema.

Em minha opinião, os benefícios desse recurso específico são muito mais significativos do que o impacto # 1995 Acho que há MUITOS exemplos de StackOverflow e exemplos de documentos para ferramentas iluminadas não PS que as pessoas gostariam de cortar / colar (inclusive eu).

Além disso, há um desejo de fornecer escape adequado na própria linguagem do PowerShell, mas não estou vendo as pessoas atingirem o número 1995 em massa (mas vou elaborar mais sobre isso lá).

  • Avançaremos com --% como um novo operador

Desculpe arrastar isso, e não estou procurando uma discussão adicional, mas que tipo de operador? (Estou interessado principalmente em sintaxe.)

$a = --% find . -iname *$pdf -print0 | xargs -0 ls -ltr

Quem pega o cachimbo? O que acontece com $ ? Semelhante a & , && e || .

Sob quais condições, se houver, --% poderá ser usado em um tubo PS daqui para frente?

Obrigado.

Desculpe arrastar isso, e não estou procurando uma discussão adicional, mas que tipo de operador? (Estou interessado principalmente em sintaxe.)

Em termos de AST, provavelmente não será tecnicamente um operador, mas é o próprio tipo de AST. Se for implementado como um operador, será um operador unário.

$a = --% find . -iname *$pdf -print0 | xargs -0 ls -ltr

Quem pega o cachimbo? O que acontece com $ ? Semelhante a & , && e || .

A string find . -iname *$pdf -print0 | xargs -0 ls -ltr seria enviada como está para o shell nativo. $a ainda estaria populado, então você poderia na próxima linha canalizar isso para algo.

Sob quais condições, se houver, --% poderá ser usado em um tubo PS daqui para frente?

Se ~ no primeiro slot de elemento de comando ~ estiver no início de uma instrução, provavelmente não. Essa é uma das atrações da nova sintaxe. Se não ~ no primeiro slot ~ no início, continuará a funcionar como hoje.

(@ daxian-dbw @ SteveL-MSFT se algo estiver errado, corrija ❤️)

@SeeminglyScience está exatamente certo

Se estiver no primeiro slot de elemento de comando, provavelmente não. Essa é uma das atrações da nova sintaxe. Se não estiver no primeiro slot, ele continuará funcionando como hoje.

Pode ser complicado passar a saída do comando upstream para o comando nativo que será invocado por --% . --% basicamente apenas entrega a string para um shell nativo, como bash , mas a saída do comando upstream não pode ser alimentada apenas para bash , mas deve ser alimentada para o o próprio comando nativo, como find .

Acho que o comportamento de streaming não é uma consideração a esse recurso, pois é principalmente para o cenário "StackOverflow". Portanto, acho melhor apenas fazer --% uma instrução especial ast, para que fique claro que não funcionará com pipelines _PowerShell_.

Ahh, eu sabia que havia um motivo para "slot de elemento de comando" estar errado. Eu quis dizer no início de uma declaração. Vou consertar, obrigado!

a saída do comando upstream não pode ser alimentada apenas para bash

_Upstream_ significa _us_? Acho que posso fazer: 'ls' | bash . Não é uma coisa peculiarmente inteligente de se fazer, mas é possível.

Estava discutindo com @jpsnover sobre isso e ele propôs o shebang #! como o sigilo. Nesse caso, você apenas digitaria:

#!/bin/sh ls | grep foo
#!sh | grep foo

(onde o segundo caso assume que sh está no caminho). Nesse caso, teríamos que decidir se a especificação do shell é necessária ou se sempre o executamos no shell padrão, portanto, neste exemplo, sh é iniciado duas vezes. No entanto, há um problema com isso devido à forma como os arquivos shebang funcionam hoje. Eles funcionam no PowerShell porque o shebang é ignorado como um comentário. Se decidirmos ignorar a primeira linha em um script se for um comentário, ainda teremos um problema no shell interativo, onde cada novo conjunto de linhas é um novo script e não há como hoje determinar se esse script é executado como um script ou interativamente.

Uma alternativa pode ser emprestar a sintaxe de markdown:

powershell ```bash ls | grep foo ```

Neste exemplo, usaríamos a sintaxe de delimitação de código de marcação. Isso também resolveria o problema de várias linhas e usaria um conceito que não é inteiramente novo. Considerando que ainda temos problemas de descoberta inicial para novos usuários, não acho que isso seja muito pior, já que você precisa ter os backticks triplos à direita.

Nesse caso, isso poderia funcionar:

powershell $a = ```bash ls | grep foo ``` | select-string bar

@ SteveL-MSFT Markdown Syntax faz muito sentido. Se formos capazes de especificar. outros tipos, como python ou talvez por extensão?

O shebang #! Iria interferir nos comentários existentes.

Isso me parece muito complicado, tanto para lidar com a análise quanto para os usuários realmente _usar_.

E sim, não precisamos confundir as pessoas com linhas que _devem_ ser comentários e não estão se transformando em comentários. #Requires já está meio estranho, um monte de coisas vai confundir o pessoal do Windows na melhor das hipóteses, e provavelmente o pessoal do Linux também.

Eu pessoalmente prefiro não misturar esse recurso com shebang, é confuso na minha opinião.
Além disso, se começarmos a usar a sintaxe de bloco de código de markdown, será apenas uma questão de tempo para as pessoas solicitarem a execução de código de linguagem arbitrário diretamente no PowerShell. Eu não acho que queremos ir por esse caminho ...

Além disso, se começarmos a usar a sintaxe de bloco de código de markdown, será apenas uma questão de tempo para as pessoas solicitarem a execução de código de linguagem arbitrário diretamente no PowerShell.

Eu literalmente nunca pararia de pedir C # embutido, mesmo sabendo que é uma má ideia.

A sintaxe de markdown não introduz código de linguagem arbitrário porque o script incorporado será executado em um processo filho. Você pode colocar código de idioma arbitrário dentro de @' agora e nada de ruim acontecerá.

Na discussão da equipe e também com Jeffrey, não devemos ser contra as pessoas que desejam executar outras linguagens em um script do PowerShell. Como @ yecril71pl observou, não estamos

Não precisamos misturar as propostas de uma linha com as de várias linhas, pois poderíamos oferecer suporte a ambas, embora a desvantagem seja que agora temos duas maneiras de fazer coisas muito semelhantes, mas com sintaxe diferente.

Eu preferiria abandonar as coisas de linha única. As ilhas de código alienígena devem ser proeminentes, caso contrário ninguém entenderá o que está acontecendo.

@ yecril71pl Estou inclinado a fazer isso principalmente porque, do ponto de vista da descoberta, a sintaxe de markdown, eu acho, seria mais facilmente reconhecível, mas isso pode ser apenas porque eu escrevo markdown com frequência. Também espero que muitos scripts de exemplo sejam provavelmente multilinhas em vez de apenas uma única linha, pois eles esperam que algum estado seja retido, onde cada linha seria um processo independente.

Na discussão da equipe e também com Jeffrey, não devemos ser contra as pessoas que desejam executar outras linguagens em um script do PowerShell.

Absolutamente 100%, mas isso não significa que precisa ser integrado diretamente na linguagem.

Como @ yecril71pl observou, não estamos

Isso já pode ser feito com as strings here, certo? Você pode elaborar um pouco sobre quais benefícios isso oferece em relação aos métodos existentes?

Além disso, por que parar em python e bash? Por que não C #, CIL, C ++?

Não há nada que exija que as pessoas façam isso. Eles podem decidir portar esse outro script para o PowerShell, se preferirem. Isso apenas fornece uma opção para que os usuários possam recortar e colar no PowerShell para fazer as coisas.

Não sou contra porque acho que serei forçado a usá-lo. Não gosto porque será difícil mantê-lo do ponto de vista da linguagem, um pesadelo para os editores, e, em última análise, encorajo scripts ilegíveis, a menos que você conheça vários idiomas.


Embora este seja um conceito muito diferente do tópico original deste tópico, com implicações muito mais amplas. Eu recomendo criar um novo problema para ele.

Isso já pode ser feito com as strings here, certo? Você pode elaborar um pouco sobre quais benefícios isso oferece em relação aos métodos existentes?

Um editor de código universal será capaz de detectá-los e decorá-los apropriadamente, ao passo que não há nada a ser feito com @' porque o editor não tem ideia do que está dentro.

Além disso, por que parar em python e bash? Por que não C #, CIL, C ++?

Porque eles não são linguagens de script?

Um editor de código universal será capaz de detectá-los e decorá-los apropriadamente, ao passo que não há nada a ser feito com @' porque o editor não tem ideia do que está dentro.

Um editor pode dizer que bash -c @' contém bash tão facilmente quanto uma barreira de código. Além disso, a parte desafiadora não é determinar a linguagem de uma seção de código, é fazer malabarismos com servidores de linguagem e marcadores de sintaxe. A única maneira de funcionar sem um investimento significativo de tempo e esforço seria basicamente renderizá-lo como se fosse uma string here.

Porque eles não são linguagens de script?

C # e C ++ têm subconjuntos orientados a script. Mesmo que não o fizessem, "linguagem de script" é subjetiva, sem definição real. Não é útil como um limite definitivo para o que seria ou não considerado para inclusão.

C # e C ++ têm subconjuntos orientados a script. Mesmo que não o fizessem, "linguagem de script" é subjetiva, sem definição real.

Uma linguagem é uma linguagem de script (autônoma) quando você normalmente chama interpet options… script arguments… e essa chamada não deixa nada exceto o que deveria deixar, excluindo coisas temporárias e privadas para o intérprete. Também significa que normalmente requer uma cópia do interpretador para ser executado. Existem linguagens de script embutidas que você não pode executar dessa maneira.

@SeeminglyScience

Isso já pode ser feito com as strings here, certo? Você pode elaborar um pouco sobre quais benefícios isso oferece em relação aos métodos existentes?

As pessoas podem usar here-strings hoje ou podem escapar de todos os argumentos passados ​​para comandos nativos se puderem descobrir como fazer isso da maneira certa. A intenção aqui é tornar mais simples para novos usuários em cenários de recortar e colar (Stackoverflow).

Além disso, por que parar em python e bash? Por que não C #, CIL, C ++?

Os cenários suportados aqui são um bloco de texto passado para um comando nativo. Qualquer idioma pode ser suportado se suportar essa convenção de chamada. Não há proposta para criar um arquivo-fonte temporário e fazer com que seja compilado.

Não sou contra porque acho que serei forçado a usá-lo. Não gosto porque será difícil mantê-lo do ponto de vista da linguagem, um pesadelo para os editores, e, em última análise, encorajo scripts ilegíveis, a menos que você conheça vários idiomas.

Não há expectativa de obter cores de sintaxe de idioma misto. Qualquer script aninhado de outro idioma será apenas monotonado.

Embora este seja um conceito muito diferente do tópico original deste tópico, com implicações muito mais amplas. Eu recomendo criar um novo problema para ele.

A implementação proposta é diferente do problema original, mas o problema é o mesmo que é o cenário de recortar e colar e "apenas funcionar".

As pessoas podem usar here-strings hoje ou podem escapar de todos os argumentos passados ​​para comandos nativos se puderem descobrir como fazer isso da maneira certa. A intenção aqui é tornar mais simples para novos usuários em cenários de recortar e colar (Stackoverflow).

Certo, mas como é realmente mais fácil? Eu entendo que é subjetivo, mas isso não parece tão diferente para mim:

~~~ PowerShell
$ a = `` `bash
encontrar . -iname * $ pdf -print0 | xargs -0 ls -ltr

~~~

vs

```powershell
$a = bash -c @'
   find . -iname *$pdf -print0 | xargs -0 ls -ltr
'@

Eles parecem quase os mesmos, com a exceção de que o último é significativamente mais claro sobre o que vai acontecer.

Os cenários suportados aqui são um bloco de texto passado para um comando nativo. Qualquer idioma pode ser suportado se suportar essa convenção de chamada. Não há proposta para criar um arquivo-fonte temporário e fazer com que seja compilado.

Bem, C # não precisaria de um arquivo fonte temporário. Talvez não seja CIL (nenhuma ideia sobre C ++). De qualquer forma, embora um limite de código seja estritamente um açúcar sintático para chamar um executável (por exemplo, bash / python / cmd) é confuso para mim. Cercas de código implicam em suporte de idioma imo.

Não há expectativa de obter cores de sintaxe de idioma misto. Qualquer script aninhado de outro idioma será apenas monotonado.

Ainda será um pesadelo para analisadores baseados em tmLanguage. Eles mal conseguem lidar com a sintaxe que esperam atm. Mais do que isso, se for esse o caso, não entendo por que é diferente de uma string here.

A implementação proposta é diferente do problema original, mas o problema é o mesmo que é o cenário de recortar e colar e "apenas funcionar".

Justo, mas já estamos com 145 comentários sobre a discussão das soluções propostas anteriormente. Especialmente porque este tópico já chegou ao fim, você provavelmente verá muito mais envolvimento em um tópico novo e dedicado.

Bem, C # não precisaria de um arquivo fonte temporário. Talvez não seja CIL (nenhuma ideia sobre C ++). De qualquer forma, embora um limite de código seja estritamente um açúcar sintático para chamar um executável (por exemplo, bash / python / cmd) é confuso para mim. Cercas de código implicam suporte a idioma imo.

Eu só quero ecoar o comentário de @SeeminglyScience sobre isso. Podemos executar o script C # sem envolver a compilação com a biblioteca Microsoft.CodeAnalysis.CSharp.Scripting . Isso é o que o dotnet interativo usa no kernel C # Jupyter. Portanto, pode ser razoável que os usuários solicitem scripts C # ou até mesmo suporte a scripts F # com essa sintaxe.

Você pode dizer que a sintaxe de proteção de código no PowerShell é para chamar um comando nativo, o que teoricamente permite isso:

    ```c:\mypath\MyArbitraryExe
        args to my arbitrary executable
    ```

mas é diferente do entendimento estabelecido pelos usuários a respeito da marcação , que, como

que teoricamente permite isso

Você coloca o script dentro, não os argumentos, e o script é equivalente ao fluxo de entrada. Além disso, se o objetivo for fornecer suporte ao SO, caminhos completos não devem ser permitidos.

Você coloca o script dentro, não os argumentos, e o script é equivalente ao fluxo de entrada

O script é o argumento para bash / cmd / python.

Além disso, se o objetivo for fornecer suporte ao SO, caminhos completos não devem ser permitidos.

Em seguida, troque para este exemplo:

~ PowerShell$ a = ipconfig /all ~

Ou adicione o exe arbitrário ao caminho primeiro.

Você coloca o script dentro, não os argumentos, e o script é equivalente ao fluxo de entrada

O script é o argumento para bash / cmd / python.

Nenhum deles permite passar um script como um argumento posicional. CMD nem mesmo permite um arquivo em lote.

Além disso, se o objetivo for fornecer suporte ao SO, caminhos completos não devem ser permitidos.

Em seguida, troque para este exemplo:

$ a = `` `ipconfig
/todos

Isso não funcionaria.

Parece que estaríamos criando uma quantidade enorme de casos especiais em uma base muito arbitrária para que essa ideia de redução de preço funcionasse.

Além disso, o principal motivo pelo qual isso é suportado no Markdown é para realce de sintaxe. Essa será uma solicitação constante se adicionarmos algo como isso à linguagem.

Esta não é uma solução eficaz.

Estava discutindo com @jpsnover sobre isso e ele propôs o shebang #! como o sigilo

só para adicionar meus 2 centavos, acho que é um erro reiniciar essa discussão depois que uma solução foi anunciada. e pessoalmente não gosto da sintaxe shebang e markdown (e ambas são alterações significativas).

Não me importo de reiniciar as discussões se alguém aparecer com algo comprovadamente melhor. Na verdade, é melhor fazer isso agora do que depois que o recurso for lançado. Eu simplesmente não acho que #! ou a abordagem de redução de custos sejam a melhor solução para fornecer uma string de linha de comando "não adulterada pelo PowerShell" para um exe nativo. Talvez eu só precise tentar para me acostumar, mas minha reação inicial é ... hum, eca.

_Se nós (e o PowerShell) sabemos que executamos um executável nativo, por que precisamos "chamar o operador nativo"? _

Se o PowerShell analisar uma string de entrada e obter CommandAst, o PowerShell fará uma descoberta do comando.
Se o comando for um comando nativo, o PowerShell converte o ast em NativeCommandProcessor.
Podemos implementar new (NativeCommandProcessor experimental) para o caso que analisará novamente a extensão Ast (que é a string de entrada) para obter o nome / caminho do executável nativo e o resto da string como argumento ou argumentos pelas regras do sistema operacional.

Se um usuário deseja copiar e colar do shell, ele deve digitar "cmd"ou" bash"- isso funciona sem edição.
Se um usuário quiser copiar e colar uma linha de comando nativafuncionará também como g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Se um usuário deseja copiar e colar do shell

Temos Get-/Set-Clipboard para isso.

para o caso que analisará novamente a extensão Ast (que é a string de entrada) para obter o nome / caminho do executável nativo e o resto da string como argumento ou argumentos pelas regras do sistema operacional.

Meu pensamento aqui é que você:

  • Analise ineficientemente o comando AST, apenas para analisar novamente a extensão mais tarde como uma string e descartar o AST original, OU
  • Você pega o AST e tenta reformá-lo em uma string (da maneira que o NativeCommandProcessor atual faz), mas ao fazer isso as coisas serão perdidas

Em vez disso, precisamos preservar a string desde o início. Agora, acho que o PowerShell já tem três tipos de token de string, mas a principal solicitação nesta discussão parece ser não querer escapar das coisas. Qualquer string precisa ser capaz de escapar de seu terminador, mas a solução discutida lá parece estar usando nova linha como terminador e não ser capaz de escapar de novas linhas (sempre há ponto e vírgula).

Não estou totalmente comprometido com um terminador somente de nova linha, mas parece ser a única solução para não querer escapar de nada. Basicamente, isso faria um token de "operador de chamada nativo" semelhante a como um comentário de linha é hoje - do operador até o final da linha é a string passada para o "shell nativo".

Então:

--% ps -o pid,args -C bash | awk '/running_script/ { print $1 }'

irá tokenizar como:

--% ps -o pid,args -C bash | awk '/running_script/ { print $1 }'
|-||-----------------------------------------------------------|
 |                                  \
Native call operator        Invocation to be passed to native shell

(Observe que enviamos até mesmo o espaço diretamente após --% para o shell nativo, uma vez que a string começa imediatamente após --% )

Então, esse token é envolvido em um AST muito simples, como:

// We need to carefully work out what AST this inherits from
// This syntax has almost no interoperability with PowerShell by design,
// so can't implement fields like redirections or backgrounding faithfully.
// But in order to participate in pipelines and similar, would need to extend a more concrete class
public class NativeCallInvocationAst : StatementAst
{
    public string NativeInvocation { get; }
}

Então, depois que isso é compilado e enviado ao pipe, ele usa um novo processador de comando nativo para enviar a string diretamente como um argumento para o shell nativo.

Embora tudo pareça implementável, algumas perguntas em minha mente são:

  • O shell nativo é configurável? Se não, como podemos justificar isso? Em caso afirmativo, deve ser explícito como parte da sintaxe (complicando a sintaxe) ou implementado como uma variável de preferência (tornando mais difícil descobrir e gerenciar)?
  • Essa implementação será detectável e não confundirá as pessoas? Os usuários entenderão quão pouco o PowerShell está fazendo aqui, ou o que está acontecendo quando eles usam | ou & ou mesmo strings?
  • Quantos cenários serão atendidos por uma string textual de uma linha aqui? Vamos implementar isso apenas para descobrir que muitas pessoas querem invocações multilinhas? A necessidade de evitar fazer algo como escapar de uma aspa simples é grande o suficiente para adicionar essas restrições?
  • Este parece ser um cenário muito específico para uma nova sintaxe de linguagem. Se estamos criando uma nova sintaxe literal de string, por que a acoplamos diretamente ao conceito de invocação nativa? Se estamos criando um novo mecanismo de invocação, por que o acoplamos diretamente ao conceito de outros shells? Boa sintaxe e boas construções de linguagem compõem, tanto sintaticamente quanto funcionalmente, e tente não se envolver demais na própria linguagem (em vez de fornecer uma plataforma de elementos para o usuário montar como seu programa) - nossa sintaxe de chamada nativa proposta atende aos padrões para uma nova sintaxe elementar a ser composta em programas? Existe uma abordagem alternativa melhor que poderíamos adotar que resolva esse problema de uma maneira simples, mas ainda nos permite manter os blocos de construção desacoplados para que possam ser compostos para resolver outros problemas?
  • Outros shells ou outras linguagens implementariam isso ou já o fizeram? Em um nível de sintaxe ou em qualquer outro nível? Se sim, como?

Meu pensamento aqui é que fazendo isso você também

@rjmholt CommandAst tem propriedade Extent com string de entrada. A nova análise é tão simples quanto dividir pelo primeiro espaço no Windows ou dividir por espaços no Unix. Portanto, espero que não tenhamos perdido nada na corda e tenhamos um bom desempenho.

No entanto, isso é implementado, acho que precisamos de um conjunto de casos de uso para testar. Deixe-me "propor" o seguinte:

  • Caso de uso 1: quero que uma única invocação literal exe (sem interpolação) funcione sem saber sobre as regras de citação / escape, por exemplo: g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 . A solução atual para isso é descobrir o que citar, por exemplo: g++ timer.cpp '@conanbuildinfo.args' -o timer --std=c++11 Um exemplo mais complicado pode incluir linhas de comando abrangendo várias linhas, por exemplo:
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 

Solução alternativa atual (troca de crase por \ ), por exemplo:

tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz `
    --exclude=/proc `
    --exclude=/lost+found
  • Caso de uso 1a: quero que uma única invocação exe com interpolação PS funcione, por exemplo: g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 . A solução atual para isso é descobrir o que citar (simples ou duplo) e o que escapar, por exemplo: g++ $CppFile '@conanbuildinfo.args' -o timer --std=c++11 Um exemplo mais complicado pode incluir linhas de comando abrangendo várias linhas, por exemplo:
tar -cvpzf "/share/Recovery/Snapshots/${ComputerName}_`$(date +%Y%m%d).tar.gz" \
    --exclude=/proc \
    --exclude=/lost+found 

Solução alternativa atual, aspas primeiro arg, troque a crase para \ e escape $ no início de `$ (data + ...).

  • Caso de uso 2: desejo executar um comando literal em por exemplo: ps -o pid,args -C bash | awk '/running_script/ { print $1 }' e wsl ls $foo && echo $PWD . _Não tenho certeza se este precisa ser um caso de uso separado do anterior._ A solução atual é citar o comando bash, por exemplo: bash -c 'ps -o pid,args -C bash | awk ''/-bash/ { print $1 }''' . Este caso de uso também pode incluir strings de várias linhas, por exemplo:
ps -o pid,args -C bash | \
awk '/running_script/ { print $1 }'
  • Caso de uso 2a: desejo executar um comando em - este é um caso de uso?

  • Caso de uso 3: preciso de npm run $scriptName para continuar a trabalhar, ou seja, interpolar variáveis . (isso pode estar implícito e não precisa ser declarado).

Há uma questão de saber se deve haver casos de uso para interpolating strings, ou seja, g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 ou devemos apenas examinar isso e dizer que as regras de escape atuais cobrem isso?

Esses provavelmente não são os casos de uso certos (ou todos), mas acho que seria útil ter casos de uso documentados e ser capaz de consultá-los ao discutir esse recurso, caso contrário, é fácil se perder em como o recurso realmente aborda (ou não ) casos de uso do cliente.

Além disso, o segundo caso de uso me parece exigir "invocação de shell". Não tenho certeza se o primeiro sim. Parece que o exe pode ser invocado diretamente.

@rkeithhill Você poderia adicionar soluções alternativas existentes para integridade em seu post anterior?

@oising , eu também agradeço sua resposta atenciosa.

O que ins ... dá que & cmd --% ... não pode?

--% em sua forma atual, _parameter_ (símbolo) - cujo comportamento presumo que queremos reter - não pode ser usado para cmd /c --% , porque não suporta _multi-command_ cmd linhas de comando (nem ^ como o caractere de escape), dado que o primeiro | não citado será interpretado como operador de pipe _PowerShell's_ e que & é passado literalmente.
O mesmo se aplica ao Unix, onde --% é geralmente quase inútil e com sh -c falha completamente, porque sh espera a linha de comando como um argumento _único_ (por exemplo, sh -c --% echo hi produz uma linha vazia, porque echo sozinho é executado como o comando).

ins , com seu requisito de delimitar a linha de comando de passagem passando-a _como uma única string_ resolve esses problemas e, como declarado, também permite o uso de interpolação de string para incorporar valores de expressão e variável _PowerShell_.

E o último nos leva a por que a --% _statement_ proposta é uma _dead-end street_ :

  • Não há _nenhum caminho de transição_ da execução de uma linha de comando preexistente _exatamente como está_ para incorporar a variável _PowerShell_ e os valores de expressão nela.

    • A única - obscura e incômoda - maneira de conseguir isso é definir _variáveis ​​de ambiente auxiliar_ que armazenam os valores do PowerShell de interesse e que você pode fazer referência (shell-apropriadamente com %...% ou $... ) em a linha de comando nativa.
  • Você tem a estranheza de não poder incorporar uma instrução --% a um pipeline maior do PowerShell.

    • Observe que existem motivos legítimos de _não_-cortar-e-colar para chamar o shell nativo, onde você pode esperar essa integração; especificamente, passar _raw byte data_ por meio de um pipeline (intermediário) e enviar _strings sem uma nova linha final_ exige o uso do pipe nativo - veja esta resposta do SO para um exemplo da vida real.
  • Também existe a estranheza de --% _parameter_ ser virtualmente inútil no Unix, o que introduz uma assimetria estranha: se o --% _statement_ funciona no Unix também, por que não o --% _parameter_ ? (por exemplo,
    /bin/echo --% 'hi "noon"' produz literalmente 'hi noon' (em vez do esperado hi "noon" ), devido a echo receber _duas_ argumentos, literalmente 'hi e noon' , com as aspas simples retidas como dados e as aspas duplas removidas).


ins , como a alternativa sugerida para a instrução --% , idealmente seria apenas um _conveniente empacotador_ em torno de cmd /c e sh -c , mas na verdade é _requirido_:

  • Pelo menos _temporariamente_ no Unix, contanto que # 1995 não seja corrigido, porque chamar sh -c '...' diretamente atualmente não passa argumentos com " incorporados corretamente.

  • _Invariavelmente_ no Windows, porque você precisa passar a linha de comando _as-is_ para cmd por meio do parâmetro lpCommandLine de CreateProcess (que o parâmetro --% não pode fazer devido a ser limitado a _um_ comando); alternativamente, tentar passar a linha de comando como uma _cadeia única_ incluída em "..." geral novamente não funciona de maneira robusta devido a # 1995.


Os problemas com a passagem de _argumento_ poderiam ser evitados fornecendo a linha de comando para executar _via stdin_ (que ins poderia e deveria suportar), ou seja, _pipe_ a linha de comando / texto do script para o executável do shell:

  • No Unix, isso funciona muito bem, porque passar o código via _stdin_ para sh é essencialmente o mesmo que passar um _arquivo de script_ para execução (assim como sh -c '<code>' is ):
PSonUnix> @'
echo '{ "foo": "bar" }' | cat -n
'@ | sh

     1  { "foo": "bar" }
  • Infelizmente, no Windows cmd não lida bem com essa entrada: ele imprime seu logotipo e ecoa cada comando antes da execução, junto com a string de prompt:
PSonWin> 'date /t & ver' | cmd

Microsoft Windows [Version 10.0.18363.836]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\Users\jdoe>date /t & ver
Sun 07/26/2020

Microsoft Windows [Version 10.0.18363.836]

C:\Users\jdoe>

Infelizmente, o próprio PowerShell exibe um comportamento igualmente inútil: consulte # 3223


Assim como sh e todas as principais conchas semelhantes a POSIX ( dash , bash , ksh , zsh ), a maioria das linguagens de script _faz_ corretamente suporte a entrada stdin-as-script, como python , node , ruby e perl .

PS> 'print("hi")' | python

hi

Para incorporar valores de expressão e variável _PowerShell_, você novamente tem a opção de usar interpolação / concatenação de string:

PS> $foo = 'bar'; "print(`"$foo`")" | python

bar

ou usando o recurso de passagem de argumento do intérprete:

PS> @'
import sys
print(sys.argv[1])
'@ | python - bar

bar

Espero que o acima esclareça que _nenhuma_ necessidade de sintaxe especial - nem uma instrução --% , nem uma instrução #! , nem blocos de código Markdown - todos eles, novamente, não teriam o capacidade de incorporar valores do script de chamada do PowerShell.

Quanto à configurabilidade do shell nativo a ser usado: Para previsibilidade, não acho que devemos oferecer um; os usuários que desejam direcionar um shell diferente podem simplesmente canalizar a linha de comando para o executável específico desse shell ou, uma vez que # 1995 seja corrigido, passá-lo como um argumento.

  • Uma variação discutível é direcionar /bin/bash (com um (improvável) fallback para /bin/sh ) - enquanto se esperaria que as linhas de comando publicadas dependessem apenas de recursos mandatados por POSIX, linhas de comando com bashismo (recursos não POSIX que nem todas as implementações de /bin/sh suportam) provavelmente estão por aí, dada a prevalência de bash .

@joeyaiello

Em minha opinião, os benefícios desse recurso específico são muito mais significativos do que o impacto # 1995

Acho surpreendente que gastemos tanto esforço quanto temos para fazer esse recurso de recortar e colar - principalmente para conveniência _interativa_ - um cidadão de primeira classe (estranho, com recursos limitados), enquanto a incapacidade fundamental do PowerShell de passar vazio argumentos e argumentos com " chars incorporados.

mas não estou vendo as pessoas atingirem o número 1995 em massa

Sim, e pelas razões que discuti anteriormente _você verá cada vez mais_.

Novamente, eu diria que um shell que faz o seguinte, como o PowerShell faz atualmente, tem um problema sério :

# On Unix
PS> /bin/echo '{ "foo": "bar" }'

{ foo: bar }

@rjmholt CommandAst tem propriedade Extent com string de entrada. A nova análise é tão simples quanto dividir pelo primeiro espaço no Windows ou dividir por espaços no Unix. Portanto, espero que não tenhamos perdido nada na corda e tenhamos um bom desempenho.

Sim, eu entendo o empate lá, e talvez as alocações do AST nesse cenário não sejam horríveis (embora eu argumentasse que a linguagem não deveria gerar alocações desnecessárias para sintaxe simples). Mas tudo que você precisa fazer é imaginar uma entrada que bash / cmd vê como uma string e o PowerShell não para começar a encontrar problemas:

Não analisa com o PowerShell atual:

--% my_command "\" "

Analisa, mas a divisão por espaços dá o resultado errado:

--% my_command "\"    "\"

(Devemos enviar 4 espaços na string, mas o PowerShell vê duas strings separadas em uma matriz)

@rjmholt , todas as grandes questões estão no final do seu comentário acima , o que para mim novamente sugere que a resposta certa é: não há necessidade de sintaxe especial.

Abordei a questão sobre qual executável de shell nativo usar em meu comentário anterior , mas quanto a:

Outros shells ou outras linguagens implementariam isso ou já o fizeram? Em um nível de sintaxe ou em qualquer outro nível? Se sim, como?

Não estou ciente de que qualquer outra linguagem fez isso no nível de sintaxe sem empregar _delimitadores explícitos_ e permitir _interpolação_.
Por exemplo, perl tem `...` para executar comandos shell, então você pode executar o seguinte comando a partir de um script no Unix, por exemplo:
my $foo='/'; print `ls $foo | cat -n`

Os principais aspectos são o uso de delimitadores explícitos e a capacidade de incorporar valores do chamador - ambos ausentes na proposta atual para a instrução --% .

Como afirmado antes, nós _podemos_ usar esta abordagem para um novo _operador_ (ou algum tipo de sintaxe baseada em delimitador, opcionalmente interpolada), mas não acho que valha a pena, dado que
ins "ls $foo | cat -n" (ou com uma variante here-string) serve, sem sobrecarregar a linguagem com complexidade adicional.

perl oferece uma solução de nível de sintaxe (adequada) _porque ele mesmo não é um shell_, mas deseja fazer chamadas de shell o mais conveniente possível.

Por outro lado, nós _semos_ um shell (também) e oferecemos invocações no estilo shell _diretamente_, sem qualquer sintaxe adicional (embora atualmente falho - veja # 1995).
O fornecimento de sintaxe especial para tornar as invocações de _um shell diferente_ o mais fácil possível parece desnecessário.

@iSazonov

_Se nós (e o PowerShell) sabemos que executamos um executável nativo, por que precisamos "chamar o operador nativo"? _

Se o PowerShell analisar uma string de entrada e obter CommandAst, o PowerShell fará uma descoberta do comando.
Se o comando for um comando nativo, o PowerShell converte o ast em NativeCommandProcessor.
Podemos implementar new (NativeCommandProcessor experimental) para o caso que analisará novamente a extensão Ast (que é a string de entrada) para obter o nome / caminho do executável nativo e o resto da string como argumento ou argumentos pelas regras do sistema operacional.

Se um usuário deseja copiar e colar do shell, ele deve digitar "cmd" ou "bash" - isso funcionará sem edição.
Se um usuário deseja copiar e colar uma linha de comando nativa funcionará também como g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Mas espere, você nunca seria capaz de usar qualquer sintaxe do PowerShell com comandos nativos se fizéssemos tudo isso? Então, como sc.exe query $serviceName passaria $serviceName como uma string literal?

Mas espere, você nunca seria capaz de usar qualquer sintaxe do PowerShell com comandos nativos se fizéssemos tudo isso? Então, como sc.exe query $serviceName passaria $serviceName como uma string literal?

A opção --% não se destina a atender a essa necessidade.

sc.exe query $serviceName é gerenciado por &"<cmd>"

A opção --% não se destina a atender a essa necessidade.

sc.exe query $serviceName é gerenciado por &"<cmd>"

Sim, com certeza, eu não acho que é a isso que @iSazonov está se referindo. Estou lendo isso como se estivessem sugerindo uma mudança geral em como a vinculação de parâmetros funciona para comandos nativos.

Sim, com certeza, eu não acho que é a isso que @iSazonov está se referindo. Estou lendo isso como se estivessem sugerindo uma mudança geral em como a vinculação de parâmetros funciona para comandos nativos.

Senhor, espero que não.

Senhor, espero que não.

Às vezes, quando você está pensando no abstrato, é fácil esquecer o óbvio. Quero dizer, para ser justo, todos nós lemos e imediatamente começamos a pensar na implementação sem perceber o quanto iria quebrar.

Ou estou perdendo algo óbvio e não é isso que está sendo proposto 😁

@essentialexch , para ser claro, re:

sc.exe query $serviceName é gerenciado por &"<cmd>"

& "<cmd>" só funciona se <cmd> avaliado como um mero comando _nome ou caminho_, _não incluindo argumentos_ - os argumentos devem ser passados ​​separadamente, individualmente - e eles não precisam de "...." citando referências de variáveis ​​a serem expandidas (por exemplo,
$exe = 'findstr'; & "where.exe $exe" falha por design; deve ser & "where.exe" $exe (aspas duplas são opcionais aqui; se omitido, & é opcional))

Sim, com certeza, eu não acho que é a isso que @iSazonov está se referindo. Estou lendo isso como se estivessem sugerindo uma mudança geral em como a vinculação de parâmetros funciona para comandos nativos.

Minha proposta é consertar o nº 1995 (com alterações significativas) o mais simples possível. Obviamente, a interpolação não funciona. Obviamente, não é a proposta final - é uma demonstração de como poderíamos implementar uma correção em minutos. E principalmente demonstra que não precisamos de um operador nativo de chamada como @ mklement0 também apontou.

Analisa, mas a divisão por espaços dá o resultado errado:

@rjmholt Sim, mas acho que você
O primeiro filho de CommandAst é StringConstantExpressionAst com o comando nativo como BareWord. Podemos cortar a palavra nua de CommandAst Extent e obter a lista de parâmetros sem alteração ast-s. Mas poderíamos adicionar novo ast para manter a lista de parâmetros também.
Dedico um tempo para a explicação porque abaixo quero resumir a discussão e sinto que precisaremos implementar algumas melhorias que parecem complexas, mas acho que elas poderiam ser implementadas de forma simples e __com fácil opt-out para compatibilidade com versões anteriores__.


Acredito que a intenção do @SteveL-MSFT da proposta inicial era evitar uma alteração significativa.
Eu concordo com @TSlivede e @ mklement0 que esta discussão mostrou que a nova sintaxe não é necessária, pois ela não resolve todos os problemas _fundamentais_ como # 1995 e # 12975. Por outro lado, adiciona complexidade à linguagem, ao passo que queremos, ao contrário, _simplificar_ o trabalho com aplicativos nativos.

__A única maneira de avançar é fazer uma alteração significativa ou mais .__ (Depois disso, um novo operador ou cmdlet também pode ser útil em alguns cenários.)


Devemos começar com um resumo dos casos de uso e das expectativas do usuário.
Veja a excelente postagem de @rkeithhill acima

  1. Os usuários desejam ativar / desativar a interpolação.
    O PowerShell já tem strings / strings here com aspas simples e duplas.
    Nosso objetivo é entender como aplicar / refinar / aprimorar seu.
  2. Os usuários querem executar qualquer executável ou um shell

    • qualquer executável - o comando começa com o nome do aplicativo e segue os argumentos

      • com / sem interpolação

      • em linha única / em linha múltipla

    • shell é provavelmente um caso especial porque argumento (s) é um _script_

      • no cenário de copiar e colar, os usuários não querem interpolação e querem uma ou várias linhas

      • no cenário de script, os usuários desejam habilitar / desabilitar a interpolação e desejam uma ou várias linhas

    Pensamentos:
    Isso nos faz pensar que o comportamento do cenário de copiar e colar deve ser o padrão.
    Os terminadores de linha dependem do executável (no bash, `no PowerShell). Isso nos faz pensar que:
    (1) um shell deve ser explicitamente declarado como qualquer executável,
    (2) talvez qualquer executável com várias linhas deva ser chamado por meio de um shell ou temos que fazer uma análise especial (para remover o terminador e construir a lista de argumentos voltando ao problema de escape)
    (3) poderíamos abordar o cenário de copiar e colar no PSReadline. Ele poderia converter um buffer para o comando correto do PowerShell.

  3. Os usuários desejam uma linha e várias linhas.

    • talvez precisemos aprimorar as strings here para uma única linha para abordar alguns cenários conforme discutido anteriormente.
    • consulte 2 - (2) - sempre use um shell para multilinhas ou implemente uma análise especial

Não quero escrever mais porque @TSlivede e @ mklement0 podem atualizar seus pensamentos e conclusões anteriores, mas que agora podem ser baseados na aceitação de alterações significativas. Eu pediria para abrir um novo problema (e fechar todos os antigos), enumerar todos os casos de uso (começando com @ @ rkeithhill 's e adicionar mais) e talvez criar testes Pester - seria uma ótima etapa para corrigir o problema.

Quero apenas acrescentar que poderíamos considerar novos cmdlets para simplificar a adoção do usuário como Invoke-Shell (Invoke-NativeShell), Test-Arguments (como echoargs.exe para mostrar que o aplicativo nativo obtém e mostra uma ajuda para os usuários), Convert-Arguments ( para converter a entrada do usuário em argumentos de escape corretos).

PS: Como mostrei acima, o comportamento de linha única pode ser facilmente desativado para compatibilidade com versões anteriores em tempo de execução.

Os terminadores de linha dependem do executável (no bash, `no PowerShell).

\ não é um terminador de linha em bash .

Obviamente, a interpolação não funciona. Obviamente, não é a proposta final - é uma demonstração de como poderíamos implementar uma correção em minutos. E principalmente demonstra que não precisamos de um operador nativo de chamada como @ mklement0 também apontou.

Da mesma forma que poderíamos corrigir todos os bugs em alguns minutos, se apenas excluíssemos o repo. Essa é uma pausa enorme que você está propondo. Mesmo que seja apenas hipotético, não vejo como isso pode ajudar.

Mesmo que seja apenas hipotético, não vejo como isso pode ajudar.

@SeeminglyScience Parece que estamos em contextos diferentes. Eu digo que podemos implementar uma correção para uma chamada nativa de linha única como uma alteração importante, mas sem interromper o Parser. Em outras palavras, podemos até mesmo mudar o comportamento em tempo real, independentemente do novo comportamento que implementaríamos.

Em outras palavras, podemos até mesmo mudar o comportamento em tempo real, independentemente do novo comportamento que implementaríamos.

Acho que o que estou perdendo é como você muda esse comportamento em tempo real, sem pedir explicitamente por isso (como com um operador de chamada nativa).

@iSazonov

_Se nós (e o PowerShell) sabemos que executamos um executável nativo, por que precisamos "chamar o operador nativo"? _

Se o PowerShell analisar uma string de entrada e obter CommandAst, o PowerShell fará uma descoberta do comando.
Se o comando for um comando nativo, o PowerShell converte o ast em NativeCommandProcessor.
Podemos implementar new (NativeCommandProcessor experimental) para o caso que analisará novamente a extensão Ast (que é a string de entrada) para obter o nome / caminho do executável nativo e o resto da string como argumento ou argumentos pelas regras do sistema operacional.

Se um usuário deseja copiar e colar do shell, ele deve digitar "cmd" ou "bash" - isso funcionará sem edição.
Se um usuário deseja copiar e colar uma linha de comando nativa funcionará também como g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Quero confirmar o que você quer dizer com isso: Você está sugerindo ignorar essencialmente toda a sintaxe do PowerShell ao chamar executáveis ​​externos?

Especificamente: você está sugerindo que, por exemplo,

/bin/echo (1..5)

no PowerShell não colocará mais 1 2 3 4 5 mas, em vez disso, deverá gerar a saída literal (1..5) ?

Se é isso que você está sugerindo, então tenho que dizer: acho que é uma péssima ideia.

Isso não simplifica as coisas (os executáveis ​​externos agora se comportam sintaticamente diferentes dos cmdlets internos) e também não precisa fazer nada com o # 1995 IMHO ...

@rjmholt
De cima :

  • Outros shells ou outras linguagens implementariam isso ou já o fizeram? Em um nível de sintaxe ou em qualquer outro nível? Se sim, como?

A única coisa que me vem à mente seria ipythons ! operator :

Executando !ps -'fax' || true nas saídas ipython:

  261 pts/0    Sl     0:00          \_ /usr/bin/python /usr/bin/ipython
  311 pts/0    S      0:00              \_ /bin/bash -c ps -'fax' || true
  312 pts/0    R      0:00                  \_ ps -fax

( ps não mostra aspas em torno dos argumentos , mas ps -'fax' || true é realmente fornecido como um único argumento para o bash.)

No entanto, esse recurso ainda permite a interpolação de variáveis ​​Python nesse comando ( {pyvar} ).

E semelhante ao @ mklement0 's exemplo , ipython apenas implementa este

porque não é em si uma casca

Se o ipython permitisse apenas a sintaxe do python, não seria muito útil como um shell, então eles permitem que esta sintaxe encaminhe comandos para o bash para serem usados ​​como um shell. O Powershell, por outro lado, afirma ser um shell (até que # 1995 seja resolvido, não direi que é um shell), então seria muito estranho adicionar uma sintaxe especial para chamar outros shells.

Se algo como isso for realmente implementado por qualquer motivo (espero que não), sugiro usar ! vez de --% como o "operador de shell de chamada".

! é algo que as pessoas podem adivinhar (porque conhecem ! para esse propósito do ipython (ou do modo de comando vim ou de less ou de ftp ) )

--% por outro lado é mais difícil de digitar, improvável de ser adivinhado e o mais importante: o uso atual de --% tem, em minha opinião, muito pouco em comum com "passar coisas para outro shell" comportamento. O --% atual emula exatamente um elemento da sintaxe cmd: substituição de variável (e mesmo assim não completamente). Ele não suporta o caractere de escape ^ , não permite o redirecionamento para arquivos e assim por diante. A única coisa de alguma forma útil sobre o --% atual é a capacidade de passar conteúdo literal para lpCommandLine - mas isso é algo em que o "operador de shell de chamada" proposto não é bom.

Mais uma diferença (que já foi mencionada) entre o "operador de shell de chamada" proposto e o atual --% : um termina em tubos, o outro não - muito confuso para novos usuários. ! por outro lado encaminha | para o shell em todos os aplicativos que testei até agora (ipython, vim, less, ed, etc.).

HELP about_Logical_Operators -S

Sim, ! por si só é problemático porque é um operador de negação lógico na linguagem.

BTW, uma das propostas alternativas era &! - uma espécie de operador "call native / shell".

Eu gostaria que o @TSlivede tivesse enfatizado mais a parte do "espero que não" quando disse "Se algo assim for, por qualquer motivo, realmente implementado (espero que não)"

  • Nenhum operador / instrução _com argumentos individuais_ pode resolver claramente o problema de linha de comando eu-quero-usar-valores-PowerShell-no-nativo-de-linha de comando, dado que $ é usado como o sigilo variável em _both_ PowerShell e shells semelhantes a POSIX.

  • Nenhum operador / instrução _sem delimitadores explícitos ao redor de toda a linha de comando nativa_ pode resolver o problema de onde termina a linha de comando nativa (com o qual você pode ou não se importar).

@ PowerShell / powershell-Committee discutiu isso hoje. Neste ponto, não parece haver acordo sobre a declaração inicial do problema, nem um design sobre como resolvê-lo, então não acreditamos que possamos chegar a 7.1 com um design estável.

@ PowerShell / powershell-Committee discutiu isso hoje. Neste ponto, não parece haver acordo sobre a declaração inicial do problema, nem um design sobre como resolvê-lo, então não acreditamos que possamos chegar a 7.1 com um design estável.

182 comentários!

Tem havido uma grande discussão sobre isso! Continuarei monitorando quaisquer novos comentários sobre este assunto. Ainda incentivamos a comunidade a implementar o cmdlet Invoke-NativeCommand type publicado no PSGallery independentemente desse problema.

Acho lamentável, quando você tinha uma solução de compromisso, mas muito útil em https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -662732627, abandonar isso. Eu sei que você quer consenso, mas às vezes um acordo é o melhor que pode acontecer. Não, não é perfeito, mas uma solução SO de uma linha seria extremamente útil. Pelo menos na opinião desta pessoa.

@essentialexch para ficar claro, não temos consenso nem dentro da equipe do PowerShell

Uma vez que isso é adiado, e quanto à implementação de ajudantes

poderíamos considerar novos cmdlets para simplificar a adoção do usuário como Invoke-Shell (Invoke-NativeShell), Test-Arguments (como echoargs.exe para mostrar que o aplicativo nativo obtém e mostrar uma ajuda para os usuários), Convert-Arguments (para converter a entrada do usuário para argumentos escapados à direita).

Acho que os argumentos de teste podem ser muito úteis.

Acabei de publicar um módulo, Native , ( Install-Module Native -Scope CurrentUser ) que convido a todos a experimentarem e cujos comandos estarei usando abaixo ; os casos de uso numéricos referenciados são do comentário de acima .

Se quisermos que a névoa conceitual se esclareça, acho que precisamos enquadrar os casos de uso de maneira diferente.
_Nenhum deles requer alterações na análise_.

Caso de uso [chamada de shell nativo]:

Você deseja executar um _comando individual_ (caso de uso 1) ou uma _linha de comandos múltiplos_ inteira (caso de uso 2) _escrito para o shell nativo da plataforma_ (este problema):

  • Já que você está usando uma sintaxe de shell diferente, você está passando o comando (linha) _como uma única string_ para o shell de destino, para que _it_ faça a análise; A interpolação de string do PowerShell oferece a capacidade de incorporar valores do PowerShell à string passada (caso de uso 2a).
  • ins ( Invoke-NativeShell ) cobre esses casos de uso.
# Use case 1: Single executable call with line continuation, on Unix.
@'
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 
'@ | ins

# Use case 2: Entire Bash command line (also with line continuation)
@'
ps -o pid,args | awk \
  '/pwsh/ { print $1 }'
'@ | ins

# Use case 2a: Entire Bash command line (also with line continuation) with string interpolation.
# Note the double-quoted here-string and the need to escape the $ that is for Bash as `$
$fields = 'pid,args'
@"
ps -o $fields | awk \
  '/pwsh/ { print `$1 }'
"@ | ins

# Alternative to use case 2a: pass the PowerShell value *as a pass-through argument*,
# which allows passing the script verbatim.
# Bash sees the pass-through arguments as $1, ... (note that the `awk` $1 is unrelated).
@'
ps -o $1 | awk \
  '/pwsh/ { print $1 }'
'@ | ins - 'pid,args'

A sintaxe here-string não é a mais conveniente (daí a sugestão de implementar uma variante in-line - veja # 13204), mas você não _tem_ de usá-la; para comandos verbatim, você pode usar '...' , que requer apenas _doubling_ incorporado ' , se presente.

Além disso, aqui está um substituto razoável para o "operador StackOverflow" :

Se você colocar o seguinte manipulador de teclas PSReadLine em seu arquivo $PROFILE , poderá usar Alt-v para criar uma chamada para ins com uma string here literalmente no qual o texto da área de transferência atual é colado.Enter submete a chamada.

# Scaffolds an ins (Invoke-NativeShell) call with a verbatim here-string
# and pastes the text on the clipboard into the here-string.
Set-PSReadLineKeyHandler 'alt+v' -ScriptBlock {
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert("@'`n`n'@ | ins ")
  foreach ($i in 1..10) { [Microsoft.PowerShell.PSConsoleReadLine]::BackwardChar() }
  # Comment the following statement out if you don't want to paste from the clipboard.
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert((Get-Clipboard))
}

Caso de uso [chamada executável direta]:

Você deseja chamar um _único executável externo_ usando a sintaxe do _PowerShell_, com _ argumentos individuais_ (casos de uso 1a e 3).

  • Este é um mandato básico de qualquer shell e é de vital importância que funcione de forma robusta.

    • Você precisa contar com o PowerShell capaz de passar por quaisquer argumentos que resultem da _its_ análise _as-is_ para o executável de destino. Atualmente, este não é o caso - ver # 1995 .
  • Ele requer que você compreenda a sintaxe do _PowerShell_ e os _seus_ metacaracteres de modo de argumento.

    • Você precisa estar ciente de que ` é usado como o caractere de escape e para a continuação da linha.
    • Você precisa estar ciente de que o PowerShell tem metacaracteres adicionais que requerem aspas / escape para uso literal; estes são (observe que @ só é problemático como o primeiro caractere de um argumento.):

      • para shells semelhantes a POSIX (por exemplo, Bash): @ { } ` (e $ , se você quiser evitar a expansão inicial pelo PowerShell)
      • por cmd.exe : ( ) @ { } # `
      • Individualmente ` escapando de tais caracteres. é suficiente (por exemplo, printf %s `@list.txt ).

Enquanto esperamos que # 1995 seja consertado, a função ie (para i nvoke (externo) e xecutável) preenche a lacuna, simplesmente adicionando uma chamada direta; por exemplo, em vez da seguinte chamada:

# This command is currently broken, because the '{ "name": "foo" }' argument isn't properly passed.
curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

você usaria o seguinte:

# OK, thanks to `ie`
ie curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

O módulo Native vem com algo semelhante a echoArgs.exe , o comando dbea ( Debug-ExecutableArguments ); exemplo de saída no Windows:

# Note the missing first argument, the missing " chars., and the erroneous argument boundaries.
PS> dbea '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
7 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <3 of snow Nat>
  <King>
  <Cole c:\temp>
  <1\ a>
  <">
  <b>

Command line (helper executable omitted):

  a&b 3" of snow "Nat "King" Cole" "c:\temp 1\\" "a \" b"

Usando a opção -UseIe ( -ie ), você pode dizer a dbea para passar os argumentos via ie , o que demonstra que corrige os problemas:

# OK, thanks to -UseIe
PS> dbea -UseIe '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
6 argument(s) received (enclosed in <...> for delineation):

  <>
  <a&b>
  <3" of snow>
  <Nat "King" Cole>
  <c:\temp 1\>
  <a \" b>

Command line (helper executable omitted):

  "" a&b "3\" of snow" "Nat \"King\" Cole" "c:\temp 1\\" "a \\\" b"

Nota: Uma vez que # 1995 é corrigido, ie não é mais necessário; no interesse da compatibilidade futura, ie foi projetado para detectar e adiar automaticamente para uma correção; ou seja, uma vez que a correção esteja implementada, ie irá parar de aplicar suas soluções alternativas e irá efetivamente agir como uma chamada direta / & .

Caso de uso [chamada direta executável do Windows não autorizado]:

Existem dois problemas principais, que surgem no _Windows only_:

  • Você está invocando um executável "não autorizado" que possui requisitos de cotação que diferem da convenção mais amplamente usada ; por exemplo, msiexec.exe e msdeploy.exe exigem que prop="<value with spaces>" seja citado precisamente dessa forma - a citação apenas em torno da parte _valor_ - embora "prop=<value with spaces>" - citação de todo argumento - _deve_ ser equivalente (o último é o que o PowerShell - justificadamente - faz nos bastidores).

  • Você está invocando _um arquivo em lote_ com _um argumento que não contém espaços, mas contém cmd.exe metacaracteres_ como & , ^ ou | ; por exemplo:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell - justificadamente - passa http://example.org/a&b _unquoted_ (já que não há nenhum espaço em branco incorporado), mas isso quebra a invocação do arquivo em lote, porque cmd.exe - irracionalmente - sujeita os argumentos passados ​​para um arquivo em lote ao mesma regra de análise que se aplicaria dentro de cmd.exe vez de aceitá-los como literais.

    • Observação: embora o uso de arquivos em lote para implementar _diretamente_ a funcionalidade esteja provavelmente diminuindo, usá-los como _pontos de entrada CLI_ ainda é muito comum, como o az CLI do Azure, que é implementado como arquivo em lote az.cmd .

Nota: ie lida automaticamente com esses cenários para arquivos em lote e para msiexec.exe e msdeploy.exe , portanto, para _most_ chamadas, nenhum esforço extra deve ser necessário ; entretanto, é impossível antecipar _todos_ os executáveis ​​"invasores".

Existem duas maneiras de resolver isso:

  • Dado que cmd.exe , após aplicar sua própria interpretação aos argumentos em uma linha de comando, _does_ preserva as aspas conforme especificado, você pode delegar a uma chamada ins no Windows:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Se você usar uma string _expandable_, isso novamente permitirá que você incorpore valores do PowerShell na string de comando.

      • $url = 'http://example.org/a&b'; ins ".\someBatchFile.cmd `"$url`""

  • Como alternativa, use a implementação --% atual, mas tome cuidado com suas limitações:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Dado como --% é implementado, a única maneira de incorporar valores do PowerShell é - desajeitadamente - definir um _aux. variável de ambiente_ e referenciá-la com a sintaxe %...% :

      • $env:url = 'http://example.org/a&b'; .\someBatchFile.cmd --% "%url%"


Manipulação de erros

O https://github.com/PowerShell/PowerShell-RFC/pull/88 pendente trará melhor integração de tratamento de erros com executáveis ​​externos (nativos).

Nesse ínterim, o módulo Native vem com dois recursos para ad-hoc opt-in para tratar um código de saída diferente de zero como um erro de encerramento de script :

  • ins suporta a opção -e / -ErrorOnFailure , que lança um erro se $LASTEXITCODE for diferente de zero após a chamada ao shell nativo.

  • iee é uma função de empacotamento para ie que analogamente lança um erro se $LASTEXITCODE for diferente de zero após a chamada para o executável externo.

Existem muitas sutilezas nos comandos que acompanham o módulo.
A ajuda baseada em comentários bastante extensa, esperançosamente, cobre-os suficientemente.

Se quisermos que a névoa conceitual se esclareça, acho que precisamos enquadrar os casos de uso de maneira diferente.

Ótimo! O que postei anteriormente foi apenas para fazer as pessoas pensarem em criar mais e melhores casos de uso.

Devo esclarecer alguns aspectos importantes de ins ( Invoke-NativeShell ) que diferem do que propus anteriormente; o objetivo era contabilizar a onipresença de bash e fornecer uma CLI consistente em todas as plataformas.

  • No Unix , decidi chamar /bin/bash vez de /bin/sh , dada a onipresença do Bash, mas você pode optar por usar /bin/sh com -UseSh ( -sh ) ( /bin/sh também serve como substituto no evento improvável de /bin/bash não estar presente).

    • No interesse de uma experiência consistente em formulários e plataformas de invocação, escondi a capacidade de definir $0 , o nome de invocação; ou seja, todos os argumentos de passagem começam com $1 , o que é consistente com a forma como os argumentos são transmitidos quando você canaliza o script do pipeline e, provavelmente, o que os usuários esperam:
# Script as argument
PSonUnix> ins 'echo $# arguments; echo "[$1] [$2]"' one two
2 arguments
[one] [two]

# Script via pipeline; note the "-" to signal that the script is piped.
PSonUnix> 'echo $# arguments; echo "[$1] [$2]"' | ins - one two
2 arguments
[one] [two]

# As an aside: script as argument, with pipeline input *as data*:
PSonUnix> 'one', 'two' | ins 'cat -n'
     1  one
     2  two
  • No Windows , tive que usar um _arquivo em lote_ temporário por motivos técnicos, mas acho que o uso da sintaxe de arquivo em lote é a melhor escolha de qualquer maneira ( %%i vez de %i em for variáveis ​​de loop, capacidade de escapar % como %% ).

    • O uso de um arquivo em lote também permite o suporte para argumentos de passagem, como no Unix:
# Script as argument
PSonWin> ins 'echo [%1] [%2]' one two
[one] [two]

# Script via pipeline; note the "-" to signal that the script is piped.
PSonWin> 'echo [%1] [%2]' | ins - one two
[one] [two]
Esta página foi útil?
0 / 5 - 0 avaliações