Powershell: 外部実行可能ファイルの引数が正しくエスケープされていない

作成日 2016年08月21日  ·  170コメント  ·  ソース: PowerShell/PowerShell

再現する手順

  1. ARGVを取得するCプログラムnative.exeを書く
  2. native.exe "`"a`""実行します

    予想される行動

ARGV [1] == "a"

実際の動作

ARGV [1] == a

環境データ

Windows 10 x64

Name                           Value
----                           -----
PSVersion                      5.1.14393.0
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14393.0
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
Committee-Reviewed WG-Engine

最も参考になるコメント

その主な仕事はプログラムを起動して引数を渡すことなので、このための特別な演算子を作成することは、コマンドラインシェルにはまったく意味がありません。 このジョブにsystem()を実行する新しい演算子を導入することは、Matlabが算術演算にバグがあるため、 calc.exeを呼び出す方法を導入することに似ています。 代わりにすべきことは次のとおりです。

  • pwshチームは、コマンドラインを修正する新しいメジャーリリースの準備をし、現在の動作を組み込みのコマンドレットの背後に移動します。
  • 一時的な解決策として、次のpwshバージョンには、コマンドラインの受け渡しに新しい正しい動作を使用する組み込みのコマンドレットが含まれています。

同じことがStart-Processも当てはまります。 (実際には、 -QuotingBehavior Legacyようないくつかのオプションを備えた「新しい」コマンドレットのかなり良い候補です...)#13089を参照してください。

全てのコメント170件

なぜ"\"a\""が期待される動作だと思いますか? PowerShellエスケープについての私の理解では、実際の動作は正しく予想される動作であると言えます。 " " ""は、 a囲むエスケープされた引用符のペアを囲む引用符のペアであるため、PowerShellは、エスケープされていない外側のペアを「これは文字列引数です」と解釈します。したがって、それらを削除し、エスケープされたペアをエスケープされた引用符として解釈して保持し、 "a"残します。文字列に\追加されることはありませんでした。

Bashが\をエスケープ文字として使用しているという事実は関係ありません。 PowerShellでは、エスケープ文字はバッククォートです。 PowerShellエスケープ文字を参照してください。

文字通り"\"a\""を渡したい場合は、次を使用すると思います。

> echo `"\`"a\`"`"
"\"a\""

@andschwa
はい、エスケープは内部コマンドレットでは正常に機能しますが、特にWindowsでネイティブバイナリと通信すると、状況がおかしくなります。
native.exe " "a ""場合、ARGV [1]は次のようになります。

"a"

(3文字)

の代わりに

a

(1文字)。

現在、 native.exeが2つの引用符とa文字を含むARGVを正しく受信できるようにするには、次の奇妙な呼び出しを使用する必要があります。

native.exe "\`"a\`""

ああ、分かった。 再開。

強い好奇心から、#1639を使用してビルドを試みるとどうなりますか?

@andschwa同じです。 PowerShellとCommandLineToArgvW両方を満足させるには、二重にエスケープする必要があります。 この行:

native.exe "`"a`""

StartProcessはcmdと同等になります

native.exe ""a""

@ be5invis @douglaswthこれはhttps://github.com/PowerShell/PowerShell/pull/2182で解決されてい

いいえ、バッククォートをエスケープした二重引用符の前にバックスラッシュを追加する必要がありますか? これは、二重エスケープの問題を解決しません。 (つまり、PowerShellとCommandLineToArgvWの両方を二重引用符で囲む必要があります。)

" " ""'"a"'に等しいので、 native.exe '"a"'"\"a\""になることを提案しますか?

これは、実装された場合、必要な二重エスケープを使用する既存の多数のPowerShellスクリプトを壊す可能性があるという機能要求のように思われるため、どのソリューションでも細心の注意が必要になります。

@vorsはい。
@douglaswth二重エスケープは本当にばかげています:なぜDOS時代に作られた「内部」エスケープが必要なのですか?

@vors @douglaswth
これは、GetCommandLineWおよびCommandLineToArgvWの結果を表示するために使用されるCコードです。

#include <stdio.h>
#include <wchar.h>
#include <Windows.h>

int main() {
  LPWSTR cmdline = GetCommandLineW();
  wprintf(L"Command Line : %s\n", cmdline);

  int nArgs;
  LPWSTR *szArglist = CommandLineToArgvW(cmdline, &nArgs);
  if (NULL == szArglist) {
    wprintf(L"CommandLineToArgvW failed\n");
    return 0;
  } else {
    for (int i = 0; i < nArgs; i++) {
      wprintf(L"argv[%d]: %s\n", i, szArglist[i]);
    }
  }
  LocalFree(szArglist);
}

これが結果です

$ ./a "a b"
Command Line : "Z:\playground\ps-cmdline\a.exe" "a b"
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: a b

$ ./a 'a b'
Command Line : "Z:\playground\ps-cmdline\a.exe" "a b"
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: a b

$ ./a 'a"b'
Command Line : "Z:\playground\ps-cmdline\a.exe" a"b
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: ab

$ ./a 'a"b"c'
Command Line : "Z:\playground\ps-cmdline\a.exe" a"b"c
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: abc

$ ./a 'a\"b\"c'
Command Line : "Z:\playground\ps-cmdline\a.exe" a\"b\"c
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: a"b"c

@ be5invis二重エスケープが煩わしいことについては同意しませんが、これに対する変更は、既存のPowerShellスクリプトが使用するものと下位互換性がある必要があると言っているだけです。

それらはいくつですか? そのような二重引用について知っている脚本家はいないと思います。 これはバグであり、機能ではなく、文書化されていません。

???? iPhone

? 2016年9 21 ?? 01:???58ダグラススリフト< [email protected] [email protected] > ???

@ be5i nvishttps://github.com/be5invis二重エスケープが煩わしいことについては同意しませんが、これを変更するには、既存のPowerShellスクリプトが使用するものと下位互換性が必要であると言っているだけです。

あなたが言及されたのであなたはこれを受け取っています。
このメールに直接返信するか、Gi tHubhttps://github.com/PowerShell/PowerShell/issues/1995#issuecomment -248381045で表示するか、読み取りをミュートします

PowerShellは9年前から存在しているため、かなりの数のスクリプトが存在する可能性があります。 StackOverflowや他のソースからの二重エスケープの必要性についての情報がたくさん見つかったので、その必要性について誰も知らないというあなたの主張に同意するのか、それとも文書化されていないのかわかりません。 。

追加のコンテキストとして、実装について少しお話ししたいと思います。
PowerShellは.NETAPIを呼び出して、Win32 API(Windows上)を呼び出す新しいプロセスを生成します。

ここで、PSは使用するStartProcessInfoを作成します
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L1063

提供されているAPIは、引数に1つの文字列を受け取り、それを引数の配列に再解析して実行します。
この再解析のルールは、PowerShellによって制御されていません。 これはWin32APIです(そして幸いなことに、dotnet coreとunixのルールで一貫しています)。
特に、このコントラクトは\"動作を記述しています。

PowerShellはよりスマートになり、より優れたエクスペリエンスを提供しようとする場合がありますが、現在の動作はcmdおよびbashと一致しています。ネイティブの実行可能行をそれらからコピーして、PowerShellで使用でき、同じように機能します。

@ be5invis中断しない方法で有効期限を延長する方法を知っている場合は、詳細を並べてください。 重大な変更については、 https://github.com/PowerShell/PowerShell/blob/master/docs/dev-process/breaking-change-contract.mdで説明されているように、RFCプロセスを使用する必要があり

これはWindowsにも当てはまりますが、LinuxまたはUnixでコマンドを実行する場合、引用符を二重にエスケープする必要があるのは奇妙です。

Linuxでは、プロセスには単一のコマンドラインはありませんが、代わりに引数の配列があります。
したがって、PowerShellの引数は、すべての引数をマージしてから再分割するのではなく、実行可能ファイルに渡される引数と同じである必要があります。

Windowsでも、現在の動作には一貫性がありません。
引数にスペースが含まれていない場合は、変更されずに渡されます。
引数にスペースが含まれている場合、引用符で囲まれている場合は、 CommandLineToArgvW呼び出しで引数をまとめます。 =>引数はCommandLineToArgvW要件を満たすように変更されます。
ただし、引数に引用符が含まれている場合、それらはエスケープされません。 => CommandLineToArgvWはこれが必要ですが、引数は変更されません。

引数は決して変更しないか、 CommandLineToArgvW要件を満たすように常に変更する必要があると思いますが、半分の場合ではありません。

契約違反について:
ダブルエスケープに関する公式文書が見つからなかったので、これを「バケット2:合理的な灰色の領域」のカテゴリと見なします。これを変更する可能性がありますか、それとも間違っていますか?

@vors引数が変数などの場合、これは非常に煩わしいものです。ネイティブアプリに送信する前に、手動でエスケープする必要があります。
「自動エスケープ」演算子が役立つ場合があります。 ^"a " " -> "a\ " "`のように

@TSlivedeは、動作に一貫性がないことを正しく示していると思います。

引数は決して変更しないか、CommandLineToArgvW要件を満たすように常に変更する必要があると思いますが、半分の場合ではありません。

バケットについてはよくわかりませんが、「明らかに破壊的な変更」バケットでさえも変更される可能性があります。 PowerShellを改善したいのですが、下位互換性は私たちの最優先事項の1つです。 だからそれはそれほど簡単ではありません。
私たちは素晴らしいコミュニティを持っており、コンセンサスを見つけることができると確信しています。

誰かがRFCプロセスを開始したいと思いますか?

PowerShellが引数に引用符を追加する必要がない場合は、.Netの代わりにP / Invokeを使用してプロセスを開始することを検討する価値があります。

@lzybkr私が知る限り、PInvokeは役に立ちません。
そして、これがUNIXAPIとWindowsAPIが異なるところです。

https://msdn.microsoft.com/en-us/library/20y988d2.aspx (スペースを区切り文字として扱います)
https://linux.die.net/man/3/execvp (スペースを区切り文字として扱いません)

私はWindowsの実装を変更することを提案していませんでした。

ここでは、プラットフォーム固有の動作を回避しようと思います。 スクリプトの移植性が損なわれます。
Windowsの動作を壊さない方法で変更することを検討できると思います。 つまり、選好変数を使用します。 そして、さまざまなデフォルトなどを設定できます。

私たちは外部コマンドの呼び出しについて話している-とにかくプラットフォームにいくらか依存している。

WindowsとLinuxでは実行可能ファイルを呼び出す方法が異なるだけなので、プラットフォームに依存することはできないと思います。 Linuxでは、プロセスは引数配列を取得しますが、Windowsでは、プロセスは1つのコマンドライン(1つの文字列)のみを取得します。
(より基本的なものと比較してください
CreateProcess ->コマンドライン(https://msdn.microsoft.com/library/windows/desktop/ms682425)
そして
execve ->コマンド配列(https://linux.die.net/man/2/execve)

引数にスペースが含まれている場合にPowershellがこれらの引用符を追加すると、PowerShellは**ある方法で引数を渡そうとし、 CommandLineToArgvWはコマンドラインを元々PowerShellで指定された引数に分割します。 (このようにして、一般的なcプログラムは、PowerShell関数が$ argsとして取得するのと同じ引数を、そのargv配列で取得します。)
これは、(p / invokeを介して提案されているように)Linuxシステムコールに引数を渡すだけと完全に一致します。

**(引用符をエスケープしないため失敗します)

PS:RFCプロセスを開始するには何が必要ですか?

正確に-PowerShellは、 CommandLineToArgvWが正しいコマンドを生成し、PowerShellが既に解析したものを_後_再解析することを確認しようとします。

これはWindowsの長年の問題点でしたが、その難しさを* nixに持ち込む理由があると思います。

私には、これは実装の詳細のように感じられ、RFCは実際には必要ありません。 Windows PowerShellの動作を変更した場合、RFCが必要になる可能性がありますが、それでも、正しい変更は(おそらく危険な)バグ修正と見なされる可能性があります。

はい、Linuxで直接システムコールを使用するように変更すると、誰もがより幸せに感じるようになると思います。

私はまだそれがウィンドウズでも変更されるべきだと思います、
(おそらく、スクリプトを変更したくない人のために設定変数を追加することによって)
今は間違っているからです-それはバグです。 これが修正された場合、引数が変更されずに次のプロセスに到達するため、Linuxでの直接のsyscallは必要ありません。

しかし、 CommandLineToArgvWと互換性のない方法でコマンドラインを分割する実行可能ファイルがあるので、 @ be5invisの引数の演算子のアイデアが好きです-しかし、自動エスケープ演算子は作成しません(すべての引数のデフォルト)が、代わりに引数をエスケープしないように演算子を追加します(引用符を追加せず、何もエスケープしないでください)。

この問題は、誰かがPowerShellで次のコマンドを試し、PowerShellが機能しなかったのに、CMDが機能しなかったときに、PowerShellを破棄していたときに発生しました。

wmic useraccount where name='username' get sid

PSCX echoargsから、wmic.exeはこれを確認します。

94> echoargs wmic useraccount where name='tso_bldadm' get sid
Arg 0 is <wmic>
Arg 1 is <useraccount>
Arg 2 is <where>
Arg 3 is <name=tso_bldadm>
Arg 4 is <get>
Arg 5 is <sid>

Command line:
"C:\Users\hillr\Documents\WindowsPowerShell\Modules\Pscx\3.2.2\Apps\EchoArgs.exe" wmic useraccount where name=tso_bldadm get sid

では、CMD.exeはプロセスを呼び出したりコマンドラインを形成したりするためにどのAPIを使用しますか? さらに言えば、このコマンドを機能させるために-%は何をしますか?

@rkeithhill CreateProcessW 。 直接電話。 本当に。

これらの2つの状況でPowershellの動作が異なるのはなぜですか? 具体的には、スペースを含む引数を二重引用符で囲んで一貫性がありません。

# Desired argv[1] is 4 characters: A, space, double-quote, B
$ .\echoargs.exe 'A \"B'
<"C:\test\echoargs.exe" "A \"B">
<A "B>
# Correct!

# Desired argv value is 4 characters: A, double-quote, space, B
$ .\echoargs.exe 'A\" B'
<"C:\test\echoargs.exe" A\" B>
<A"> <B>
# Wrong...

韻や理由はないようです。 最初の状況では、引数を二重引用符で囲みますが、2番目の状況ではそうではありません。 スクリプトで手動でラップできるように(またはラップしないように)、二重引用符でラップする場合としない場合を正確に知る必要があります。

.echoargs.exeは、以下をcl echoargs.cコンパイルして作成します。

// echoargs.c
#include <windows.h>
#include <stdio.h>
int wmain(int argc, WCHAR** argv) {
    wprintf(L"<%ls>\n", GetCommandLineW());
    for(int i = 1; i < argc; i++) {
        wprintf(L">%s< ", argv[i]);
    }
    wprintf(L"\n");
}

編集:これが私の$ PSVersionTableです:

Name                           Value
----                           -----
PSVersion                      5.1.15063.296
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.15063.296
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

見積もりに関する動作は複数回変更されたため、次のようなものを使用することをお勧めします。

編集:以下のフォームを更新
古いバージョン:

# call helper

function Run-Native($command) {
    $env:commandlineargumentstring=($args | %{'"'+ ($_ -replace '(\\*)"','$1$1\"' -replace '(\\*)$','$1$1') + '"'}) -join ' ';
    & $command --% %commandlineargumentstring%
}

# some test cases

Run-Native .\echoargs.exe 'A "B' 'A" B'
Run-Native .\echoargs.exe 'A "B'
Run-Native .\echoargs.exe 'A" B'
Run-Native .\echoargs.exe 'A\" B\\" \'

出力:

<"C:\test\echoargs.exe"  "A \"B" "A\" B">
<A "B> <A" B>

<"C:\test\echoargs.exe"  "A \"B">
<A "B>

<"C:\test\echoargs.exe"  "A\" B">
<A" B>

<"C:\test\echoargs.exe"  "A\\\" B\\\\\" \\">
<A\" B\\" \>

最初の-replaceは、引用符の前のバックスラッシュを
2番目の-replaceは、引数の最後で円記号を

これは--% (PS v3以降)を使用します。これは、ネイティブ実行可能ファイルに引用符を渡す唯一の信頼できる方法です。


編集:

Run-Native更新バージョン、現在はInvoke-NativeCommandと呼ばれています(提案どおり)

function Invoke-NativeCommand() {
    $command, [string[]] $argsForExe = $args
    if($argsForExe.Length -eq 0){
        & $command
    } else {
        $env:commandlineargumentstring=($argsForExe | %{
            if($_ -match '^[\w\d\-:/\\=]+$'){
                $_ #don't quote nonempty arguments consisting of only letters, numbers, or one of -:/\=
            } else {
                $_ <# double backslashes in front of quotes and escape quotes with backslash #> `
                    -replace '(\\*)"','$1$1\"' `
                   <# opening quote after xxx= or after /xxx: or at beginning otherwise #> `
                    -replace '^([\w\d]+=(?=.)|[/-][\w\d]+[:=](?=.)|^)','$1"' `
                   <# double backslashes in front of closing quote #> `
                    -replace '(\\*)$','$1$1' `
                   <# add closing quote #> `
                    -replace '$','"'
            }
        }) -join ' ';
        & $command --% %commandlineargumentstring%
    }
}

iepからいくつかのインスピレーションを得て)

  • 単純なスイッチを引用していません
  • 空の引数でも機能します
  • 引数が存在しない場合に機能します
  • 主にmsiexec、cmdkeyなどで動作するはずです...
  • 共通のルールに従うプログラムでも常に機能し
  • 非標準の""をエスケープされた" ""として使用しない-したがって、 .batまたはmsiexec引数に埋め込まれた引用符に対しては機能しません

おかげで、私は--%について知りません

誰もが使用できるRun-Nativeコマンドレットを実装するPowerShellモジュールはありますか? これは、Powershellギャラリーにあるべきもののように聞こえます。 それが十分に良ければ、RFCの基礎になる可能性があります。

「漏れ」は、セキュリティについて心配しているように聞こえます。 ただし、コマンドラインはとにかく子プロセスに表示されることに注意してください。 (例:Windowsではgwmi win32_process |select name,handle,commandline|Format-Table ps -f 、Linuxでは

それでも環境変数を避けたい場合は、invoke-expressionを使用して何かを構築できる可能性があります。

RFCについて:
このようなコマンドレットは必要ないと思いますが、代わりにこれをデフォルトの動作にする必要があります。

https://github.com/PowerShell/PowerShell-RFC/issues/90

PowerShellのデフォルトの動作を修正する必要があることに同意します。 私は、下位互換性の理由で変更されないことを悲観的に想定していました。そのため、モジュールを作成することを提案しました。 ただし、RFCで、設定変数を介して古いエスケープ動作を再度有効にする方法が本当に気に入っています。

適切な意見を述べて、議論を要約しましょう。

  • 下位互換性の問題があることは明らかであるため、古い動作を引き続き利用できるようにする必要があります。

  • @TSlivedeRFC提案は、_未来への道_を称賛に値する一方で、それを説明しています。
    残念ながら、彼の提案はこの記事の執筆時点では_PR_として衰退しており、RFC_draft_としてもまだ受け入れられていません。


_未来_とは、つまり:

  • PowerShellはそれ自体がシェルであり、すぐにcmd.exe関連の手荷物を取り除くことができます。したがって、外部ユーティリティ((通常は)コンソール/ターミナルアプリケーションである実行可能ファイル)の呼び出しに関して重要な考慮事項は次のとおりです。 :

    • 渡す引数は、_PowerShell_の引数モード解析_only_のルールで指定する必要があります。

    • そのプロセスからの_literals_の結果が何であれ、_individual_引数として_そのまま_ターゲット実行可能ファイルに渡される必要があります。

    • 言い換えると、ユーザーとして焦点を当てる必要があるのは、_PowerShell_の解析の結果がどうなるか、そしてその結果がそのまま渡されることを信頼できるようにすることだけです。 -シーンエンコーディング-必要に応じて。


未来の_実装_:

  • _Windows_の場合:

    • _歴史的な_理由により、Windowsは引数をリテラルの配列としてターゲット実行可能ファイルに渡すことを許可していません。 代わりに、_pseudoシェル構文_を使用して_all_引数をエンコードする_single_文字列が必要です。 さらに悪いことに、その単一の文字列を解釈して引数に分割するのは、最終的には個々のターゲット実行可能ファイル次第です。

    • PowerShellが実行できる最善の方法は、コマンドラインを個々の引数に_独自に分割した後、舞台裏で、_予測可能な標準化された方法_でその単一の文字列を形成することです。

    • @TSlivedeRFC提案は、PowerShellが解析を実行するときにWindows C / C ++ランタイムが入力引数をそのまま回復するように疑似シェルコマンドラインを合成することを提案することにより、まさにそれを提案し

      • コマンドラインを解釈するのは最終的に各ターゲット実行可能ファイル次第であることを考えると、これがすべての場合に機能するという_保証_はありませんが、_ほとんどの_既存のユーティリティがこれらの規則を使用するため、ルールが最も賢明な選択です。

      • 唯一の注目すべき例外は_バッチファイル_であり、RFC提案が示唆しているように、特別な扱いを受ける可能性があります。

  • _Unix_プラットフォームの場合:

    • 厳密に言えば、Windows引数の解析を悩ます問題は、発生する必要はありません。新しいプロセスを作成するためのプラットフォームネイティブの呼び出しは、リテラルの配列として引数を受け入れます。PowerShellが_own_解析を実行した後に終了する引数はすべて_as-is_で渡す必要があります。 。
      @lzybkrを引用すると、「その難しさを* nixに持ち込む理由は見当たらない」

    • 残念ながら、.NET Core(CoreFX)の現在の制限により、これらの問題が発生します。CoreFXAPIは、Unixの世界にも、疑似コマンドラインを使用する必要があるため、Windows_引数の無秩序を不必要に強制するためです。 Unix。

    • このCoreFXの問題を作成し修正を依頼しました。

    • それまでの間、CoreFXが上記のC / C ++ルールに基づいて疑似コマンドラインを引数に分割することを考えると、 @ TSlivedeの提案はUnixプラットフォームでも機能するはずです。

https://github.com/PowerShell/PowerShell/issues/4358がこれの複製として閉じられたため、ここにその問題の簡単な要約を示します。

末尾に円記号が付いた外部実行可能ファイルの引数にスペースが含まれている場合、現在は単純に引用符で囲まれています(引数の前後に引用符を追加してください)。 通常のルールに従う実行可能ファイルは、次
@ mklement0コメントから

".\test 2\"前にあるため、 "は、エスケープされた "として解釈され、文字列の残りの部分が-その後欠落している終了にもかかわらず"がの一部として解釈されます。同じ議論。

例:
@akervinenコメントから

PS X:\scratch> .\ps-args-test.exe '.\test 2\'
受け取った引数: .\test 2"

PSReadLineはディレクトリのオートコンプリートに末尾の円記号を追加するため、この問題は非常に頻繁に発生します。

corefxは必要なAPIの作成にオープンであるように思われるため、これを6.1.0に延期します。 6.0.0の場合、#4358を修正できるかどうかを確認します

@TSlivede関数を取得し、名前をInvoke-NativeCommandRunは有効な動詞ではないため)、エイリアス^を追加して、PowerShellGalleryのモジュールとして公開しました。

install-module NativeCommand -scope currentuser
^ ./echoargs 'A "B' 'A" B'

@ SteveL-MSFT:

それの素敵な一時しのぎを持っているが、あまり面倒1のようになります-私たちはCoreFXソリューションを待っている間-明確に定義された公式の引用/引数の解析ルールを実装するために@TSlivedeのRFC案で説明するように自分自身を予め-どのdoesnの難しいとは思えません。

\"問題のみを修正した場合、次のような単純なシナリオでも、引数の受け渡しは根本的に壊れています。

PS> bash -c 'echo "hi there"'
hi    # !! Bash sees the following tokens:  '-c', 'echo hi', 'there'

この時点で、動作がどうあるべきかについて十分な合意があると思うので、完全なRFCプロセスは必要ありませんか?

唯一の未解決の決定は、_Windows_の下位互換性の問題にどのように対処するかです。

@ mklement0 @ SteveL-MSFT
私たちはすでに互換性を破って

唯一の未解決の決定は、Windowsの下位互換性の問題にどのように対処するかです。

ええ、でもそれは難しい部分ですよね?

@ be5invis 「すでに互換性を破った」とはどういう意味ですか?

さらに、CoreFXがレイヤーで修正の危機に瀕している場合は、修正する前にレイヤーに一時的なギャップを作成したくありません。

そして、誰かがスレッドで上で言ったように、これは迷惑ですが、コミュニティでもかなりよく文書化されています。 次の2つのリリースで2回壊す必要があるかどうかはわかりません。

@joeyaiello

#4358の修正は、最後の\ 2倍にすることで問題を回避した人にとって、すでに重大な変更ではありません。 例: "c:\tmp 1\\" ? 言い換えると、この修正への変更を制限すると、2つの重大な変更が保証されます。これは現在、もう1つは将来のCoreFxAPIに切り替えた後です。 そして、完全な一時停止が今実装された場合にも発生する可能性がありますが、この今後の変更について私たちが知っていることを考えると、それはありそうにありません。

逆に、次のような一般的な引用シナリオの場合、Unixでの採用を妨げる可能性があります。
bash -c 'echo "hi there"'が正しく機能しません。

ただし、これを修正することは、はるかに大きな重大な変更であることを認識しています。

@ PowerShell / powershell-committeeはこれについて議論し、少なくとも--%を使用すると、引用符がエスケープされてネイティブコマンドが受け取るという点で、bashと同じ動作をする必要があることに同意しました。 まだ議論の余地があるのは、これが--%を使用しないデフォルトの動作であるかどうかです。

注意:

  • _Windows_で発生するシェルの動作を_emulate_しようとするのではなく、_Unix_で--%を使用する場合は、実際のシェル実行可能ファイルを呼び出す必要があると思います。 Windowsではエミュレートは難しくありませんが、エミュレートする必要のある機能がもっとたくさんあることを考えると、Unixでははるかに難しいでしょう。

  • 次に、実際のシェルを使用すると、どのシェルを使用するかという疑問が生じます。 bashはどこにでもありますが、デフォルトの動作はPOSIXに準拠しておらず、POSIXが存在する必要もありません。 /bin/sh 、POSIXによって宣言されたシェル実行可能ファイル(互換モードで実行されているBash(macOSなど)である可能性がありますが、必ずしもそうする必要はありません(UbuntuではDash))。

間違いなく、 /bin/shもターゲットにする必要がありますが、これは、一部のBash機能(特にブレース拡張、特定の自動変数など)が使用できないことを意味します。


--%

以下の例として、コマンドechoargs --% 'echo "hi there"'を使用します。

引用符がエスケープされてネイティブコマンドが引用符を受け取るという点で、bashと同じ動作です。

_将来、CoreFX APIが拡張されたら_実行する方法は、エスケープをまったく実行せず、代わりに次のようにすることです。

  • 次のようにプロセスを作成します。

    • 実行可能ファイルとして/bin/sh 、(事実上) ProcessStartInfo.FileName割り当てられます。

    • 次の_literal _、_ individual_引数トークンの配列はProcessStartInfo.ArgumentListです:

    • 最初の引数として-c

    • 2番目の引数としてechoargs 'echo "hi there"' -つまり、元のコマンドラインは、 --%が削除されたことを除いて、指定されたとおりに_literally_を使用していました。

実際には、コマンドラインは_as-is_を介してシェル実行可能ファイルに渡され、シェル実行可能ファイルは_its_解析を実行できます。

リテラル引数を渡す配列ベースの方法が現在ない場合、 -cechoargs 'echo "hi there"'を_single_文字列に_エスケープして_結合する必要があることを理解しています。残念ながら_唯一の利益のためです。 CoreFX API_は、実際のプロセスを作成するときに、このステップを_反転_して、単一の文字列をリテラルトークンに分割します。この反転によって、常にリテラルトークンの元のリストが作成されるようにすることは、難しい部分です。

繰り返しますが、ここでエスケープする必要がある唯一の理由は、現在のCoreFXの制限によるものです。
したがって、この制限を処理するには、次の単一のエスケープ文字列をProcessStartInfoインスタンスの.Argumentsプロパティに割り当て、 C ++コマンドライン引数の解析で指定されたとおりにエスケープを実行する必要があります。

  • 実行可能ファイルとして/bin/sh 、(事実上) ProcessStartInfo.FileName割り当てられます。
  • ProcessStartInfo.Argumentsの値としての次の単一のエスケープ文字列:
    -c "echoargs 'echo \"hi there\"'"

##デフォルトの動作

まだ議論の余地があるのは、これが-%を使用しないデフォルトの動作であるかどうかです。

Unixでのデフォルトの動作は大きく異なるはずです。

  • PowerShell自身以外のエスケープの考慮事項は決して機能しないはずです(悲しいことに、それを回避できない_Windows_を除いて;しかし、MS C ++ルールは、_舞台裏で適用される_;それが失敗した場合、 --%はエスケープハッチを提供します)。

  • _PowerShell_が終了する引数は、それ自体の解析後に、次のProcessStartInfo.ArgumentListプロパティを介して_リテラルの配列_として渡す必要があります。

--%なしの例に適用: echoargs 'echo "hi there"'

  • PowerShellは通常の解析を実行し、次の2つの引数で終了します。

    • echoargs
    • echo "hi there" (一重引用符-_PowerShell_への構文機能のみがあり、削除されました)
  • 次に、 ProcessStartInfoが次のように入力され、次のCoreFX拡張機能が配置されます。

    • (有効な) .FileNameプロパティ値としてのechoargs
    • _Literal_ echo "hi there" .ArgumentListによって公開されたCollection<string>インスタンスに追加する唯一の要素としてのecho "hi there" .ArgumentList

繰り返しになりますが、 .ArgumentListがない場合、これはオプションではありませんが、暫定的には、上記と同じMS C ++準拠の補助エスケープを使用できます。

@ SteveL-MSFT
Unixで停止解析記号(-%)を機能させる(#3733)ですでに述べたように、 --%動作を変更しないことを強くお勧めします。

/bin/sh -c特別な機能が必要な場合は、別の記号使用して、 --%そのままにしてください。

@TSlivede

_If_何か--% -_like_がUnixに実装されており、ネイティブのグロブと一般的にコマンドラインに精通したUnixの群集を使用すると、その必要性が少ないと感じます。次に、 --$などの_異なるシンボル_を選択します。

異なるシンボルは、移植性のない_プラットフォーム固有の_動作が呼び出されていることを視覚的に目立つように思い出させる役割も果たします。

その葉、それは全体の来るときPowerShellは何をすべきか疑問--% Unix上と--$ Windows上で。

--%をそのままにしておいて大丈夫です。 / bin / shを呼び出す--$ようなものを導入すると、Windowsのcmd.exeがこれを解決する良い方法かもしれないと思います。

これらの動作のコマンドレットを作成する機会はありませんか?

@iSazonovInvoke-Nativeようなものを提案していますか? 私がそのファンかどうかはわかりません。

はい、 Start-Native
冗談として:-)、PowerShellのコマンドレットが好きではありませんか?

Build.psm1には、 https: //mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right/へのリンクを含むStart-NativeExecutionがあります。

@ SteveL-MSFT

-%をそのままにしておいて大丈夫です。

--%は、_Windows_と同じように動作し続ける必要があることに私たちは皆同意していると思います。

対照的に、_Unix_では、ここで説明しようとしたように、この動作は意味がありません。

  • 一重引用符が正しく処理されていません
  • 環境変数を参照する唯一の方法は、_cmd.exe-style _( %var% )です。
  • グロブや単語分割などの重要なネイティブ機能は機能しません。

--%を導入する主な動機は、私が正しく理解していれば、_既存のcmd.exeコマンドラインの再利用_をそのまま有効にすることでした。

  • そのため、 --%は、現在の動作ではUnixでは役に立ちません。
  • Unixで_analogous_機能が必要な場合は、上記で提案したように、おそらく別の記号を使用して、 /bin/sh -cベースにする必要があります。

Windowsではcmd /cベースの機能は必要ないと思います。 --%は、おそらく_十分_な方法で_ほとんど_カバーされているからです。
@TSlivedeは、すべてのシェル機能がエミュレートされているわけではないことを指摘していますが、実際には問題ではないようです(たとえば、 %envVar:old=new%などの変数値の置換はサポートされていません。 ^はエスケープ文字ではなく、 --%の使用は_single_コマンドに制限されています- cmd.exeのリダイレクトおよび制御演算子の使用はありません;とはいえ、 --%はないと思います

そのため、 --$ようなもの(実装されている場合)は、Unixの_counterpart_から--%ます。

いずれにせよ、少なくともabout_Parsingヘルプトピック、いくつかの場合を除いて、

@iSazonov特定のシナリオを処理するためにStart-Nativeを使用することは理にかなっているかもしれませんが、ネイティブexeの使用がより自然で予測可能になるように、PowerShellを改善する必要があります。

@ PowerShell / powershell-committeeはこれを確認し、 --%は、関連するプラットフォームと同じように引数を処理することを意味することに同意します。つまり、WindowsとLinuxでは動作が異なりますが、Windows内では一貫性があり、Linux内では一貫性があります。 新しい印章を導入することは、ユーザーにとってより混乱するでしょう。 これを有効にする方法については、エンジニアに実装を任せます。

クロスプラットフォームのスクリプトは、この動作の違いを認識している必要がありますが、ユーザーがこれにぶつかる可能性は低いようです。 クロスプラットフォームの使用が増えるために必要があるというユーザーフィードバックがある場合は、新しい印章の導入を再検討できます。

ripgrepを使用した場合、これらのネイティブとコマンドレットの文字列引数の解析の違いに2度目に噛まれました。

これがPowerShell呼び出しの結果です(echo.exeは「C:\ ProgramFiles \ Git \ usrbin \ echo.exe」からのものです)

今、私はこの"癖に気をつけるべきだと知っています:

> echo.exe '"test'
test

しかし、この癖は私を超えています...

echo.exe '^\+.+;'
^\+.+;
echo.exe '^\+.*;'
^+.*;

2番目のケースでは、 \をネイティブコマンドに渡すために2倍の\を配置する必要があります。最初のケースでは、それを行う必要はありません😑

これは、この動作を変更するための重大な変更であるため、cmletsとネイティブコマンドの間に違いはないことを理解しています。 ただし、このような癖は、PowerShellをデフォルトのシェルとして使用することを妨げるものだと思います。

@mpawelski git 2.20.1.vfs.1.1.102.gdb3f8aeを使用してWindowsボックスでこれを試しましたが、6.2-RC.1では再現されません。 数回実行すると、一貫して^\+.+;エコーされます

@ SteveL-MSFTは、私は@mpawelskiが誤って二度同じコマンドを指定したと思います。 たとえば、 '^\"+.*;'を渡すと、問題が発生します。 \"部分に注意してください。--合理的な-一重引用符で囲まれた文字列の内容がそのまま渡されることを期待しています。 、外部ターゲットプログラムが^\"+.*;を引数の値として認識するようにします。

# Note how the "\" char. is eaten.
PS> bash -c 'printf %s "$1"' - '^\"+.*;'
^"+.*; 

#"# Running the very same command from Bash does NOT exhibit the problem:
$ bash -c 'printf %s "$1"' - '^\"+.*;'
^\"+.*; 

-%は、私が正しく理解していれば、既存のcmd.exeコマンドラインをそのまま再利用できるようにすることでした。

それだけではありません。 --%が導入されたのは、多くのWindowsコマンドラインユーティリティが、ユーティリティの呼び出しを完全に妨害するPowerShellによって解釈される引数を受け取るためです。 これは、お気に入りのネイティブユーティリティを簡単に使用できなくなった場合に、すぐに酸っぱくなる可能性があります。 この問題のために正しく機能しないSOや他のREネイティブexeコマンドに関する質問に答えるたびに四半期があれば、おそらく家族をQobaでの夕食に連れて行くことができます。 :-)たとえば、 tf.exeは、ワークスペースの指定の一部として;を許可します。 Gitでは{}@~1などが許可されます。

--%が追加され、PowerShellにコマンドラインの残りの部分を解析しないように指示しました。ネイティブexeに「そのまま」送信するだけです。 1回の摩擦で、cmd envvar構文を使用して変数を許可します。 それは少し醜いですが、人間は、それは本当に、Windows上でまだ本当に便利です。

これをコマンドレットにすると、それがどのように機能するかわかりません。 --%は、EOLまで解析をダムダウンするようにパーサーに通知します。

率直に言って、PowerShellと特にこの機能の長年のユーザーとして、他のプラットフォームで同じ演算子を使用することは、単に意味するために理にかなっています-EOLまで解析を控えめにしてください。 何らかの形の変数置換をどのように許可するかという問題があります。 少し醜い感じがしますが、macOS / Linuxでは%envvar%を取り、対応するenv変数の値に置き換えることができます。 そうすれば、プラットフォーム間で移植できる可能性があります。

重要なのは、これを行わないと、条件付きコードになってしまうということです。これは、私がポータブルと呼んでいるものとは異なります。

$env:Index = 1
if ($IsWindows) {
    git show --% @~%Index%
}
else {
    git show --$ @~$Index
}

私はすべてのプラットフォームでこの作業を好みます:

$env:Index = 1
git show --% @~%Index%

互換性があるため、Windowsでの動作はそのままにする必要があります。

@rkeithhill

それだけではありません。

消去法により、Windowsの世界でPowerShellより前のコマンドラインがcmd.exe用に作成されました。これは、次のように、

  • --%cmd.exeスタイルの%...%環境変数参照を展開します( %USERNAME% (シェルや特別なロジックが含まれていない場合、そのようなトークンは渡されます) _verbatim_)

  • PowerShellの口から直接(必要に応じて、強調を追加):

Webには、Cmd.exe用に作成されたコマンドラインがたくさんあります。 これらのコマンド行はPowerShellで十分に機能しますが、セミコロン(;)、ドル記号($)、中括弧などの特定の文字が含まれている場合は、いくつかの変更を加え、おそらく引用符を追加する必要があります。 これが多くの小さな頭痛の種の原因であるように思われました。

このシナリオに対処するために、コマンドラインの解析を「エスケープ」する新しい方法を追加しました。 魔法のパラメータ-%を使用すると、コマンドラインの通常の解析が停止され、はるかに単純なものに切り替わります。 引用符は一致しません。 セミコロンだけではありません。 PowerShell変数は展開しません。 Cmd.exe構文(%TEMP%など)を使用する場合は、環境変数を展開します。 それ以外は、行の終わり(またはパイプの場合はパイプ)までの引数はそのまま渡されます。 次に例を示します。

このアプローチは、 (上から要約すると)

  • *.txtなどの引用符で囲まれていない引数のグロブは機能しません。

  • Unixの世界の従来の(POSIXのような)シェルは、環境変数を参照するために%...%を使用しません。 彼らは$...構文を期待しています。

  • Unixの世界には(幸いなことに)_raw_コマンドラインはありません。外部実行可能ファイルには_literals_の_array_を渡す必要があるため、コマンドラインを最初に引数に解析する必要があるのはPowerShellまたはCoreFxです。

  • Unixの世界の従来の(POSIXのような)シェルは'...' (一重引用符で囲まれた)文字列を受け入れますが、 --%はそれを認識しません-#10831を参照してください。

しかし、Windowsの世界でも、 --%は、明白ではない厳しい制限があります。

  • --%を使用する場合、コマンドラインでPowerShell変数を直接参照することはできません。 唯一の(面倒な)回避策は、一時的に_environment_変数を定義することです。これは、 %...%参照する必要があります。
  • コマンドラインを(...)で囲むことはできません-閉じる)は、コマンドラインのリテラル部分として解釈されるためです。
  • ;と別のステートメントでコマンドラインを追跡することはできません- ;はコマンドラインのリテラル部分として解釈されるためです。
  • 単一行のスクリプトブロック内で--%使用することはできません-終了する}は、コマンドラインのリテラル部分として解釈されるためです。
  • リダイレクトはコマンドラインのリテラル部分として扱われるため使用できませんが、 cmd --% /c ... > fileを使用して、 cmd.exeにリダイレクトを処理させることができます。
  • 行継続文字(PowerShell( ` )もcmd.exe( ^ ))も使用できません。これらは_literals_として扱われます。

    • --%は、(最大で)行の終わりまでしか解析しません。


幸いなことに、私たちはすでにクロスプラットフォームの構文を持っています:_PowerShell独自の構文_。

はい、それを使用するには、_PowerShell_がメタ文字と見なすものを知る必要があります。これはcmd.exeとBashなどのPOSIXのようなシェルの両方の_スーパーセット_ですが、それはよりリッチなプラットフォームに支払う代償です-不可知論者のコマンドラインエクスペリエンス。

それでは、PowerShellが"文字の引用を非常にうまく処理できないのは、どれほど残念なことです。これは、この問題の主題であり、このドキュメントの問題に要約され

@rkeithhill 、私は少し接線を始めました。 それを閉じてみましょう:

  • --%は、PowerShell Core6.2.0の時点で実際にはすでに実装されています。 例えば、
    /bin/echo --% %HOME%は、環境変数HOMEの値を出力します。 対照的に、
    /bin/ls --% *.txt *.txtはリテラルとして渡されるため、 /bin/ls --% *.txtは期待どおりに機能しません。

  • 最終的に、 --%を使用しない場合、ユーザーが_最終的に_舞台裏で_構築されたコマンドライン/引数配列がどのように見えるかを診断できるようにする必要があります(これにより、由緒ある#1761に戻ります)。

    • あなたの役に立つechoArgs.exeはまさにそれをします、そしてリンクされた問題であなたはそのような機能がPowerShell自体の一部であることを賢明に要求しました。
    • @ SteveL-MSFTは、結果のコマンドラインをエラーレコードに含めることを検討しました。

最後に、私の前の議論を-本質的に移植可能なものとしてPowerShell独自の構文を使用して-あなたの例に適用するには:

# Works cross-platform, uses PowerShell syntax 
# Note: No need for an aux. *environment* variable (which should be cleaned up afterward)
$Index = 1
git show "@~$Index"

# Alternative, quoting just the '@'
git show `@~$Index

はい、トークンの頭文字@はメタ文字であるため、引用する必要があることを知っておく必要がありますが、PowerShellが広く使用されるようになると、そのような要件の認識もさらに広まるはずです。

参考までに、この問題はWindowsとUnixライクでほぼ同じであるはずです。 Unix SDProcessのCoreFx実装には、 ParseArgumentsIntoListと呼ばれるものがあります。これ_setargvスイッチなしで(引用符で囲まれた文書化されていない""して) CommandLineToArgvWほぼ正確に実装します→ "機能)。 現在の形ではWindowsと同じように壊れているので、Unixはこれで追加の問題になるべきではありません。

_setargvは、結局のところ、すべてのプログラムが使用するものではありません。CRTバージョン間の動作の変更によって多少影響を受けるため、検討する価値はないでしょう。 私たちができる最善のことは、すべてを二重引用符で囲み、いくつかの素敵なバックスラッシュを追加することです。それだけです。

引数が正しく解析されていない別の例:

az "myargs&b"

この場合、azはmyargsを取得し、 bが新しいコマンドとして実行されようとします。

回避策は次のとおりです。az-% "myargs&b"

@ SteveL-MSFT、必要な機能であるコレクションベースのProcessStartInfo.ArgumentListプロパティが.NET Core 2.1以降で利用可能になっている場合、 Waiting - DotNetCoreラベルを削除できます。

@TSlivedeは新しい方法を認識しており、それを使用する予定ですが、関連するRFC、 https://github.com/PowerShell/PowerShell-RFC/pull/90は、残念ながら苦しんでいます。

そこで_実装_の議論を続けることをお勧めします。

ただし、RFCの議論では、 @ joeyaielloが変更を実験的な機能にすることについて話しますが、既存のコードを大幅に壊さずに引用動作を修正できないことがますます明らかになっています。

_回避_しなければならなかった人:

  • 空の文字列引数を渡すことができない( foo.exe ""現在_no_引数を渡します)
  • 埋め込まれた二重引用符の自動エスケープがないため、予期しない効果的な二重引用符の削除( foo.exe '{ "foo": "bar" }'が不適切にエスケープされた"{ "foo": "bar" }"として渡される)
  • _全体として_二重引用符で囲まれた特定の引数を受け入れないmsiexecなどの特定のCLIの癖( foo.exe foo="bar none""foo=bar none"として渡されます)。
    注:ここで責任があるのはmsiexecであり、提案された変更が適用されると、必要なfoo="bar none"形式の引用を渡すには、 --%必要になります。

提案された変更が適用されると回避策が_中断_されるため、問題が発生します。

したがって、追加の質問は次のとおりです。

  • 少なくとも_opt-in_機能として正しい動作を利用できるようにするにはどうすればよいですか?

    • Start-Processで引用が壊れている場合、少なくとも正しい動作のために新しいパラメータを導入するオプションがありますが、直接呼び出すことは明らかにオプションではありません(そのような別の「魔法のシンボル」を導入しない限り) --% )。
  • このようなメカニズムの基本的な問題は、通常は設定変数によるものですが、 usingステートメントによるものも増えていますが、PowerShellの動的スコープです。 つまり、オプトインされた動作は、デフォルトで、オプトインされたコードから_from_と呼ばれるコードにも適用されますが、これには問題があります。

    • おそらく、機能の_lexical_スコープを一般的に導入するときです。これは、@ lzybkrによって作成された-同様に苦しい-字句厳密モードRFCでの字句スコープのusing strict提案の一般化です。

    • 字句スコープのusing preference ProperArgumentQuotingようなもの? (名前は明らかに交渉可能ですが、私はそれを思い付くのに苦労しています)。

これらすべての警告と、これは直接呼び出しの問題でもあり、新しいパラメーターを単純に追加することはできないことを考えると、私は古い動作を壊すことに固く賛成です。

はい、それはおそらく多くを壊すでしょう。 しかし、実際には、そもそも完全に壊れていたからです。 _実際に機能する_機能を持つよりも、壊れた動作の回避策の膨大な山に相当するものを維持することを優先することは特に実現可能ではないと思います。

新しいメジャーバージョンとして、v7は、この状況をかなりの期間適切に修正できる唯一のチャンスであると思います。その機会を利用する必要があります。 ユーザーに気づかせれば、全体的に移行がうまく受け入れられないと思います。

重大な変更が避けられないと感じた場合は、インタラクティブセッションでの印刷が簡単であり、すべてのプラットフォームでより適切に機能するスクリプトバージョンを作成できることを考慮して、理想的なソリューションを設計する必要があります。

同意しました、 @ vexx32 :デフォルトでのみであっても、既存の動作を維持することは、永続的な問題点のままです。 ユーザーは、オプトインの必要性を期待していません。一度オプトインすると、時々忘れたり、毎回適用する必要性に憤慨したりする可能性があります。

外部プログラムに引数を確実に渡さないシェルは、そのコアマンデートの1つに失敗しています。

あなたは確かに重大な変更を行うことに_my_票を持っていますが、特にv7が長期のWinPSユーザーがPSCoreに移行できると宣伝されているため、他の人が違った気持ちになるのではないかと心配しています。


@iSazonovhttps

その精神を要約するには:

PowerShellは、シェルとして、_its_ルールに従って引数を解析し、結果の展開された引数値_verbatim_をターゲットプログラムに渡す必要があります。ユーザーは、PowerShellがそれをどのように実現するかを考える必要はありません。 彼らが心配しなければならないのは、_PowerShell_構文を正しくすることだけです。

  • Unixライクなプラットフォームでは、展開された引数値の_array_をターゲットプログラムにそのまま渡すことができるので、 ProcessStartInfo.ArgumentListはこれを完全に実装する方法を提供します。これは、引数の受け渡しが(賢明に)機能するためです。この世界で。

  • Windowsでは、 Windowsのコマンドライン解析必要がありますが、シェルとしては、RFCが「可能な限り舞台裏で機能させる」ことをお勧めします。説明-残念ながら、 ProcessStartInfo.ArgumentListだけを使用するしわaz[.cmd]示されているように、CLIエントリポイントとして_batchファイル_が広まっているため)上記の--%ます。

おそらくPSSAは、変更される引数形式を使用することをユーザーに警告することで、重大な変更を軽減するのに役立ちます。

この画期的な変更を他の人たちと一緒に進めるには、オプション機能のようなものを採用することを検討する必要があると思い

既存の行動を維持することに本当に価値はありますか? 下位互換性以外は、つまり。

古いコードによっては時々必要になる可能性があるため、壊れた実装を保持するためだけに、このために2つのコードパスを維持する価値はないと思います。 folxがたまにコードを更新することを期待するのは不合理ではないと思います。 😅

PS7がWinPSの代わりになると予想される場合は、二重にそうです。 v7の動作を維持するために私が見ることができる唯一の理由は、5.1と7の両方でコマンドを実行するために同じスクリプトを使用することが予想される場合です。これは、PS7が5.1の優れた代替品である場合、(願わくば)非常にまれなケースです。

それでも、ユーザーが両方を説明することはそれほど難しくありません。 実際の言語構文を変更しないのであれば、次のようなことを行うのは非常に簡単です。

if ($PSVersionTable.PSVersion.Major -lt 7) {
    # use old form
}
else {
    # use new form
}

ユーザーに違いを認識させれば、これまでPSで奇妙なネイティブ実行可能ファイルを処理することの苦痛から解放されることを歓迎すると思います。 😄

@TylerLeonhardtがオプション機能の説明で述べたように、実装とは、それぞれを維持およびテストする必要がある_複数の_個別の実装を維持し、さらにオプション機能フレームワークを維持およびテストすることを意味します。 これには本当に価値がないようです、tbh。

@ vexx32の下位互換性はここでは大きな問題です。 ネイティブ実行可能ファイルへの引数の問題はこれだけではありません。 それらすべてをバケット化し、それらすべてを1つの「オプション機能」にしたいのです。 たとえば、 https://github.com/PowerShell/PowerShell/issues/1761およびhttps://github.com/PowerShell/PowerShell/issues/10675です。 ネイティブコマンドとの相互運用性を「修正」することは、vNextで解決したいことです。 したがって、誰かがこのカテゴリの既存または新しい問題を見つけた場合は、cc meと私が適切にタグ付けします(またはトリアージ許可がある場合は、他の問題と同じようにタグ付けします)。

オプション機能はモジュールです:-) Engineにオプション機能があると、サポート、特にWindowsサポートにとって大きな頭痛の種になります。 内部の依存関係を減らし、内部APIをパブリックに置き換えることでEngineをモジュール化できます。その後、Engineにオプション機能を簡単に実装できます。

@iSazonov私のチームがvNextで検討することの1つは、エンジンをよりモジュール化することです:)

ここでエンドユーザーに推奨される解決策は何ですか?

これは工夫されていますが、.NETFramework自体から正しい* ArgumentList処理を取得するための最も簡単な方法です。

Add-Type -AssemblyName "System"
function startProcess([string] $FileName, [string[]] $ArgumentList) {
    $proc = ([System.Diagnostics.Process]::new())
    $proc.StartInfo.UseShellExecute = $false
    $proc.StartInfo.FileName = $FileName
    $proc.StartInfo.CreateNoWindow = $true
    foreach ($a in $ArgumentList) { $proc.StartInfo.ArgumentList.Add($a) }
    $proc.Start()
    return $proc
}

startProcess -FileName 'C:\Program Files\nodejs\node.exe' -ArgumentList '-e','console.log(process.argv.join(''\n''))','--','abc" \" messyString'

もちろん、位置パラメータといくつかのget-commandトリックを使用することで、ここでの使用を減らすことができます。

*免責事項:Windowsでコマンドラインを解析する単一の正しい方法はありません。 「正しい」とは、UNIXでのArgumentList処理、 main (string[] args)処理、および外部スポーン呼び出しのために、すべてのプラットフォームで.NETによって実装されたMSVCRTスタイルのコマンドラインfrom / toargv変換を意味します。 このサンプルは現状のまま提供されており、一般的な相互運用性は保証されていません。 提案されたNodeJSchild_processドキュメントの「windowsコマンドライン」セクションも参照してください。

@ Artoria2e5まさにそれが私が

2N backslashes + " ==> N backslashes and begin/end quote
2N+1 backslashes + " ==> N backslashes + literal " 
N backslashes ==> N backslashes

その結果、引数をエスケープする次のロジックを考え出し、プロセス実行のために引数を二重引用符"にラップします。
https://github.com/choovick/ps-invoke-externalcommand/blob/master/ExternalCommand/ExternalCommand.psm1#L244

また、外部実行可能ファイルの実行中にSTDOUTとSTDERRをリアルタイムで取得するのは難しい場合があるため、このパッケージを作成しました

https://github.com/choovick/ps-invoke-externalcommand

私はWindows、Linux、Macで頻繁に使用しており、これまでのところ問題なく使用しており、改行やその他の特殊文字を含む引数を渡すことができます。

GitHub
GitHubでアカウントを作成して、choovick / ps-invoke-externalcommandの開発に貢献します。
GitHub
GitHubでアカウントを作成して、choovick / ps-invoke-externalcommandの開発に貢献します。

@choovick全体のstdioリダイレクトは素晴らしいです!

ただし、エスケープの部分については少し同意しません。これは、ArgumentListと呼ばれるエスケープ部分がすでに存在するためです。 これは比較的最近の(?)追加であり、MSがSDProcessStartInfoのString、String []初期化子を配置するのを忘れたため、がっかりすることは理解しています。 (これらの….NETインターフェースの提案のための場所はありますか?)


チットチャット

そのNodeJSの例からの私のエスケープ関数はあなたのものとは少し異なります:それは引用符のために文書化されていない(しかし.NETコアとMSVCRTにあります) ""エスケープを使用します。 そうすることで、バックスラッシュのピッキング作業が簡単になります。 これを行ったのは、主に、 \"が文字列の残りの部分を引用解除してはならないことを理解していないすべての強力なcmdに使用されたためです。 \^"に苦労する代わりに、私は、時間の初めから秘密裏に使用されてきたものを使ったほうがよいと考えました。

@ Artoria2e5残念ながら、私が得ている例を使用すると、WindowsのPowerShell5.1ではArgumentListを使用できません。

`` `null値の式でメソッドを呼び出すことはできません。
C:Users \ yser \ dev \ test.ps1:7 char:37で

  • ... oreach($ ArgumentListの$ a){$ proc.StartInfo.ArgumentList.Add($ a)}
  • ~~~~ ~~~~

    • CategoryInfo:InvalidOperation :( :) []、RuntimeException

    • FullyQualifiedErrorId:InvokeMethodOnNull

      `` `

カスタム引数エスケープロジック以降...

チットチャット

NodeJSの `\ ^" `に関しては、数年前にそれをしなければならなかったと思います:)そしてそれが機能したと思います

System.Diagnostics.Processは、外部実行可能ファイルを実行するための唯一の信頼できる方法です。

問題は、PowerShellの出力ストリームとの統合が得られず、パイプラインでストリーミング動作が得られないことです。

PowerShellに呼び出しを実行させたい場合に必要な回避策の概要は、次のとおりです(これは間違いなく望ましいことです)

  • _embedded_ "文字を使用して引数を渡す必要がある場合は、可能であればWindowsで_double_するか、 \エスケープします。

    • Windowsでは、_batch files_を呼び出すときに、ターゲットプログラムが""をエスケープされた"として理解していることがわかっている場合は、 $arg -replace '"', '""'使用します。

      • Windowsでは""が推奨されます(Windows PowerShellの問題を回避し、Node.jsやAzureなどのバッチファイルを_stubs_として使用するCLIで機能します)が、すべての実行可能ファイルがそれをサポートしているわけではありません(特に、RubyとPerl)。
    • それ以外の場合(常にUnixの場合)、 $arg -replace '"', '\"'使用します

    • 注:_Windows PowerShell_では、値に_spaces_も含まれている場合、これは常に正しく機能するとは限りません。これは、PowerShell Coreとは異なり、値にリテラル\"すると、状況に応じて二重引用符を囲むトリガーが発生しないためです。 たとえば、 '3\" of snow'ブレークを渡す。

    • さらに、上記をエスケープする前に、 "直前の\インスタンスをリテラルとして扱う場合は、それらを2倍にする必要があります。

      • $arg = $arg -replace '(\\+)"', '$1$1"'
  • _empty_引数を渡す必要がある場合は、 '""'渡します。

    • '' -eq $arg ? '""' : $arg (WinPSの代替: ($arg, '""')['' -eq $arg]
  • Windows PowerShellのみ、PS Core(問題が修正されている)ではこれを行わないでください。

    • 引数にスペースが含まれ、(1つ以上の) \で終わる場合は、末尾の\インスタンスを2倍にします。

      • if ($arg -match ' .*?(\\+)$') { $arg = $arg + $Matches[1] }
  • cmd /バッチファイルが、スペースを含まない(したがって、PowerShellによる自動二重引用符をトリガーしない)引数を使用して呼び出されているが、 &|<>^,;いずれかが含まれている場合(例: a&b )、_ embedded enclosed double-quoting_を使用して、PowerShellが二重引用符で囲まれたトークンを渡し、 cmd /バッチファイル呼び出しを中断しないようにします。

    • $arg = '"' + $arg + '"'
  • msiexec.exeなどの動作の悪い実行可能ファイルを処理する必要がある場合は、引数を一重引用符で囲んでください。

    • 'foo="bar none"'

前述のように、根本的な問題が修正されると、


以下は、単純な(高度ではない)関数iep (「外部プログラムの呼び出し」用)です。

  • ターゲットプログラムに応じて、 msiexec自動特殊ケーシングや、 \"よりも""エスケープを優先するなど、上記のすべてのエスケープを実行します。

    • アイデアは、_PowerShell_の文字列構文のみに焦点を当てることで任意の引数を渡すことができ、PowerShellが認識する逐語的な値がターゲットプログラムにも表示されるように、関数に依存して必要なエスケープを実行することです。

    • PowerShell _Core_では、これはかなり堅牢に機能するはずです。 Windows PowerShellでは、(上記で説明したように) \"エスケープを使用する必要がある場合に、二重引用符が埋め込まれたエッジケースがまだあります。

  • シェルコマンド呼び出し構文を保持します。

    • コマンドラインの前にiep を追加するだけです。

  • 直接呼び出しが行うように、それは:

    • PowerShellのストリームと統合します

    • パイプラインを介して出力を1行ずつ送信します

    • 外部プログラムの終了コードに基づいて$LASTEXITCODEを設定します。 ただし、 $?は信頼できません。

注:この関数は、できるだけ目立たないようにするため、意図的に最小限に抑えられています(パラメーター宣言、コマンドラインヘルプ、短い(不規則な)名前)。コマンドラインの先頭にiepを追加するだけです。動作するはずです。

EchoArgs.exeを使用した呼び出しの例choco install echoargs -yした_elevated_セッションからChocolatey経由でインストール可能):

PS> iep echoargs '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
Arg 0 is <>
Arg 1 is <a&b>
Arg 2 is <3" of snow>
Arg 3 is <Nat "King" Cole>
Arg 4 is <c:\temp 1\>
Arg 5 is <a \" b>

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" "" a&b "3\" of snow" "Nat \"King\" Cole" "c:\temp 1\\" "a \\\" b"

上記は、PowerShellCoreの出力を示しています。 空の引数を含め、PowerShellによって逐語的に見られるように、すべての引数がどのように正しく渡されたかに注意してください。

Windows PowerShellでは、不明な実行可能ファイルを呼び出すために\"エスケープが使用されるため、 3" of snow引数は正しく渡されません(前述のとおり)。

バッチファイルが引数を正しく渡すことを確認するために、 echoargs.exeラッパーとしてechoargs.cmdを作成できます。

'@echoargs.exe %*' | Set-Content echoargs.cmd

iep .\echoargs.cmd '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'として呼び出す

バッチファイルが呼び出されるようになったため、 "" -escapingが採用され、Windows PowerShellから呼び出すときの3" of snow問題が修正されました。

この関数は、Unixライクなプラットフォームでも同様に機能します。これは、 echoargsという名前のshシェルスクリプトを作成することで確認できます。

@'
#!/bin/sh
i=0; for a; do printf '%s\n' "\$$((i+=1))=[$a]"; done
'@ > echoargs; chmod a+x echoargs

iep ./echoargs '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'として呼び出す


重要この関数のより完全なバージョンは、モジュールNative公開したばかりで、 ieI nvoke(external) E xecutable)として公開されています。これを使用することをお勧めします。代わりに。 モジュールをインストールする
Install-Module Native -Scope CurrentUser
このモジュールには、#13068で説明されているユースケースに対応するinsInvoke-NativeShell )コマンドも含まれています。https: //github.com/PowerShell/PowerShell/issues/13068#issuecomment-671572939を参照して

関数iepのソースコード(代わりにモジュールNative使用してください-上記を参照):

function iep {

  Set-StrictMode -Version 1
  if (-not (Test-Path Variable:IsCoreClr)) { $IsCoreCLR = $false }
  if (-not (Test-Path Variable:IsWindows)) { $IsWindows = $env:OS -eq 'Windows_NT' }

  # Split into executable name/path and arguments.
  $exe, [string[]] $argsForExe = $args

  # Resolve to the underlying command (if it's an alias) and ensure that an external executable was specified.
  $app = Get-Command -ErrorAction Stop $exe
  if ($app.ResolvedCommand) { $app = $app.ResolvedCommand }
  if ($app.CommandType -ne 'Application') { Throw "Not an external program, non-PS script, or batch file: $exe" }

  if ($argsForExe.Count -eq 0) {
    # Argument-less invocation
    & $exe
  }
  else {
    # Invocation with arguments: escape them properly to pass them through as literals.
    # Decide whether to escape embedded double quotes as \" or as "", based on the target executable.
    # * On Unix-like platforms, we always use \"
    # * On Windows, we use "" where we know it's safe to do. cmd.exe / batch files require "", and Microsoft compiler-generated executables do too, often in addition to supporting \",
    #   notably including Python and Node.js
    #   However, notable interpreters that support \" ONLY are Ruby and Perl (as well as PowerShell's own CLI, but it's better to call that with a script block from within PowerShell).
    #   Targeting a batch file triggers "" escaping, but in the case of stub batch files that simply relay to a different executable, that could still break
    #   if the ultimate target executable only supports \" 
    $useDoubledDoubleQuotes = $IsWindows -and ($app.Source -match '[/\\]?(?<exe>cmd|msiexec)(?:\.exe)?$' -or $app.Source -match '\.(?<ext>cmd|bat|py|pyw)$')
    $doubleQuoteEscapeSequence = ('\"', '""')[$useDoubledDoubleQuotes]
    $isMsiExec = $useDoubledDoubleQuotes -and $Matches['exe'] -eq 'msiexec'
    $isCmd = $useDoubledDoubleQuotes -and ($Matches['exe'] -eq 'cmd' -or $Matches['ext'] -in 'cmd', 'bat')
    $escapedArgs = foreach ($arg in $argsForExe) {
      if ('' -eq $arg) { '""'; continue } # Empty arguments must be passed as `'""'`(!), otherwise they are omitted.
      $hasDoubleQuotes = $arg.Contains('"')
      $hasSpaces = $arg.Contains(' ')
      if ($hasDoubleQuotes) {
        # First, always double any preexisting `\` instances before embedded `"` chars. 
        # so that `\"` isn't interpreted as an escaped `"`.
        $arg = $arg -replace '(\\+)"', '$1$1"'
        # Then, escape the embedded `"` chars. either as `\"` or as `""`.
        # If \" escaping is used:
        # * In PS Core, use of `\"` is safe, because its use triggers enclosing double-quoting (if spaces are also present).
        # * !! In WinPS, sadly, that isn't true, so something like `'foo="bar none"'` results in `foo=\"bar none\"` -
        #   !! which - due to the lack of enclosing "..." - is seen as *2* arguments by the target app, `foo="bar` and `none"`.
        #   !! Similarly, '3" of snow' would result in `3\" of snow`, which the target app receives as *3* arguments, `3"`, `of`, and `snow`.
        #   !! Even manually enclosing the value in *embedded* " doesn't help, because that then triggers *additional* double-quoting.
        $arg = $arg -replace '"', $doubleQuoteEscapeSequence
    }
      elseif ($isMsiExec -and $arg -match '^(\w+)=(.* .*)$') { 
        # An msiexec argument originally passed in the form `PROP="value with spaces"`, which PowerShell turned into `PROP=value with spaces`
        # This would be passed as `"PROP=value with spaces"`, which msiexec, sady, doesn't recognize (`PROP=valueWithoutSpaces` works fine, however).
        # We reconstruct the form `PROP="value with spaces"`, which both WinPS And PS Core pass through as-is.
        $arg = '{0}="{1}"' -f $Matches[1], $Matches[2]
      }
      # As a courtesy, enclose tokens that PowerShell would pass unquoted in "...", 
      # if they contain cmd.exe metachars. that would break calls to cmd.exe / batch files.
      $manuallyDoubleQuoteForCmd = $isCmd -and -not $hasSpaces -and $arg -match '[&|<>^,;]'
      # In WinPS, double trailing `\` instances in arguments that have spaces and will therefore be "..."-enclosed,
      # so that `\"` isn't mistaken for an escaped `"` - in PS Core, this escaping happens automatically.
      if (-not $IsCoreCLR -and ($hasSpaces -or $manuallyDoubleQuoteForCmd) -and $arg -match '\\') {
        $arg = $arg -replace '\\+$', '$&$&'
      }
      if ($manuallyDoubleQuoteForCmd) {
        # Wrap in *embedded* enclosing double quotes, which both WinPS and PS Core pass through as-is.
        $arg = '"' + $arg + '"'
      }
      $arg
    }
    # Invoke the executable with the properly escaped arguments.
    & $exe $escapedArgs
  }
}

@ mklement0印象的ですが、Windowsでは機能しないカップルがあります。

iep echoargs 'somekey="value with spaces"' 'te\" st'

Arg 0 is <somekey="value>
Arg 1 is <with>
Arg 2 is <spaces">
Arg 3 is <te\">
Arg 4 is <st>

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" somekey=\"value with spaces\" te\\\" st

これが私のテスト配列の引数です:)

$Arguments = @(
    'trippe slash at the end \\\',
    '4 slash at the end \\\\',
    '\\servername\path\',
    'path=\\servername\path\',
    'key="\\servername\pa th\"',
    '5 slash at the end \\\\\',
    '\\" double slashed double quote',
    'simple',
    'white space',
    'slash at the end \',
    'double slash at the end \\',
    'trippe slash at the end \\\',
    'trippe slash at the end with space \\\ ',
    '\\" double slashed double quote',
    'double slashed double quote at the end \\"',
    '\\\" triple slashed double quote',
    'triple slashed double quote at the end \\\"',
    # slash
    'single slashes \a ^ \: \"',
    'path="C:\Program Files (x86)\test\"'
    # quotes
    'double quote " and single quote ''',
    # windows env var syntax
    "env var OS: %OS%",
    # utf16
    ('"utf16 ETHIOPIC WORDSPACE: \u1361"' | ConvertFrom-Json),
    # special chars
    "newLine`newLine"
    "tab`tab"
    "backspace`bbackspace"
    "carriage`rafter",
    "formFeed`fformFeed",
    # JSON Strings
    @"
[{"_id":"5cdab57e4853ea7b5a707070","index":0,"guid":"25319946-950e-4fe8-9586-ddd031cbb0fc","isActive":false,"balance":"`$2,841.15","picture":"http://placehold.it/32x32","age":39,"eyeColor":"blue","name":{"first":"Leach","last":"Campbell"},"company":"EMOLTRA","email":"[email protected]","phone":"+1 (864) 412-3166","address":"127 Beadel Street, Vivian, Vermont, 1991","about":"Ex labore non enim consectetur id ullamco nulla veniam Lorem velit cillum aliqua amet nostrud. Occaecat ipsum do est qui sint aliquip anim culpa laboris tempor amet. Aute sint anim est sint elit amet nisi veniam culpa commodo nostrud cupidatat in ex.","registered":"Monday, August 25, 2014 4:04 AM","latitude":"-12.814443","longitude":"75.880149","tags":["pariatur","voluptate","sint","Lorem","eiusmod"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Lester Bender"},{"id":1,"name":"Concepcion Jarvis"},{"id":2,"name":"Elsie Whitfield"}],"greeting":"Hello, Leach! You have 10 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cdab57e8cd0ac577ab534a4","index":1,"guid":"0be10c87-6ce7-46c4-8dd6-23b1d9827538","isActive":false,"balance":"`$1,049.56","picture":"http://placehold.it/32x32","age":33,"eyeColor":"green","name":{"first":"Lacey","last":"Terrell"},"company":"XSPORTS","email":"[email protected]","phone":"+1 (858) 511-2896","address":"850 Franklin Street, Gordon, Virginia, 4968","about":"Eiusmod nostrud mollit occaecat Lorem consectetur enim pariatur qui eu. Proident aliqua sunt incididunt Lorem adipisicing ea esse do ullamco excepteur duis qui. Irure labore cillum aliqua officia commodo incididunt esse ad duis ea. Occaecat officia officia laboris veniam id dolor minim magna ut sit. Aute quis occaecat eu veniam. Quis exercitation mollit consectetur magna officia sit. Irure ullamco laborum cillum dolore mollit culpa deserunt veniam minim sunt.","registered":"Monday, February 3, 2014 9:19 PM","latitude":"-82.240949","longitude":"2.361739","tags":["nostrud","et","non","eiusmod","qui"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Meyers Dillard"},{"id":1,"name":"Jacobson Franco"},{"id":2,"name":"Hunt Hernandez"}],"greeting":"Hello, Lacey! You have 8 unread messages.","favoriteFruit":"apple"},{"_id":"5cdab57eae2f9bc5184f1768","index":2,"guid":"3c0de017-1c2a-470e-87dc-5a6257e8d9d9","isActive":true,"balance":"`$3,349.49","picture":"http://placehold.it/32x32","age":20,"eyeColor":"green","name":{"first":"Knowles","last":"Farrell"},"company":"DAYCORE","email":"[email protected]","phone":"+1 (971) 586-2740","address":"150 Bath Avenue, Marion, Oregon, 991","about":"Eiusmod sint commodo eu id sunt. Labore esse id veniam ea et laborum. Dolor ad cupidatat Lorem amet. Labore ut commodo amet commodo. Ipsum reprehenderit voluptate non exercitation anim nostrud do. Aute incididunt ad aliquip aute mollit id eu ea. Voluptate ex consequat velit commodo anim proident ea anim magna amet nisi dolore.","registered":"Friday, September 28, 2018 7:51 PM","latitude":"-11.475201","longitude":"-115.967191","tags":["laborum","dolor","dolor","magna","mollit"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Roxanne Griffith"},{"id":1,"name":"Walls Moore"},{"id":2,"name":"Mattie Carney"}],"greeting":"Hello, Knowles! You have 8 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cdab57e80ff4c4085cd63ef","index":3,"guid":"dca20009-f606-4b99-af94-ded6cfbbfa38","isActive":true,"balance":"`$2,742.32","picture":"http://placehold.it/32x32","age":26,"eyeColor":"brown","name":{"first":"Ila","last":"Hardy"},"company":"OBLIQ","email":"[email protected]","phone":"+1 (996) 556-2855","address":"605 Hillel Place, Herald, Delaware, 9670","about":"Enim eiusmod laboris amet ex laborum do dolor qui occaecat ex do labore quis sunt. Veniam magna non nisi ipsum occaecat anim ipsum consectetur ex laboris aute ut consectetur. Do eiusmod tempor dolore eu in dolore qui anim non et. Minim amet exercitation in in velit proident sint aliqua Lorem reprehenderit labore exercitation.","registered":"Friday, April 21, 2017 6:33 AM","latitude":"64.864232","longitude":"-163.200794","tags":["tempor","eiusmod","mollit","aliquip","aute"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Duncan Guy"},{"id":1,"name":"Jami Maxwell"},{"id":2,"name":"Gale Hutchinson"}],"greeting":"Hello, Ila! You have 7 unread messages.","favoriteFruit":"banana"},{"_id":"5cdab57ef1556326f77730f0","index":4,"guid":"f2b3bf60-652f-414c-a5cf-094678eb319f","isActive":true,"balance":"`$2,603.20","picture":"http://placehold.it/32x32","age":27,"eyeColor":"brown","name":{"first":"Turner","last":"King"},"company":"DADABASE","email":"[email protected]","phone":"+1 (803) 506-2511","address":"915 Quay Street, Hinsdale, Texas, 9573","about":"Consequat sunt labore tempor anim duis pariatur ad tempor minim sint. Nulla non aliqua veniam elit officia. Ullamco et irure mollit nulla do eiusmod ullamco. Aute officia elit irure in adipisicing et cupidatat dolor in sint elit dolore labore. Id esse velit nisi culpa velit adipisicing tempor sunt. Eu sunt occaecat ex pariatur esse.","registered":"Thursday, May 21, 2015 7:44 PM","latitude":"88.502961","longitude":"-119.654437","tags":["Lorem","culpa","labore","et","nisi"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Leanne Lawson"},{"id":1,"name":"Jo Shepard"},{"id":2,"name":"Effie Barnes"}],"greeting":"Hello, Turner! You have 6 unread messages.","favoriteFruit":"apple"},{"_id":"5cdab57e248f8196e1a60d05","index":5,"guid":"875a12f0-d36a-4e7b-aaf1-73f67aba83f8","isActive":false,"balance":"`$1,001.89","picture":"http://placehold.it/32x32","age":38,"eyeColor":"blue","name":{"first":"Petty","last":"Langley"},"company":"NETUR","email":"[email protected]","phone":"+1 (875) 505-2277","address":"677 Leonard Street, Ticonderoga, Utah, 1152","about":"Nisi do quis sunt nisi cillum pariatur elit dolore commodo aliqua esse est aute esse. Laboris esse mollit mollit dolor excepteur consequat duis aute eu minim tempor occaecat. Deserunt amet amet quis adipisicing exercitation consequat deserunt sunt voluptate amet. Ad magna quis nostrud esse ullamco incididunt laboris consectetur.","registered":"Thursday, July 31, 2014 5:16 PM","latitude":"-57.612396","longitude":"103.91364","tags":["id","labore","deserunt","cillum","culpa"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Colette Mullen"},{"id":1,"name":"Lynnette Tanner"},{"id":2,"name":"Vickie Hardin"}],"greeting":"Hello, Petty! You have 9 unread messages.","favoriteFruit":"banana"},{"_id":"5cdab57e4df76cbb0db9be43","index":6,"guid":"ee3852fe-c597-4cb6-a336-1466e8978080","isActive":true,"balance":"`$3,087.87","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":{"first":"Salas","last":"Young"},"company":"PLAYCE","email":"[email protected]","phone":"+1 (976) 473-2919","address":"927 Elm Place, Terlingua, North Carolina, 2150","about":"Laborum laboris ullamco aliquip occaecat fugiat sit ex laboris veniam tempor tempor. Anim quis veniam ad commodo culpa irure est esse laboris. Fugiat nostrud elit mollit minim. Velit est laborum ut quis anim velit aute enim culpa amet ipsum.","registered":"Thursday, October 1, 2015 10:59 AM","latitude":"-57.861212","longitude":"69.823065","tags":["eu","est","et","proident","nisi"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Day Solomon"},{"id":1,"name":"Stevens Boyd"},{"id":2,"name":"Erika Mayer"}],"greeting":"Hello, Salas! You have 10 unread messages.","favoriteFruit":"apple"},{"_id":"5cdab57ed3c91292d30e141d","index":7,"guid":"ef7c0beb-8413-4f39-987f-022c4e8ec482","isActive":false,"balance":"`$2,612.45","picture":"http://placehold.it/32x32","age":36,"eyeColor":"brown","name":{"first":"Gloria","last":"Black"},"company":"PULZE","email":"[email protected]","phone":"+1 (872) 513-2364","address":"311 Guernsey Street, Hatteras, New Mexico, 2241","about":"Laborum sunt exercitation ea labore ullamco dolor pariatur laborum deserunt adipisicing pariatur. Officia velit duis cupidatat eu officia magna magna deserunt do. Aliquip cupidatat commodo duis aliquip in aute dolore occaecat esse ad. Incididunt est magna in pariatur ut do ex sit minim cupidatat culpa. Voluptate eu veniam cupidatat exercitation.","registered":"Friday, June 26, 2015 7:59 AM","latitude":"38.644208","longitude":"-45.481555","tags":["sint","ea","anim","voluptate","elit"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Abby Walton"},{"id":1,"name":"Elsa Miranda"},{"id":2,"name":"Carr Abbott"}],"greeting":"Hello, Gloria! You have 5 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cdab57edc91491fb70b705d","index":8,"guid":"631ff8a0-ce4c-4111-b1e4-1d112f4ecdc7","isActive":false,"balance":"`$2,550.70","picture":"http://placehold.it/32x32","age":25,"eyeColor":"brown","name":{"first":"Deirdre","last":"Huber"},"company":"VERBUS","email":"[email protected]","phone":"+1 (871) 468-3420","address":"814 Coles Street, Bartonsville, Tennessee, 7313","about":"Ipsum ex est culpa veniam voluptate officia consectetur quis et irure proident pariatur non. In excepteur est aliqua duis duis. Veniam consectetur cupidatat reprehenderit qui qui aliqua.","registered":"Monday, April 1, 2019 2:33 AM","latitude":"-75.702323","longitude":"45.165458","tags":["labore","aute","nisi","laborum","laborum"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Genevieve Clarke"},{"id":1,"name":"Black Sykes"},{"id":2,"name":"Watson Hudson"}],"greeting":"Hello, Deirdre! You have 8 unread messages.","favoriteFruit":"strawberry"}]
"@
)

System.Diagnostics.Processを使用する場合には制限があります。そのため、STDOUT STDERRをキャプチャし、それらを変数に組み合わせる機能を備えた独自のランナーを作成する必要がありました。これは、ユースケースには十分ですが、完全ではありません。

編集:あなたが言ったように、Windows Poweshell5ではエッジケースがあるようです。Powershell6でテストしましたが、うまく機能します。 残念ながら、Windowsの場合は7が引き継ぐまでPoweshell5を処理する必要があります...

印象的ですが、Windowsでは機能しないカップルがあります。

はい、これらの制限は_Windows PowerShell_に適用され、以前のコメントで詳しく説明したように、埋め込まれた" ""エスケープのサポートを想定できない実行可能ファイルがあります。
すべての呼び出しで""エスケープのサポートを想定する場合(_most_ですが、Windows上のすべての実行可能ファイルがサポートしているわけではありません)、関数を簡単に調整できます。

そして、編集であなたが言ったことを確認するには:PowerShell _Core_:

  • iep echoargs 'somekey="value with spaces"' 'te\" st'は正常に機能します。
  • 引数のテスト配列も正常に機能しているようです。

すべての呼び出しで""エスケープのサポートを想定する場合(_most_ですが、Windows上のすべての実行可能ファイルがサポートしているわけではありません)、関数を簡単に調整できます。

おかげさまで、近いうちに試してみます。 私は時々sqlcmdを扱いますが、それは確かに""をサポートしていません。 その場合、特定の引数のエスケープロジックをスキップするオプションを簡単に提供できます。

Windowsがコマンドライン引数を解析する方法は、 C ++コマンドライン引数の解析にあります

Microsoft C / C ++スタートアップコードは、オペレーティングシステムのコマンドラインで指定された引数を解釈するときに次のルールを使用します。

  • 引数は、スペースまたはタブのいずれかである空白で区切られます。

  • キャレット文字(^)は、エスケープ文字または区切り文字として認識されません。 文字は、プログラムのargv配列に渡される前に、オペレーティングシステムのコマンドラインパーサーによって完全に処理されます。

  • 二重引用符で囲まれた文字列( " string ")は、空白が含まれているかどうかに関係なく、単一の引数として解釈されます。 引用符で囲まれた文字列を引数に埋め込むことができます。

  • バックスラッシュ(\ ")が前に付いた二重引用符は、リテラルの二重引用符文字(")として解釈されます。

  • バックスラッシュは、二重引用符の直前にない限り、文字通りに解釈されます。

  • 偶数のバックスラッシュの後に二重引用符が続く場合、バックスラッシュのペアごとに1つのバックスラッシュがargv配列に配置され、二重引用符は文字列区切り文字として解釈されます。

  • 奇数の円記号の後に二重引用符が続く場合、円記号のペアごとに1つの円記号がargv配列に配置され、残りの円記号によって二重引用符が「エスケープ」され、リテラルが生成されます。 argv配置される二重引用符( ")。

これについては、 _exec、_wexec関数でも説明されてい

文字列に埋め込まれたスペースは、予期しない動作を引き起こす可能性があります。 たとえば、 _execに文字列"hi there"を渡すと、新しいプロセスは"hi""there" 2つの引数を取得します。 新しいプロセスで「hithere」という名前のファイルを開くことが目的の場合、プロセスは失敗します。 文字列"\"hi there\""引用することで、これを回避できます。

Pythonがネイティブ実行可能ファイルを呼び出す方法

Pythonのsubprocess.Popen、Windowsでの引数シーケンスの文字列への変換で説明されている方法でargsを文字列に変換することにより、エスケープを正しく処理できます。 実装はsubprocess.list2cmdlineです。

これにより、 --%や二重エスケープ(PowerShellとCMD構文の両方に従う)を使用する代わりに、PowerShellがこれをよりエレガントな方法で処理する方法が明らかになることを願っています。 現在の回避策は、Azure CLI(python.exeに基づく)の顧客に実際にバグをもたらします。

この問題の処理方法を理解するための明確で簡潔な方法はまだありません。 これがv7で単純化された場合、Powershellチームの誰かが光を当てることができますか?

幸いなことに、PS 7.1の焦点の1つは、ネイティブコマンドの呼び出しを簡単にすることです。 7.1投資に関するブログ投稿から:

ほとんどのネイティブコマンドはPowerShell内からは問題なく機能しますが、引数の解析が理想的でない場合もあります(引用符を適切に処理するなど)。 その目的は、ユーザーが一般的なネイティブツールのサンプルコマンドラインを切り取ってPowerShellに貼り付けることができるようにすることであり、PowerShell固有のエスケープを必要とせずに機能します。

だから多分(うまくいけば)これは7.1で対処されるでしょう

ありがとう、 @ rkeithhill 、しかししません:

その目的は、ユーザーが一般的なネイティブツールのサンプルコマンドラインを切り取ってPowerShellに貼り付けることができるようにすることであり、PowerShell固有のエスケープを必要とせずに機能します。

本質的に概念的に問題のある--% (構文解析の停止記号)に対する別の見方のように聞こえますか?

@ SteveL-MSFT、この今後の機能について詳しく教えてください。


要約すると、ここで提案されている修正は、_PowerShell_の構文要件を満たすことだけに焦点を当て、PowerShellが_舞台裏で_すべてのエスケープを処理することです(Windowsの場合、Unixの場合、これはもう必要ありません。.NETで許可されています。逐語的なトークンの配列をターゲットプログラムに渡す)-ただし、下位互換性を維持する必要がある場合は、修正をオプトインする必要があることは間違いありません。

この修正により、他のシェルのコマンドラインをいじる必要がありますが、より簡単になります。また、 --%と比較すると、PowerShellの構文と変数の展開( (...)式)の全機能を保持できます。

  • \"`"に置き換える必要がありますが、_は"..."です。

  • $USERなどのBashスタイルの環境変数参照が機能しない(_PowerShell_変数として解釈される)ように、_no(その他)shell_が関与していることに注意する必要があります。同等のPowerShell構文、 $env:USER

    • 余談ですが、 --%は、 cmd.exeスタイルの環境変数参照( %USERNAME%など)を拡張することで(特に_常に_)それを補おうとしますが、それだけではないことに注意してください。 tサポートBashスタイルの参照( $USER )はターゲットプログラムに_verbatim_渡されますが、Unixライクなプラットフォームでcmd.exeスタイルの参照を予期せず拡張し、 '...'認識しません
    • それぞれのプラットフォームネイティブシェルが関与する代替案については、以下を参照してください。
  • PowerShellには、逐語的に使用するために引用符/エスケープを必要とする_additional_メタ文字があることに注意する必要があります。 これらは( @は引数の_first_文字としてのみ問題があることに注意してください。):

    • POSIXのようなシェル(Bashなど)の場合: @ { } ` (PowerShellによる事前の拡張を防ぎたい場合は$
    • cmd.exe( ) @ { } # `
    • 個別に` -そのような文字をエスケープします。 十分です(例: printf %s `@list.txt )。

やや不自然な例:

次のBashコマンドラインを使用します。

# Bash
$ printf '"%s"\n' "3\" of snow"
"3" of snow"        # output

代わりに提案されている修正により、必要とされるすべてが交換することです\"内のインスタンスを"..."で-enclosed引数`"

# PowerShell - WISHFUL THINKING
PS> printf '"%s"\n' "3`" of snow"
"3" of snow"        # output

つまり、 '...' "内に埋め込まれた"..."内で""ます)。

上記のiep関数は、この修正を(一時的なギャップとして)実装しているため、 iep printf '"%s"\n' "3`" of snow"は意図したとおりに機能します。


これを現在の壊れた動作と比較してください。Bashのようにコマンドを機能させるには、次のフープをジャンプする必要があります( \でのエスケープの_追加_ラウンドが不可解に必要です):

# PowerShell - messy workaround to compensate for the current, broken behavior.
PS> printf '\"%s\"\n' "3\`" of snow"
"3" of snow"        # output

修正が行われると、プラットフォームの_default shell_を介して_as-is_で特定のコマンドラインを使用したい場合は、逐語的な_here-string_を使用してsh -c (またはbash -cに渡すことができます。 cmd /c ; 例えば:

# PowerShell - WISHFUL THINKING
PS> sh -c @'
printf '"%s"\n' "3\" of snow"
'@
"3" of snow"  # output

--%使用はここでは機能しないことに注意してください( printf --% '"%s"\n' "3\" of snow" )。ここで文字列ベースのアプローチの追加の利点は、さまざまな--%制限が適用されないことです。 、特に出力リダイレクトを使用できないこと( > )。

_double_-quoted here-string( @"<newline>....<newline>"@ )に切り替えると、 --%とは異なり、_PowerShell変数と式_を埋め込むこともできます。 ただし、展開された値がターゲットシェルの構文を壊さないことを確認する必要があります。

そのような呼び出しのための簡潔なエイリアス(たとえば、 Invoke-NativeShell / inssh -c / cmd /c必要ありません指定する)が、複雑なコマンドラインをそのまま渡すために、ヒア文字列を使用する方法は考えていません。

# PowerShell - WISHFUL THINKING
# Passes the string to `sh -c` / `cmd /c` for execution, as appropriate.
# Short alias: ins
PS> Invoke-NativeShell @'
printf '"%s"\n' "3\" of snow"
'@
"3" of snow"  # output

もちろん、プラットフォームネイティブシェルの機能に依存している場合、そのような呼び出しは定義上、プラットフォーム[ -

これが、長期的には_独自の構文_を持つPowerShell _alone_に依存することが望ましい理由です。これは、外部プログラムを呼び出すための予測可能なクロスプラットフォームエクスペリエンスを提供します。です; PowerShellの人気が高まるにつれ、必要な変更を見つけて知ることの苦痛が軽減されることを期待しています。また、PowerShellバージョンのコマンドラインを示すドキュメントが増えることを期待しています(あまりにも)。

  • \"`"に置き換える必要がありますが、_は"..."です。

これはどこでも機能するわけではありません。

# working everywhere but polluted with \
❯ node -e 'console.log(\"hey\")'
hey

# working in Node:
❯ node -e 'console.log(`"hey`")'
hey

# not working in Julia:
❯ julia -e 'print(`"hey`")'
`hey`

# not working anywhere:
❯ node -e "console.log(`"hey`")"
❯ node -e "console.log("hey")"
❯ node -e "console.log(""hey"")"
❯ node -e 'console.log(""hey"")'

Bash構文:

❯ node -e 'console.log("hey")'
hey

Powershellの提案:

PowerShellチームが、前の構文を壊さない記号を探しているだけの場合は、リテラルを自動的にエスケープし、文字列の補間を可能にするBashのような動作にbackticks`のようなものを使用しないでください。 これもJavaScriptの構文に似ています。

❯ node -e `console.log("hey")`
hey

❯ $a=hey 
❯ node -e `console.log($hey)`
hey

Bashのような動作にバックティック `のようなものを使用しないのはなぜですか

バックティックは、POSIXシェルのバックスラッシュと同じ目的で、ものをエスケープするためにすでに使用されています。 あなたが言及しているコメントはすでにそれを指摘しています。 ASCII引用符のようなものをすべて使い果たしました。 通常の文字列リテラルに$プレフィックスを追加することは機能するかもしれませんが、それは十分に意味があるとは思いません。


Windowsがコマンドライン引数を解析する方法は次の場所にあります...

問題は、Windows MSVCRがそれを行うだけでなく、文書化されていない方法でコーナーケースすることです。 ""ものは非常にしっかりと設定されているので、.NETをUnixに移植したときにCoreFXに入れることさえできます。 しかしとにかく、少なくとも誰かがグロブを要求するまで、それは逃げるのに常に十分です。

みんなが違うやり方でやっているという古典的な問題もありますが、生のコマンドラインには常に.NETがあるので、それについて心配する必要はありません。

Bashのような動作にバックティック `のようなものを使用しないのはなぜですか

バックティックは、POSIXシェルのバックスラッシュと同じ目的で、ものをエスケープするためにすでに使用されています。 ASCII引用符のようなものをすべて使い果たしました。

ここで `が文字列を導入していることをパーサーが検出できない場合は、記号の組み合わせを使用する可能性があります。 ''ようなものでも機能する可能性があります。

@aminyaは、古いWindows
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606

WindowsではPowerShell5,1、Linux / Macでは6+を処理する必要があるため、複雑なJSONを渡すkubectl、helm、terraformなどのツールを使用できるようにする独自の実装が何年も問題なく機能しています。パラメータ内のオブジェクト:
https://github.com/choovick/ps-invoke-externalcommand

GitHub
GitHubでアカウントを作成して、choovick / ps-invoke-externalcommandの開発に貢献します。

@choovickのやや短い実装がこのスレッド

@AndrewSav @TSlivedeRun-Native関数は非常に巧妙で簡潔であり、_WindowsPowerShell_でも確実に機能します。 注目に値するいくつかの事柄:必然的に、子プロセスはauxを見ます。 commandlineargumentstring環境変数(おそらく、実際に問題が発生することはめったにありません)、引数なしの呼び出しは現在正しく処理されていません(関数のみを使用するようにすると、簡単に修正でき、問題も発生しません) _with_引数、これが目的です)、_ all_引数は二重引用符で囲まれます(たとえば、 42ようなものは"42"として渡されます)。これは、Windowsでは(残念ながら)次のような副作用があります。二重引用符で囲まれた(またはmsiexec場合のように部分的に二重引用符で囲まれた)引数を異なる方法で解釈するプログラム。

@aminyanode -e 'console.log(`"hey`")' (一種の)が機能する唯一の理由は、現在の壊れた動作のためです(以下を参照)。 渡すつもりだったのは_verbatim _ console.log("hey")だと思います。これは、Windows上のPowerShellがここで提案されているように正しくエスケープした場合、そのまま一重引用符で囲みます: node -e 'console.log("hey")' 。 これは_自動的に_ node -e "console.log(\"hey\")" (二重引用符、 \ -escaped verbatim " )_舞台裏_に翻訳する必要があります。

このスレッドがどれだけ長くなったかを考えて、要約してみましょう。
_PowerShell_の構文要件についてのみ心配する必要があります。また、PowerShell自体の解析の結果である_verbatim引数値_が外部プログラム_as-is_に渡されるようにするのは、シェルとしてのPowerShellの仕事です。

  • Unixライクなプラットフォームでは、.NET Coreがサポートされているので、これを行うのは簡単です。逐語的な値を引数の_array_の要素としてそのまま渡すことができるためです。これは、プログラムが引数をネイティブに受け取る方法です。 。
  • Windowsでは、外部プログラムは引数を_単一のコマンドライン文字列_(残念な歴史的設計上の決定)として受け取り、独自のコマンドライン解析を実行する必要があります。 単一の文字列の一部として複数の引数を渡すには、引数を適切に描写するために引用符と解析のルールが必要です。 これは最終的にはすべて無料ですが(プログラムは自由に解析できます)、最も広く使用されている構文規則(前述のとおり、残念ながら現在は廃止されたRFCでも提案されてい、MicrosoftのC / C ++コンパイラが実装するものです。だからそれで行くのは理にかなっています。

    • _Update_:Windowsでも、.NET CoreのSystem.Diagnostics.ProcessStartInfo verbatim-tokens ArgumentListプロパティを利用できます。Windowsでは、適切に引用されエスケープされたコマンドに自動的に変換されます。 -プロセス開始時の行文字列。 ただし、_バッチファイル_については、特別な処理が必要になる場合があります-https ください

上記の実装は間違いなく大規模な重大な変更であるため、おそらくオプトインが必要です。
特にUnixの世界で、PowerShellの採用を妨げている現在の引用の頭痛の種をすべて取り除きたいのであれば、これを進めることは必須だと思います。


node -e 'console.log(`"hey`")''...'では、その文字をそのまま通過させたい場合を除いて、 `使用しないでください。 PowerShellは現在、逐語的な"文字をエスケープしないためです。 \"の舞台裏での引数では、 nodeがコマンドラインに表示されるのはconsole.log(`"hey`") 、これは2つの直接隣接する文字列リテラルとして解析されます。引用符で囲まれていないconsole.log(`と二重引用符で囲まれた"hey`"\エスケープされていないために_syntactic_関数を持つ"削除した後、実行されるJavaScriptコードは最終的にconsole.log(`hey`)になりますが、これは`....`囲まれたトークンは、JavaScriptの文字列リテラルの形式、つまり_templateliteral_です。

@AndrewSavクレイジーなテストオブジェクトでテストしましたが、"明示的に扱うことでも知られているmsiexecまたはsqlcmdは扱いません。

私の個人的な実装にも、あなたが言及したものと同様の単純なエスケープロジックがあります: https

しかし、そのモジュール内でSTDOUTスレッドとSTDERRスレッドをリアルタイムで表示およびキャプチャするためのコードをたくさん作成しました...おそらく大幅に簡略化できますが、必要はありませんでした...

@ mklement0このスレッドは決して終了しません(:ほとんどのユースケースに

GitHub
GitHubでアカウントを作成して、choovick / ps-invoke-externalcommandの開発に貢献します。

@ mklement0このスレッドは決して終了しません(:ほとんどのユースケースに

PowerShellチームがプログラム自体でこれを修正しないことを決定した場合、外部プログラムをネイティブに正しく実行できないシェルは、私の選択したシェルではないと言えます。 これらは、PowerShellに欠けている基本的なものです。

@aminya yea、私がそれに移動しなければならなかったとき、問題は私自身非常に迷惑であることがわかりました。 しかし、以下のような機能はそれだけの価値があります。

  • 柔軟なパラメータフレームワーク、信頼性の高いCMDletの構築が簡単。
  • モジュールと内部モジュールリポジトリは、組織全体で共通のロジックを共有し、コードの重複を回避し、一度にリファクタリングできるコア機能の集中化を回避します。
  • クロスプラットフォーム。 PS 5.1のWindowsと、PS6 / 7のlinux / macでツールを実行している人がいます。

PSチームが将来これを改善して、複雑さを軽減することを本当に望んでいます。

上記の実装は間違いなく大規模な重大な変更であるため、おそらくオプトインが必要です。

https://github.com/PowerShell/PowerShell-RFC/pull/90を実装することを意味し

@aminya 、引数を使用して外部プログラムを呼び出すことは

@choovickによって提案されているように、モジュールは、セキュリティクリティカルな修正のみのメンテナンスモードにある
補足として、外部プログラムの呼び出しに関する提案された概念的なヘルプトピックが記述された場合( https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152を参照@TSlivedeのように、そこに直接投稿できます。

@iSazonovはい、 https://github.com/PowerShell/PowerShell-RFC/pull/90を実装することを意味しました。

スレッドが終わることはなく、重大な変更の懸念については、次のようになります。

リンクされたRFCに対する最後の公式の応答は、 2019年7月8日からの@joeyaielloによるこのコメント(強調が追加されました):

しかし、この[RFC]は、既存の動作に関係なく、またその破壊性これがオプトインとオプトアウトの動作であるかどうか、何らかの移行がある

_個人的に_、それは重大な変更ですが、_デフォルトで_動作を修正してもかまいません。 RFCは確かにそれを提案し、_old_(壊れた)動作が必要な場合はオプトインを提案します。

ただし、_既存のすべての回避策が機能しなくなる_(上記を参照)ため、レガシーコードを維持している人は反対するだろうと思います。また、下位互換性を維持することが依然として包括的な目標のようです。

新しい修正された動作がオプトインされた場合でも、正しい動作を取得するためだけに何かをしなければならないという厄介な問題がありますが、少なくとも既存のコードは壊れません。

しかし、既存のオプトインメカニズム自体に問題があります。
@KirkMunroオプション機能RFCが拒否されたため、_preference変数_がほとんど残っており、PowerShellの動的スコープが課題となっています。オプトインしたスコープから呼び出されたサードパーティのコードで、新しいものを使用するように設計されていません。その後、実装が中断する可能性があります(設定変数が一時的にリセットされない限り)。

ここではオプトインの_字句_スコープが必要ですが、現在はありません。 strictモードの_lexical_スコープに関するRFCはusingステートメント(特に、一般的に_dynamicly_スコープ)を介して、字句スコープ機能(または別の目的)の実装を提案します。 このパターンに従って、字句スコープのusing ProperExternalArgumentQuotingステートメント(名前はWIPです:)-技術的に可能な場合-検討する価値があります。

PowerShell委員会が(再び)検討し、今後の方向性について明確なガイダンスを提供し、質問が発生したときに_タイムリーに_フィードバックを提供する必要があります。 @ SteveL-MSFT?


7.1のブログ投稿(上記を参照)によって示唆された--%ようなソリューション(個人的には追求する価値がないと思います-上記このコメントを参照)は、PowerShellのネイティブを修正する_個別の_機能になることに注意してください(非エミュレーション)動作は依然として必須です。

個人的には、それが重大な変更であるとしても、デフォルトで動作を修正してもかまいません。 RFCは確かにそれを提案し、古い(壊れた)動作が必要な場合はオプトインを提案します。

新しい修正された動作がオプトインされた場合でも、正しい動作を取得するためだけに何かをしなければならないという厄介な問題がありますが、少なくとも既存のコードは壊れません。

同意しました。実際、デフォルトが壊れていて、正しく実装されているデフォルトを使用することはあまり意味がないと私は主張します。 現在の実装が実際にはバグであるという事実を考えると、新しい動作はオプトアウトではなくオプトインである必要があります。予期せずに侵入する傾向がある壊れた外部シェル呼び出しを引き続き奨励することは意味がないためです。方法。 いずれにせよ、PowerShell 7は、従来のWindowsPowerShellよりも改善に努める必要があります。

@ SteveL-MSFTと私は、#13068を優先してこれを閉じる必要があることに同意しました。 ここで触れることは、重大な変更ではありません。オプトインモードとして機能する新しい演算子を使用して問題に対処する必要があります。

#13068がこれをどのように解決するかは絶対にわかりません。その演算子が意図したとおりに導入された場合でも、引数の特定の配列または変数に由来するコンテンツを持つ明示的な引数を使用して、ネイティブ実行可能ファイルを適切に呼び出す方法がありません。

@JustinGroteがそのスレッドで提供した例は、現在確実に機能せず(引数のペイロードに引用符が埋め込まれている場合)、その演算子を追加しても、何かを改善する代替手段は提供されません。

@joeyaiello少なくとも、その演算子が実際に存在し、誰かがその演算子がこのスレッドで言及されている何かをどのように改善するかを示すことができるまで、この問題を開いたままにしておくことはできますか?

ああ、Linuxはどうですか? この問題は、Windowsでは愚かで予期しないものですが、Linuxでは、特にLinuxのPowerShellスクリプトの長い歴史がないため、それはさらに意味がありません。

その主な仕事はプログラムを起動して引数を渡すことなので、このための特別な演算子を作成することは、コマンドラインシェルにはまったく意味がありません。 このジョブにsystem()を実行する新しい演算子を導入することは、Matlabが算術演算にバグがあるため、 calc.exeを呼び出す方法を導入することに似ています。 代わりにすべきことは次のとおりです。

  • pwshチームは、コマンドラインを修正する新しいメジャーリリースの準備をし、現在の動作を組み込みのコマンドレットの背後に移動します。
  • 一時的な解決策として、次のpwshバージョンには、コマンドラインの受け渡しに新しい正しい動作を使用する組み込みのコマンドレットが含まれています。

同じことがStart-Processも当てはまります。 (実際には、 -QuotingBehavior Legacyようないくつかのオプションを備えた「新しい」コマンドレットのかなり良い候補です...)#13089を参照してください。

これらの2つの状況でPowershellの動作が異なるのはなぜですか? 具体的には、スペースを含む引数を二重引用符で囲んで一貫性がありません。

v.7で一貫した結果が得られます。 修正されたようです。

PING 'A \"B'

ping要求でホストA "Bが見つかりませんでした。

PING 'A\" B'

ping要求でホストA "Bが見つかりませんでした。

ping表示される逐語的なホスト名はA \"BA\" B -_ with_ \文字であるため、修正されていません。

PowerShellは、シェルとして、_its_ルールに従って引数を解析する必要があります-のみ-次に、PowerShell自体の解析の結果であるのと同じ逐語的な値がターゲットプロセスに認識されるようにします。

その_other_シェル-そして、渡された個々の引数を抽出するためだけに_コマンドライン_を解析する必要があるという意味で、独自のシェルのように動作する必要があるWindowsで実行されている貧弱なプログラム-エスケープとして\を使用します文字をここに入力しないでください-これに対応すること(Windowsでのみ必要、Unixでは逐語的な引数を配列として直接渡すだけです)は、シェルとしてのPowerShellの仕事です。

余談ですが、PowerShell自体が" _inside '...' _(一重引用符で囲まれた文字列)をエスケープする必要がないのと同様に、 bashなどのPOSIX互換シェルも必要ありません。たとえば、 bashから実行されると、 /bin/echo 'A \"B' (賢明に) A \"B\は一重引用符で囲まれた文字列のリテラルとして扱われます)- PowerShellからの非常に同じコマンド(予期せず)が生成されます
A "B\がありません)は、ここで説明した問題の兆候です。

私は明確にする必要があります:

  • 最終的にA "B _verbatim_を渡したいという観点からは、PowerShellから'A "B'を使用できるはずです。

  • PowerShellが現在舞台裏で構築しているコマンドラインには"A "B"が含まれています-ターゲットプロセスはA B見なします-つまり、_embedded_ "をエスケープせずに、 "..."のブラインドエンクロージャーです。 "により、埋め込まれた"が実質的に失われました。 この場合、PowerShellが舞台裏のコマンドラインで使用する必要があるのは"A \"B"です。つまり、埋め込まれた"\エスケープが必要です。

  • 同様に、同じブラインドエンクロージャーを使用すると、舞台裏のコマンドラインで'A \"B'"A \"B"として表されます。これにより、_embedded_ \"が_escaped_に変わります。 "文字。したがって、ターゲットプロセスはこれをA "B見なします。 つまり、_automatic_エスケープがないため、埋め込まれた\が実質的に失われました。 この場合、PowerShellが舞台裏のコマンドラインで使用する必要があるのは"A \\\"B"です。つまり、 \"両方をエスケープする必要があります。

ping表示される逐語的なホスト名はA \"BA\" B -_ with_ \文字であるため、修正されていません。

「それ」とは、ここで引用された苦情を指しますが、幸いなことに再現することはできません。

@ yecril71pl 、なるほど:私の(間違った)仮定は、「スペースを含む引数を二重引用符で一貫して折り返す」とは、埋め込まれた"および\文字の自動エスケープの欠如を指すというもの"私の以前のコメントで説明しました-そしてそれがこの問題の核心です。

PowerShell Coreには、WindowsPowerShellにはないマイナーな修正があります。 私は今1つしか考えることができません:

  • Windows PowerShellは、末尾に\がある場合にブラインド二重引用符を使用します: 'A B\'は(壊れた) "A B\"変わります-PS Coreはそれを正しく処理します( "A B\\" ) 。

このリポジトリはPSCore専用であるため、PS Coreでまだ壊れているものに焦点を当てるだけで十分です(違いについては脇に置いておくと便利ですが、その側面を明確にするのが最善です)。


そして、あなたが考えていたシナリオでさえ、PS Coreではまだ壊れています-しかし、引数から\を省略した場合のみ_:

'A" B'を渡すと、バックグラウンドで_non_-double-quoted A" Bが発生します(一方、 'A\" B'"A\" B"これも、説明したように壊れています-のみ別の方法で)。

このリポジトリはPSCore専用であるため、PS Coreでまだ壊れているものに焦点を当てるだけで十分です(違いについては脇に置いておくと便利ですが、その側面を明確にするのが最善です)。

メタはさておき:他のユーザーのコメントで言及されている間違った振る舞いは当てはまらないことを知っておくと便利だと思います。 もちろん、ユーザーがわざわざ現在のバージョンを確認しなかったという理由だけで、コメントを却下することもできます。 上手。 手に負えない記者は手に負えないので、それでも確実にする方が良いです。 私見では。

そこには議論はありませんが、そのような_asides_に_適切なフレーミングとコンテキスト_を提供することは重要です。特に、コンテキストに必要な元のコメントがずっと前に投稿され、実際にはデフォルトで_非表示_になっている(傷つくことはありません)引用されている元のコメントに実際に_リンク_します)。

これらの議論が違いを生むのだろうか。 明らかに、委員会の決定は、コミュニティが望んでいることとは無関係です。 タグを見てください: Resolution- won't fix.しかし、PowerShellはオープンソース(MIT)であるため、コミュニティはこれを別のフォークで修正し、このPowerShellCommunity( pwshc )を略して呼び出すことができます。

これにより、下位互換性が不要になります。 後のPowerShell8で、委員会はフォークを統合する可能性があります。

下位互換性について:PowerShellはどのオペレーティングシステムにもプリインストールされていません。私にとって、 PowerShellCommunityのインストールの難しさはPowerShellと同じです。 私は、コミュニティバージョンをインストールして、将来の8バージョンを待つ(または新しい演算子でコードをより複雑にする)のではなく、すぐに使用することを好みます。

委員会は物事を壊しておくことを決定したので、それらがどれほどひどく壊れているかを知ることはより良いことです。 コミュニティは正しいことをするInvoke-Nativeと一緒に暮らすことができると思います。 彼らの意志に反してマイクロソフトの顔を救うことはコミュニティの仕事ではありません。

彼らがどれほどひどく壊れているかを知ることはより良いです

私は完全に同意します-問題が現時点で解決できない場合でも、原則として正しい方法を知っていて、時が来ればいつでも重要です-たとえその時が特定の言語の文脈で来たとしても。

コミュニティは正しいことをするInvoke-Nativeと一緒に暮らすことができます

明確にするために:

  • Invoke-NativeShellようなものは、#13068の主題である_異なるユースケース_に対応します。そのユースケースの場合、そのようなコマンドレットは一時的なものではありません。これは適切な解決策です。https://github.com/を参照して

  • _This_の問題は_PowerShell自体_の修正に関するものであり(プラットフォーム-_ネイティブ_機能に関するものではありません)、それに対する_stopgap_ソリューションは低セレモニーの_opt-in_を提供することです-したがって、関数iepをビルドとして

マイクロソフトの顔を救うことはコミュニティの仕事ではありません

@aminyaの懸念は、誰かの顔を救うことではないと思います。それは、シェルの中心的な任務である領域で根本的に壊れた動作を修正することです。

とはいえ、PowerShellエコシステムをフォークで断片化することが正しい方法かどうかはわかりません。

現時点では、これらすべてに対応する時間がありませんが、これは合理的だと思うので、再開します。

その演算子が実際に存在し、誰かがその演算子がこのスレッドで言及された何かをどのように改善するかを示すことができるまで、少なくともこの問題を開いたままにしておくことができますか?

関連する問題で述べたように、ネイティブの呼び出し演算子はUXに複雑さを追加し、それは間違った方向です。
同時に、ここでの全体的な議論は、ネイティブアプリケーションとの相互作用を単純化することについてです。

私たちを止める唯一のことは、それが壊滅的な変化であるということです。 これは破壊が多すぎると何度も言いましたが、それを

インタラクティブセッションを見てみましょう。 ユーザーは新しいPowerShellバージョンをインストールし、ネイティブアプリケーションを呼び出すときに新しい動作を発見します。 彼は何と言いますか? 彼は「ありがとう-ついにタイプするだけでうまくいく!」と言うだろうと推測します。

スクリプトの実行/ホスティングのシナリオを見てみましょう。 アプリケーションの新しいバージョンは(わずかな変更が1つでも!)、ビジネスプロセスを壊す可能性があります。 私たちは常にこれを期待し、更新後にプロセスをチェックします。 問題を見つけた場合、いくつかの方法があります。

  • アップデートをロールバックする
  • スクリプトの高速修正を準備する
  • これらが修正されるか、時間の経過とともにスクリプト自体が停止するまで、スクリプトを中断する機能をオフにします。

_バージョンの更新は常に何かを壊すので、最後のオプションは私たちが持って受け入れることができる最善のものです。_

(現時点では、PowerShell CoreはWindowsのコンポーネントではないため、ホスティングアプリケーションを直接損なうことはできず、スクリプトのみが直接影響を受けることを指摘したいと思います。)

ジェイソンが上で言ったことを思い出させたい-これはバグ修正です。
それを修正して、すべての人のためにすべてを簡素化しましょう。 ユーザーがpowershell-y作業できるようにします。

関連する問題で述べたように、ネイティブの呼び出し演算子はUXに共謀を追加し、それは間違った方向です。

複雑さ❓

インタラクティブセッションを見てみましょう。 ユーザーは新しいPowerShellバージョンをインストールし、ネイティブアプリケーションを呼び出すときに新しい動作を発見します。 彼は何と言いますか? 彼は「ありがとう-ついにタイプするだけでうまくいく!」と言うだろうと推測します。

別のシナリオがあります。新しいユーザーがPowerShellを試してみて、不思議なことに失敗したことに気づき、ごみ箱に移動して二度と戻らないというシナリオがあります。 それが私がすることです。

スクリプトの高速修正を準備する

これは、ユーザースクリプトの明らかなケースをカバーするために行う必要があることです。

それが不思議なことに失敗することに気づきます

ここでの全体の議論は、この「不思議な失敗」を取り除くことについてだけです。 そして、これを行うには、新しい不思議なものを単純化するが追加しないことが最善です。

ここでの全体の議論は、これを取り除くことについてです。 そして、これを行うには、新しい不思議なものを単純化するが追加しないことが最善です。

実行可能プログラムを直接呼び出す機能を削除したくありません。

古い呼び出しは、何かがすでに引用されているかどうかを推測することで、cmdlineに直接ではありません。 さらに、上記の能力は-%で保持されます。

さらに、上記の能力は-%で保持されます。

--%は事前定義されたコマンドラインが必要なため、そのユーティリティは制限されています。

@ yecril71pl

実行可能プログラムを直接呼び出す機能を削除したくありません。

@iSazonovは、既存の回避策を破ることを意味しますが、バグのある動作を取り除くこと、つまりバグを適切に修正することを意味すると思います(追加の構文によるオプトインなしで)。 正解、@ iSazonov?

@ Artoria2e5

古い呼び出しは、何かがすでに引用されているかどうかを推測することで、cmdlineに直接ではありません

[_Update_:引用された行を読み間違えましたが、情報がまだ興味深いものであることを願っています。]

  • _推測_する必要はなく、ルールは単純です。

    • コマンド名またはパスが_quoted _- '/foo bar/someutil '-および/または_変数参照(または式)が含まれている場合のみ_ $HOME/someutil - &が_必須_です。
  • この必要性は残念なことだと思われるかもしれませんが、PowerShell言語の中心であり、2つの基本的な解析モードである引数モードと式モードを構文的に区別できる必要があります。

    • この問題は_外部プログラム_の呼び出しに固有のものではないことに注意してください。 ネイティブPowerShellコマンドも、引用符で囲まれた文字列/変数参照を介して指定された場合、呼び出しに&が必要です。
  • この単純なルールを覚えたくない場合は、より単純なルールは次のとおりです。_常に_ &を使用すれば、問題ありません。何も必要としないよりも便利ではありませんが、厳密には困難ではありません(そしてより短いです)。 --%よりも、これは絶対に間違った解決策です-以下を参照してください)

上記の能力は-%で保持されます。

[_Update_:引用された行を読み間違えましたが、情報がまだ興味深いものであることを願っています。]

いいえ、残念ながら#13068と_this_の問題が混同されていますが、 --% _ its_、常に_platform固有の構文を使用して_native shell_を呼び出す機能)は、目前の問題の回避策ではなく、議論しています。それ自体は混乱を助長するだけです。

--%は非常に異なるユースケースであり、現在提案されているように、厳しい制限があります。 _operator_を導入する(または既存の演算子の動作を変更する)価値がない場合は、逐語的なコマンドラインをネイティブシェルに渡すことができます(もちろん、 sh -c '...'を使用してすでに自分で行うことができます)。 cmd /c '...' 、_butだけ確実にこの問題がfixed_を取得する場合、バイナリInvoke-NativeShell / insレット実装、主に離れたターゲットシェルの詳細を抽象化しながら、 CLI構文は、( System.Diagnostics.ProcessStartInfo.ArgumentList直接使用することにより)目前の問題を回避するため、独立して実装できます。

@ yecril71pl

別のシナリオがあります。新しいユーザーがPowerShellを試してみて、不思議なことに失敗したことに気づき、ごみ箱に移動して二度と戻らないというシナリオがあります。 それが私がすることです。

明確にできますか:PowerShellの現在の動作がこの不快なユーザーエクスペリエンスにつながる可能性があることを恐れていますか、または提案された変更がそのユーザーエクスペリエンスにつながることを恐れていますか?

私の意見では、PowerShellの現在の動作は、そのようなエクスペリエンスを生成する可能性がはるかに高いためです。 上で述べたように:PowerShellの現在の動作はバグと見なす必要があります。

PowerShellが壊れていることを知らなくても、新しいユーザーが歪んだ引数を使用してコマンドを発行するのはなぜですか?

@ yecril71pl念のために

あなたの質問は、私が不治のオタクであるというあなたの仮定に由来していると思います。 それは正しいですが、私は通常のランダムなチャップがゆがんでいると見なすものを想像する能力を保持しています。

@ mklement0
@ Artoria2e5は、引数の配列を単一のlpCommandLine文字列に変換することについて話していたと思います

古い呼び出しは、何かがすでに引用されているかどうかを推測することで、cmdlineに直接ではありません

電話するとき

echoargs.exe 'some"complicated_argument'

PowerShellがsome"complicated_argument周りに引用符を追加するかどうかにかかわらず、実際に多かれ少なかれ推測する必要があります。

例1:ほとんどのPowerShellバージョン
echoarg.exe 'a\" b'echoarg.exe '"a\" b"'はに翻訳されます
"C:\path\to\echoarg.exe" "a\" b" (テスト済みバージョン2.0; 4.0; 6.0.0-alpha.15; 7.0.1)
しかし、Win10(バージョン5.1.18362.752)のデフォルトのPowerShellは翻訳されます
echoarg.exe 'a\" b'から"C:\path\to\echoarg.exe" a\" bおよび
echoarg.exe '"a\" b"'から"C:\path\to\echoarg.exe" ""a\" b""

例2:古いPowerShellバージョンは翻訳します
echoarg.exe 'a"b c"'から"C:\path\to\echoarg.exe" "a"b c"" (テスト済みバージョン2.0; 4.0;)
一方、新しいバージョンは翻訳します
echoarg.exe 'a"b c"'から"C:\path\to\echoarg.exe" a"b c" (テスト済みバージョン5.1.18362.752; 6.0.0-alpha.15; 7.0.1)。


動作は明らかにすでに複数回変更されているため、期待される動作を取得するためにもう一度変更できない理由がわかりません。

なるほど、 @ TSlivede 、明確にしてくれてありがとう、そして誤解してすみません、@ Artoria2e5。

本当の問題については、私たちは100%同意しています。実際、Windowsの舞台裏でlpCommandLineが最終的に使用されることを考える必要はありません。 PowerShellが正しいことをした場合、誰もする必要はありません(エッジケースを除いて、PowerShellのせいではありません。それは、 --% (現在実装されている)が役立つ場合です。適切な修正があれば、決してありません。 Unixライクなプラットフォームではエッジケースになります)。

単に問題を適切に修正することに関しては、あなたは確かに私の投票権を持っています(しかし、既存の回避策は壊れます)。

TL; DR:Microsoft Windows NTサブシステムの任意のプログラムに引数として任意の値を確実に渡すことができるという仮定は間違っているので、これが私たちの目標であると偽ることをやめるべきです。 しかし、議論の範囲を考えれば、まだ救われるべきことがたくさんあります。

ネイティブのWindows実行可能ファイルを呼び出すときは、元の引用符を保持する必要があります。 例:

CMD /CSTART="WINDOW TITLE"

システムはファイルWINDOWを見つけることができません。

 { CMD /CSTART="WINDOW TITLE" }. Ast. EndBlock. Statements. PipelineElements. CommandElements[1]

StringConstantType
BareWord
/ CSTART = WINDOW TITLE
StaticType
System.String
エクステント
/ CSTART = "WINDOW TITLE"
CMD / CSTART = "WINDOW TITLE"

エクステントをテンプレートとして使用した場合、何も失われることはなく、期待どおりにネイティブ実行可能ファイルを呼び出すことができます。 文字列引数を使用する回避策はここで機能しますが、PowerShell内に適切なサポートが実装されていれば、厳密に技術的に必要であるとは思いません。 このアプローチはすべての場合に機能します。

バックスラッシュエスケープを解釈するツール( TASKLIST "\\\"PROGRAM FILES" )と解釈しないツール( DIR "\""PROGRAM FILES" /B )と気にしないツール( TITLE A " B )があるため、引用符内の引用符は克服できない問題を提示します。 )。 ただし、エスケープする場合、バックスラッシュを使用した標準のエスケープは、引用符をまったくサポートしていないため、すべての一般的なファイル管理ツールに悪影響を及ぼします。ダブルバックスラッシュ\\は、まったく異なる意味を持ちます( DIR "\\\"PROGRAM FILES" /B試してください)。 CMD /CECHO='A " B' )を提供することはできません。

環境変数はCMD値を表さず、環境変数が展開されるときに再解析されるコードフラグメントを表し、他のコマンドへの引数として確実に扱うための規定がないことに注意してください。 CMDは、文字列でさえも、どの種類のオブジェクトでも動作しません。これが、現在の難問の根本的な原因のようです。

TL; DR:Microsoft Windows NTサブシステム内の任意のプログラムに引数として任意の値を確実に渡すことができるという仮定は_間違っている_ので、これが私たちの目標であると偽ることをやめるべきです。

それが目標であるべきですが、そうではありませんか? プログラムが受け取った引数を解釈できない場合でも、PowerShellの問題ではありません。

ネイティブのWindows実行可能ファイルを呼び出すときは、元の引用符を保持する必要があります。 例:

CMD /CSTART="WINDOW TITLE"

プログラムを呼び出すと、言語がPowerShellから呼び出されたプログラムが使用するものに動的に変更されることを提案していますか? その例をPowerShellで作成しました。つまり、次のいずれかと同等である必要があります。

CMD "/CSTART=WINDOW TITLE"
CMD '/CSTART=WINDOW TITLE'
CMD /CSTART=WINDOW` TITLE

TL; DR:Microsoft Windows NTサブシステム内の任意のプログラムに引数として任意の値を確実に渡すことができるという仮定は_間違っている_ので、これが私たちの目標であると偽ることをやめるべきです。

それが目標であるべきですが、そうではありませんか? プログラムが受け取った引数を解釈できない場合でも、PowerShellの問題ではありません。

プログラムCMDは引数/CECHO=A " B解釈それを歪めはできません

ネイティブのWindows実行可能ファイルを呼び出すときは、元の引用符を保持する必要があります。 例:

CMD /CSTART="WINDOW TITLE"

プログラムを呼び出すと、言語がPowerShellから呼び出されたプログラムが使用するものに動的に変更されることを提案していますか? その例をPowerShellで作成しました。つまり、次のいずれかと同等である必要があります。

CMD "/CSTART=WINDOW TITLE"
CMD '/CSTART=WINDOW TITLE'
CMD /CSTART=WINDOW` TITLE

私は、Microsoft Windows NTサブシステムで外部プログラムとインターフェイスする場合、PowerShellには、すべてPowerShellと同等であるが、受信プログラムとは同等ではない引数をエンコードする無数の方法があることを提案しようとしました。 ユーザーが実際に使用した引用符の配置に注意を払わずに、率直で、引数をエンコードするOne TrueWay™を強制することは、控えめに言っても役に立ちません。

@ yecril71pl私はあなたのコメントに本当に混乱しています。 ここで正確に何を提案していますか? ユースケースはすべて--%カバーされています。 あなたは先に言ってそれを却下しました

--%は事前定義されたコマンドラインが必要なため、そのユーティリティは制限されています。

しかし実際には、 --%環境変数を使用できます。 これを試して:

PS > $env:mytitle='WINDOW TITLE'
PS > cmd --% /CSTART="%mytitle%"

だから私は何が欠けていますか?

ENV:物事を漏らさずに、構文CMD /CSTART="$mytitle"が欠落しています。

ひどい考えとして、 Environment.ExpandEnvironmentVariablesを別のものに置き換えるオプションがあります。 とにかくUnixにはネイティブの実装はなく、C#で書き直したときに、処理するものがパフォーマンスにとって重要になるとは思わない。

とにかく環境変数名で等号は許可されていないので、 %=$a%$a意味することができます。 これは、JSのテンプレート文字列のように機能させるなど、非常に柔軟な(そしておそらく悪い)拡張機能を可能にしながら、既存のものを壊すことはありません。 地獄、 %VARNAME=$var%をある種のフォールバック構文として定義することもできます。

ドキュメントの地獄に関しては、これが引き起こすでしょう...私はお詫び申し上げます。

  • 解析の問題はありません。

  • 私たちが抱えているのは、PowerShellが_its_解析の結果として生じた逐語的で文字列化された引数を外部(ネイティブ)実行可能ファイルに渡す方法の問題です。

    • _Windows_での問題は、バックグラウンドで構築された外部実行可能ファイルを呼び出すコマンドラインが、Microsoft C / C ++コンパイラドキュメントのParsingC ++コマンドで詳しく説明されているように、引数を引用するために最も広く使用されている規則に準拠していないことです。行引数セクション。

    • 現在起こっていることは、_異なる_規則が使用されていることでもありません。おそらく見落としのために、埋め込まれた二重引用符とスペースの組み合わせに関連して、引数の詳細に応じて、構築されるコマンドラインが状況的に_構文的に根本的に壊れています_空の文字列引数も同様です。

    • 最終的に、問題はWindowsでのプロセス作成の基本的なアーキテクチャです。プロセスに渡す引数を、引数の配列として渡すのではなく、コマンドラインとして(すべての引数を表す単一の文字列)エンコードする必要があります。 Unixライクなプラットフォームがそれを行う方法です)。 コマンドラインを渡す必要がある場合は、_引用とエスケープのルール_を実装する必要があり、最終的には、与えられたコマンドラインをどのように解釈するかは各プログラム次第です。 事実上、これはプログラムをある種のミニシェルにすることを不必要に強制することになります。プログラムは、シェルがすでに実行したタスクを_再実行_することを余儀なくされます。これは、_シェルのみ_の範囲内である必要があるタスクです(現状のまま) Unixの場合)、つまりコマンドラインを個々の引数に解析します。 一言で言えば、これはWindowsで受け渡される引数である

    • 実際には、無秩序は前述の規則に準拠する_ほとんどの_プログラムによって軽減され、開発中の新しいプログラムは、主にコンソールアプリケーションを支える広く使用されているランタイムがこれらの規則(Microsoft C / C ++ /など)を実装するため、その規則に準拠する可能性が高くなります。 .NETランタイム)。 したがって、賢明な解決策は次のとおりです。

      • 舞台裏でコマンドラインを構築するときは、PowerShellをこの規則に準拠させます。
      • この規則に準拠していない「不正な」プログラム(特にcmd.exe 、バッチファイル、およびmsiexec.exemsdeploy.exeなどのMicrosoftユーティリティを含む)の場合、明示的にメカニズムを提供します。ターゲット実行可能ファイルに渡されるコマンドラインを制御します。 これは--%であり、解析停止記号が提供するものです-かなり厄介ですが。
    • _Unix_での問題は、_aコマンドラインがまったく構築されていないことです_-代わりに、逐語的な引数の配列を_as-is_ProcessStartInfo.ArgumentList介して;プロセスがUnixライクなプラットフォームで作成される場合、-賢明に-_コマンドラインはなく、引数配列のみが存在することを考えると、_常に_これをサポートしているはずです)。

    • ProcessStartInfo.ArgumentListを使用すると、Unixのすべての問題が解消されます。

これらの問題を修正することが、 @ TSlivedehttps://github.com/PowerShell/PowerShell-RFC/pull/90のすべてです。

https://github.com/PowerShell/PowerShell-RFC/pull/90#issuecomment -650242411で、バッチファイルの「不正」をさらに自動的に補正することを提案しました。これは、高レベルのCLIエントリポイントとしてまだ非常に広く使用されているためです。 -Azureなどのプロファイルソフトウェア(CLI azはバッチファイルaz.cmdとして実装されます)。
同様に、 msiexec.exemsdeploy.exe 、そしておそらく他の有名な「不正な」MicrosoftCLIについても同じことを検討する必要があります。


ie関数i nvoke(external) e xecutableの略)を介して上記のすべてに対処するモジュールNativeInstall-Module Native -Scope CurrentUser )を公開しました。上で紹介したiep関数のより完全な実装です)。

また、#13068に対応するinsInvoke-NativeShell )と、引数の受け渡しを診断するためのdbeaDebug-ExecutableArguments )も含まれています。https:// githubを参照してくださいcom / PowerShell / PowerShell / issues / 13068#issuecomment -671572939を参照してください。

言い換えると、 ieは、この問題が修正されるのを待つ間、コマンドとしてieを呼び出しの前に付けるだけで、目立たない一時停止として機能できます。

の代わりに:

# 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" }'

次を使用します。

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

CMD /CSTART="WINDOW TITLE"例(より慣用的な形式はcmd /c start "WINDOW TITLE"であり、すでに機能しています):

これは本質的に、 msiexec / msdeploy prop="<value with spaces>"引数の場合と同じ問題です。PowerShellは-当然のことながら- /CSTART="WINDOW TITLE""/CSTART=WINDOW TITLE"に変換します。ただし、これはcmd.exe呼び出しを中断します。

これを解決するには、次の2つの方法があります。

  • ins / Invoke-NativeShell委任します( cmd.exe /cは事実上暗示されていることに注意してください):

    • ins 'START="WINDOW TITLE"'
    • _expandable_文字列を使用する場合は、PowerShell値をコマンド文字列に埋め込むことができます。

      • $title = 'window title'; ins "START=`"$title`""

  • または、現在の--%実装を使用しますが、その制限に注意してください。

    • cmd --% /CSTART="WINDOW TITLE"
    • 説明したように、 --%問題のある制限は、_PowerShell_値を埋め込む唯一の方法が_auxを使用することであるということです。 環境変数_そして%...%構文でそれを参照します:

      • $env:_title = 'window title'; cmd --% /CSTART="%_title%"

      • この制限を回避するには、 --%を常に_single_文字列引数で実装する必要があります。

        cmd --% '/CSTART="WINDOW TITLE"'またはcmd --% "/CSTART=`"$title`"" -ただし、これは下位互換性を損なうことなく変更できないため、_new_シンボルを導入する必要があります-個人的には、その必要性はわかりません。

  • シェルがすでに実行したタスクを_再実行_することを余儀なくされます

CMD.EXEがコマンドラインを引数に分割するとは思いません。必要なのは、呼び出す実行可能ファイルを見つけることだけです。残りは、ユーザーが記述したコマンドラインだけです(環境変数の置換後、引数の境界を考慮せずに実行されます)。 もちろん、内部シェルコマンドはここでは例外です。

これは本質的に、 msiexec / msdeploy prop="<value with spaces>"引数の場合と同じ問題です。

私はどちらも自信のないユーザーなので、もっと身近なものを持ち出すことを好みました。

明確にするために:以下は私の前のコメントでなされたポイントに影響を与えません。

CMD.EXEがコマンドラインを引数に分割するとは思わない

  • _external実行可能ファイル_(子プロセス内の別の実行可能ファイルによって実行されるコマンド)を呼び出すときに_explicit_分割なしで回避できますが、_batchファイル_に対して実行する必要があります。

  • 外部実行可能ファイルを呼び出す場合でも、特定のメタ文字( & )に_syntactic_関数があるかどうか、または二重引用符で囲まれた引数の一部であるために処理されるかどうかを判断するために、引数の境界を_認識_する必要があります。リテラルとして:

:: OK - the "..." around & tells cmd.exe to use it verbatim
C:\>echoArgs.exe one "two & three"
Arg 0 is <one>
Arg 1 is <two & three>

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" one "two & three"

また、 cmd.exeは_embedded_ "文字を認識します。 "..."文字列は、 ""としてエスケープされた場合に認識されます。

:: OK - the "" is recognized as an escaped "
C:\>echoArgs.exe "3"" of rain & such."
Arg 0 is <3" of rain & such.>

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" "3"" of rain & such."

残念ながら、 cmd.exe _only_は(Windowsのみの) ""をサポートし、より広く使用されている\"もサポートしていません(これは、UnixのPOSIXのような_shells_が_排他的に_使用するものです-注:プログラムはシェルの解析から生じる逐語的な引数の配列を見るだけなので、_programs_ではなく_shells_)。

Windows上のほとんどのCLIは""\"両方をサポートしていますが、一部の_only_は\" (特にPerlとRuby)を理解しているため、問題が発生します。

:: !! BROKEN: cmd.exe misinterprets the & as *unquoted*, thinks it's the statement-sequencing operator, 
:: !! and tries to execute `such`:
C:\>echoArgs.exe "3\" of rain & such."
Arg 0 is <3" of rain >

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" "3\" of rain

'such."' is not recognized as an internal or external command,
operable program or batch file.

したがって:

  • 可能であれば、 cmd.exe直接呼び出すこと

    • _PowerShellの_構文を使用して、外部実行可能ファイルを直接(この問題が修正されたら)またはie (今のところ)経由で呼び出します。
  • cmd.exeを呼び出す必要がある場合は、 ins / Invoke-NativeShell 、一般的な単純さ、特にPowerShell変数と式の値をコマンドラインに簡単に埋め込むことができます。 。

    • それでもcmd.exe直接呼び出す正当な理由は、PowerShellがパイプラインで生のバイトデータをサポートしていないことを補うためです。例については、このSOの回答を参照してください。

私はここで多くの高射砲を捕まえるつもりだと知っています、そして私は起こっている議論の深さに本当に感謝しています、しかし...アヒル...誰かが実際のシナリオで実際に重要なこのいずれかの例を持っていますか?

現在Windowsの引数解析で存在する「無秩序」を解決する権限がPowerShellにないのは私の考えです。 そして、問題を解決できないのと同じ理由の多くのために、WindowsとVC ++コンパイラがこの動作を壊さないことを選択したのには十分な理由があります。 それは横行しており、物事を変更した場合にのみ、新しい(そしてほとんど解読できない)問題の非常に長いテールを作成します。

すでにクロスプラットフォームであり、WindowsとLinuxの間で頻繁に使用されているユーティリティ(Docker、k8s、Gitなど)の場合、この問題が現実の世界で現れることはありません。

そして、貧弱な仕事をするそれらの「不正な」アプリケーションのために:それらは主にレガシーの、Windowsのみのユーティリティです。

@ mklement0について説明したことは、主に「正しい」解決策であることに同意します。 私は本当に物事を台無しにせずにそこに着く方法がわかりません。

かなり基本的な使用法は壊れます:

❯ git commit --allow-empty -m 'this is what we call a "commit message" which contains arbitrary text, often with punctuation'
error: pathspec 'message which contains arbitrary text, often with punctuation' did not match any file(s) known to git
❯ $a = 'this is what we call a "commit message" which contains arbitrary text, often with punctuation'
❯ git commit --allow-empty -m "$a"
error: pathspec 'message which contains arbitrary text, often with punctuation' did not match any file(s) known to git
❯ $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.0.3
PSEdition                      Core
GitCommitId                    7.0.3
OS                             Microsoft Windows 10.0.19042
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

git.exeは正常に動作するアプリケーションであるため、PowerShellでの修正は簡単ですが、すべてのスクリプターがスマートな回避策を元に戻す必要があります。 cmd.exeは適応するのが難しく、いくつかの問題を解決できるかもしれないが、おそらくすべてではない、はるかに思いやりのあるアプローチが必要です。 PowerShellがWindowsNTツールとして開始されたことを考えると、これは実際には恐ろしいことです。 私はこの質問を_ cmd.exeような動作の悪いレガシーユーティリティがインターフェイスで問題を引き起こす方法でPowerShellから呼び出される実際のシナリオがあるかどうか_として理解しています。 PowerShellは、 cmd.exe冗長にするために、ほとんどの機能をcmd.exeに複製することで、この問題に対処しようとしました。 これは他のツールでも可能です。たとえば、MSIはActiveXを介して操作できますが、そうするにはかなりの知識が必要です。 それで、カバーされていない本質的なものはありますか?

@ PowerShell / powershell-委員会がこれについて議論しました。 実世界の説得力のある例を明確に示しているgitの例に感謝します。 このような重大な変更を行うことの影響を検証するために、7.2の早い段階で実験的な機能を用意する必要があることに同意しました。 追加のテスト例は、 --%さえ、解析されていないはずなのに問題があることを示しています。

PS> testexe --% -echoargs 'a b c "d e f " g h'
Arg 0 is <'a>
Arg 1 is <b>
Arg 2 is <c>
Arg 3 is <d e f >
Arg 4 is <g>
Arg 5 is <h'>

これは、ネイティブコマンドパラメータバインダーの問題のようです。

ええ、@ cspotcodeありがとうございます。 その例は私にとって間違いなくああ瞬間でした(特に私が実際に現実の世界でそれを打ったことを考えると)。

私はまだ重大な変更の側面について懸念しています。これは、PowerShellの複数のバージョンで実験的なままである可​​能性のある実験的な機能の候補になる可能性があると考えています。これは、最終的には確実に実現するものではありません。

また、RFCの許可リスト/「ルージュアプリ」の側面である@ mklement0を理解するために、さらに掘り下げる必要があります。そのようなリストを維持するためにどれだけサインアップしたいかわからないからです。

@joeyaielloと@ SteveL-MSFT、最初にメタ観測を行います。

@cspotcodeの例で一瞥」が

これは_個人的な_判断ではありません。私は、非常に薄く伸ばすことがどれほど難しいか、そして非常に幅広い主題について短時間で決定を下さなければならないことを十分に認識しています。

ただし、これは_構造_の問題を示しています。私には、議論されている問題の表面的な理解に基づいて、@ PowerShell / powershell-committeeによって日常的に決定が行われ、コミュニティ全体に損害を与えているようです。

私にとって、ここで議論されている問題に対する委員会の対応は、これまでのこの構造的問題の最も重要な例です。

したがって、私はあなたにこれを考慮するようにお願いします:

委員会がその_do_と協議する主題固有の_sub_-委員会を任命して、関連する問題について必要な理解を持っているのはどうですか?

testexe SteveL-MSFTのコンテンツを共有できますか?確認したいだけです!

@TSlivedeは、問題をhttps://github.com/PowerShell/PowerShell/issues/13068#issuecomment-665125375に適切に要約してい

一方、PowerShellはシェルであると主張しています(#1995が解決されるまで、それがシェルであるとは言いません)

以前に何度も述べたように、シェルの中心的な任務は、引数を使用して外部実行可能ファイルを呼び出すことです。

現在、PowerShellは、二重引用符が埋め込まれた引数と空の文字列引数が正しく渡されないため、この義務を果たすことができません。

前に述べたように、これは、対応する外部CLIの欠如がこの問題を表面化することはめったになかった、Windowsのみの時代にはそれほど問題ではなかったかもしれませんが、最近はなくなり、PowerShellが信頼できるクロスプラットフォームとしての地位を確立したい場合シェル、それはこの問題に対処する必要があります。

@cspotcodegit例は良い例です。 JSON文字列を渡す実行可能ファイル( curl )は別のものです。

# On Unix; on Windows, 
#   echoArgs.exe '{ "foo": "bar" }' 
# would show the same problem.
PS> /bin/echo '{ "foo": "bar" }'
{ foo: bar }  # !! Argument was incorrectly passed.

下位互換性はさておき:

  • Unixでは、この問題は、舞台裏でProcessStartInfo.ArgumentListを使用することで、簡単かつ_完全に_解決されます。

  • Windowsでは、舞台裏でProcessStartInfo.ArgumentListを使用することで、問題は簡単に解決されます。

    • エッジケース(「不正な」CLI)の場合、(実装が不十分な--%
    • _礼儀_として、特定のよく知られたエッジケースを補正して、 --%の必要性を減らすことができます-以下を参照してください。

したがって、_できるだけ早く_、次のいずれかの選択を行う必要があります。

  • 引数の受け渡しを適切に機能させることの重要性を認識し、_下位互換性を犠牲にして修正_します。

  • 下位互換性が本当に重要な場合は、問題を修正する新しい演算子またはie関数などの関数をNativeモジュールから提供し、外部を呼び出す唯一の信頼できる方法として広く公表します。実行可能ファイル。

ひどく壊れた基本的な機能に対処するために_実験的な_機能を提案することは完全に不十分です。


@ SteveL-MSFT

この問題の解決策として--%使用を検討することでさえ、根本的に誤った方向に進んでいます。

これは、 "..."引用符と%...%スタイルの環境変数参照のみを知っている_Windowsのみの_機能です。

Unixでは、「解析の停止」の概念は基本的に適用されません。子プロセスに渡すコマンドラインはなく、引数の配列のみです。

したがって、_someone_はコマンドラインを引数_before_ invocationに解析する必要があります。これは、Unixではコマンドラインの解析に_Windows_規則を使用する.Argumentsプロパティを介して、 ProcessStartInfoクラスに暗黙的に委任されます。 -したがって、 "..."引用符(埋め込まれた"""または\"としてエスケープする)のみを認識します。

--%はWindows専用の機能であり、その正当な目的は「不正な」CLIを呼び出すことだけです。


@joeyaiello

WindowsとVC ++コンパイラは、この動作を壊さないことを選択しました。

VC ++コンパイラは、無政府時代に秩序をもたらすために、

ここProcessStartInfo.ArgumentListすると自動的に得られます。

_これだけで、通話の大部分をカバーします。 すべての呼び出しをカバーすることは不可能であり、PowerShellの責任ではありません。_

前述のように、従来とは異なる形式の引用を必要とする「不正な」CLIの場合、 --%使用する必要があります(またはNativeモジュールのins / Invoke-NativeShell )。

_礼儀として_、よく知られている「不正な」シナリオ、つまりバッチファイルや特定の有名なMicrosoftCLIの呼び出しを自動的に補正できます。

  • バッチファイルの場合は一般的なものであり、簡単に説明および概念化できます(たとえば、引用符を必要としない場合でも、 a&b"a&b"として渡す)-使用する必要がなくなりますの--%すべてのCLIとなるようアズールの、それらのエントリポイントとして使用するバッチファイル(これは非常に一般的です)、 az.cmd

  • 特定のCLIの例外をハードコーディングする代わりに(明らかに混乱する可能性があります)、PowerShellの解析の結果として生じる引数で次の_pattern_を検出することです- <word>=<value with spaces> -そして、 "<word>=<value with spaces>"を渡す代わりに、現在のように、 <word>="<value with spaces>"を渡す; 後者は「不正な」CLIを満たしますが、規則に準拠したCLIでも受け入れられます。 たとえば、 echoArgs "foo=bar baz"は、最終的にechoArgs --% foo="bar baz"と同じ最初の引数を認識します。

@musm TestExeのソースコードはhttps://github.com/PowerShell/PowerShell/blob/master/test/tools/TestExe/TestExe.csにあります。

GitHub
すべてのシステムにPowerShell! GitHubでアカウントを作成して、PowerShell / PowerShellの開発に貢献します。

デフォルトで例外に対応することは、PowerShellの「有用性」を元に戻す必要がある現在の状況と同様の状況につながると思います。 例外がある場合は、それらが適用されていることは明らかです。

多分次のようなものです:

# Arguments passed correctly, without regard for the program's ability to handle them
& $program a "" 'c "d e" f'
# Try to pass the arguments intelligently based on the program being called
&[] $program a "" 'c "d e" f'
# Escape the arguments for a batch file, eg) " -> ""
&[bat] $program a "" 'c "d e" f'

壊れていないこの構文を見つけるのに本当に苦労しています。 プログラムをキャストすることと考えると、少なくともこの種の意味はありますが、プログラムを含む実際の変数をキャストするには、括弧で囲む必要があります。

それは、人々が望むどんな壊れた振る舞いに対しても例外を追加できるようにすることに加えて、うまくいけば--%必要性を排除するはずです。 登録する例外クラスには、プログラムに適用できるかどうかを判断するためのメソッド(インテリジェントな呼び出し用)と、コマンド抽象構文ツリーをスローするだけで引数の配列/文字列を返すエスケープメソッドがあります。

  • 現在行われているように"<word>=<value with spaces>"を渡す代わりに、 <word>="<value with spaces>"を渡します

コマンドラインの作成に使用される引用符は、PowerShellでの呼び出しの引用符の方法に従う必要があります。 したがって:

  1. テキストに二重引用符が含まれていない呼び出しでは、値全体を二重引用符で囲む必要があります。
  2. 二重引用符を含む呼び出しは、可能であればスクリプトに記述されているとおりに保持する必要があります。

特に:
| スクリプト引数| コマンドライン|
| ----------------- | ---------------- |
| p=l of v | "p = l of v" |
| p=l` of` v | "p = l of v" |
| p="l of v" | p = "l of v" |
| p="l of v"'a s m' | p = "l of v" a "sm" |
| p="l of v"' s m' | p = "l of vsm" |

最後の行は、元の二重引用符を保持できない例を示しています。

私はここで多くの高射砲を捕まえるつもりだと知っています、そして起こっている議論の深さに本当に感謝しています、しかし..._アヒル_...誰かが実際のシナリオで実際に重要なこのいずれかの例を持っていますか?

Powershell

rg '"quoted"'

そしてgitbashではそうではありませんでした。

悲しいことに、この長いgithubの問題を見つけ、 "をPowershelに渡すことが完全に壊れていることがわかったため、このWTFの瞬間が少なくなりました。 最近の「git.exe」の例も素晴らしいです。

正直なところ、パラメーターとして文字列で"を渡す可能性があることがわかっている場合は、Powershellを使用してネイティブコマンドを呼び出すことさえあえてしません。 間違った結果やエラーが発生する可能性があることはわかっています。

本当に、 @ mklement0はそれを素晴らしいものにまとめました(これはどこかに石に刻まれているはずです)

以前に何度も述べたように、シェルの中心的な任務は、引数を使用して外部実行可能ファイルを呼び出すことです。
現在、PowerShellは、二重引用符が埋め込まれた引数と空の文字列引数が正しく渡されないため、この義務を果たすことができません。
前に述べたように、これは、対応する外部CLIの欠如がこの問題を表面化することはめったになかった、Windowsのみの時代にはそれほど問題ではなかったかもしれませんが、最近はなくなり、PowerShellが信頼できるクロスプラットフォームとしての地位を確立したい場合シェル、それはこの問題に対処する必要があります

そして、変化を壊すことについて。
最近、同僚から、私のスクリプトが自分のマシンでは機能しないとの連絡がありました。 私はPowershelCoreでのみ実行しており、彼はWindowsPowershellで実行していました。 Windows Powershellでは「BOM」があり、Powershel CoreではBOMがない、 Out-File -Encoding utf8エンコードされたファイルが見つかります。 これはどういうわけか容赦のない例ですが、Powershelにはすでに微妙な重大な変更があり、それで有名な言語から癖や直感的な動作を排除しているので、これは良いことです。 Windowsの外部に出荷されているクロスプラットフォームのPowershellがあり、Windows Powershellが「メンテナンス」モードであり、次の場合は永久に使用できることがわかっているので、Powershelチームが変更を壊すという点でもう少し寛大であれば素晴らしいと思います。新しいバージョンのPowershellで壊れた古いスクリプトを実行する必要があります。

RE:変更を壊す最後のポイント-私は完全に同意します。 さまざまな理由で許容できる重大な変更が多数あります。 しかし、多くの場合、いくつかの重大な変更は、好みの理由で単に嫌われ、実際の値を適切に考慮していない場合があります。

このような変更がいくつかあり、PowerShellの外部にアクセスして作業を行う必要がある場合は、シェルエクスペリエンス全体が_大幅に_改善されます。これは常に発生します。 現在の振る舞いは受け入れがたいものであり、最も単純な使用法以外ではすでに大部分が壊れていることが何度も合意されています。 それでも、すでに受け入れられている重大な変更が多数あり、その一部は同様に大きな影響を及ぼしているにもかかわらず、私たちは依然として重大な変更に対するこの躊躇に直面しています。

例を求めている人のために-スタックオーバーフローに一度アクセスするのに少し時間がかかります。 @ mklement0には、新しいバージョンでの重大な変更を説明するためにコミュニティの支援が必要な例がたくさんあると確信しています。 それは_常に_起こります。 有益な重大な変更を行わない理由はありません。

MSFTチームが同じことを何度も繰り返すときはいつでも、公に言うことができる以上のことを彼らが知っていると確信できます。 私たちは彼らの内なる規律を尊重し、彼らに圧力をかけるべきではありません。 _妥協点を見つけることができるかもしれません。_今日は、怠惰な移行を伴う代替パスについて説明する時間があればいいのですが。

私はそれを認識しています、そしてそれが私がそれを疑うことをめったにしない理由です。

ただし、これはオープンソースプロジェクトです。 それらの決定を可視化する可能性がない場合、人々は必然的にイライラすることになります。 そのコインのどちらの側にもキャストする責任はありません、それはここでの状況の現実です、IMO。 そうですね、移行パスがあるとその苦痛は多少緩和されるかもしれませんが、できるだけ多くの人々のために物事が機能するように、それがどのように機能する必要があるかについて定義された明確なポリシーが必要です。 ただし、情報が不足していると、妥協点に到達するのは困難です。

私はあなたがあなたの袖に何を持っているか見るのを楽しみにしています。 😉

@ mklement0あなたは絶対に正しいです、そして私が今あなたのメタポイントにしか応答できないほどです。 残念ながら、このような質問に答えるのに必要な深さのレベルに到達できない場合、より安全なアプローチは、多くの場合、時間がかかるまで、重大な変更を延期または拒否することです。

ただし、重大な変更について別のメタポイントを作成したいと思いほとんどのPowerShell7ユーザーが独自のバージョンを管理していないことを意味します。 ユーザーを6.2から7.0にアップグレードするなど、快適な管理環境で自動スクリプトを実行しています(8/3から6.2ユーザーが7.0ユーザーになる2日間のジャンプを参照してください。これは、ここでの唯一のデータポイントではありませんが、それは今のところ便利なものです)。 これらのユーザーにとって、完全に機能するスクリプトを機能しないスクリプトに変えるような重大な変更は受け入れられません。

また、コミュニティに、ブレークの変更の影響についてどのように考えているかについてのブログを借りています。つまり、ブレークの識別と修正の容易さに対して、既存の使用の普及とブレークの重大度をトレードオフします。 これは既存のスクリプトで非常に一般的であり、識別と修正が混乱します。破壊的な動作は完全な成功から完全な失敗までであるため、ここで何かを行うことは非常に控えめです。

7.1ではここでは何もしないと言っても過言ではありませんが、これを7.2の調査の優先事項にすることは間違いなくオープンです(つまり、委員会の時間以上にこれについて話し合うことになります)。

委員会が協議する主題別の小委員会を任命して、関連する問題について必要な理解を持っているのはどうですか?

現在取り組んでいます。 前にも言ったことは知っていますが、私たちは非常に親密です(ブログを作成しているので、すぐにいくつかの新しいレーベルが登場するのを目にするでしょう)。

皆様のご理解とご協力に感謝申し上げますとともに、議論に多大な配慮と配慮を注いでいる数週間ごとに、委員会からささいな返事をいただくのは煩わしいことです。 それは私たちが物事について深く考えていないことを意味しているように見えることは知っていますが、私たちは人々がここで行うほど詳細に議論の深さを表現していないだけだと思います。 私自身のバックログには、委員会内からの意思決定についての考え方に関する最新の変更など、一連のブログトピックがありますが、座ってそれらを排除する機会を得たことがありません。 しかし、私はここで、おそらく人々がその中に多くの価値を見いだすであろうことを見ることができます。

この議論で私がレールから離れすぎないことを願っています。 この問題がプロジェクトの管理に関するメタ問題になることを完全に望んでいませんが、ここで見られる理解できるフラストレーションのいくつかに対処したいと思いました。 これについてもっと詳しく話したい人は、来週のコミュニティコールに参加することをお勧めします(ここに質問や考えを追加してください。コールで必ず対処します)。

メタポイントについて簡単に説明します。思いやりのある応答、@ joeyaielloに感謝します。

重大な変更の重大度について:次のステートメントは対立しているようです。

誰かが実際のシナリオで実際に重要なこのいずれかの例を持っていますか

対。

これは既存のスクリプトで非常に普及しており、識別と修正が混乱します

それがすでに蔓延している場合、特にケースの数が増えると予想されることを考えると、必要な回避策の厄介さと曖昧さが最終的にこれを修正する理由です。

私は、既存のすべての回避策が機能しないことを認識しています。

それを回避することが最も重要な場合は、以前に提案されたこの

問題を修正するNativeモジュールのie関数などの_new演算子または関数_を提供し、外部実行可能ファイルを呼び出す唯一の信頼できる方法として広く公表します。

ieなどの_function_を使用すると、_language_に新しい構文要素(演算子)を負担することなく、_minimal fuss_を_stopgap_として正しい動作にオプトインできます。その唯一の存在理由は次のとおりです。壊れすぎて修正できないと思われるレガシーバグを回避するには:

  • 一時的なギャップは、正しい動作への_公式に認可された_アクセスを提供します(_実験的な_機能に依存しません)。
  • 一時的なギャップが必要である限り、それは広く公表され、適切に文書化される必要があります。

デフォルトの動作が修正された場合:

  • 関数は、それを使用するコードを壊さないように、それに従うように変更できます。
  • 関数を必要とせずに新しいコードを書くことができます。

ieなどの機能を使用すると、一時的なギャップとして、最小限の手間で正しい動作にオプトインできます。

#13428を使用すると、採用を簡素化できます。 これは、 @ mklement0のエンジンでの調査で透過的に注入できます。

@Dabombber

デフォルトで例外に対応することは、PowerShellの「有用性」を元に戻す必要がある現在の状況と同様の状況につながると思います。 例外がある場合は、それらが適用されていることは明らかです。

私が提案している調整は、大部分の呼び出しを「正しく機能する」ものにします。これらは、ユーザーを保護するために最善を尽くす必要があるWindowsコマンドラインアナーキーからの有用な抽象化です。

正当な仮定は、このシールドは_legacy_実行可能ファイルの1回限りの作業であり、新しく作成されたものはMicrosoft C / C ++の規則に従うというものです。

ただし、すべての場合にそれを行うことは不可能です。 自動的に対応できない場合は、 --%ます。

個人的には、特定のユーティリティfooがたまたまfoo.batまたはfoo.cmdとして実装されているかどうか、またはfoo="bar none"が必要かどうかについて考える必要はありません。特に"foo=bar none"も受け入れません。これは、規則に準拠した実行可能ファイルの場合は同等です。

また、 &[bat]などのさまざまな例外に対して個別の構文形式は必要ありません。
代わりに、 --%は、(Windowsのみの)キャッチオールツールであり、ターゲットプログラムの特定の型破りな要件が何であれ、渡したいとおりにコマンドラインを作成します。

具体的には、提案された宿泊施設は次のとおりです。

注意:

  • 前述のように、これらは_Windowsでのみ_必要です。 Unixでは、すべての問題を解決するには、 ProcessStartInfo.ArgumentListに委任するだけで十分です。

  • 少なくとも大まかに言えば、これらの調整は概念化と文書化が容易です。

  • これらは、(Windowsのみの)プロセスへの変換コマンドラインステップで、PowerShellの通常の解析の後に適用されることに注意してください。 つまり、PowerShell自体のパラメーター解析は関与しません-そして、@ yecril71plであるべきではありません。

  • これらの宿泊施設でカバーされていない本当にエキゾチックなケースは、ユーザー自身が処理する必要があります。
    --% -または、 Nativeモジュールがインストールされている場合、 ins / Invoke-NativeShellを使用すると、PowerShell変数と式の値を呼び出しに簡単に埋め込むことができます。

    • NativeモジュールのdbeaDebug-ExecutableArguments )コマンドは、最終的に使用されるプロセスコマンドラインの診断と理解に役立ちます。以下のサンプルセクションを参照してください。

宿泊施設のリスト:

  • バッチファイルの場合(前述のように、このケースに自動的に対応することの重要性は、Azureのaz.cmdなど、バッチファイルをエントリポイントとして使用する注目度の高いCLIの普及です)。

    • 埋め込まれた二重引用符がある場合は、すべての引数で( \"ではなく) ""としてエスケープされます。
    • スペースが含まれていないが、いずれか二重引用符または含まれている任意の引数cmd.exeのようなメタ文字& (PowerShellは、デフォルトでのみ二重引用符でスペースを引数を囲むのに対し)、二重引用符で囲まれています。 たとえば、PowerShellによってa&bと見なされる逐語的な引数は、バッチファイルに渡されるコマンドラインに"a&b"として配置されます。
  • msiexec.exe / msdeploy.execmdkey.exeなどの注目度の高いCLIの場合(ハードコーディングの例外なし):

    • 次の形式の引数を少なくとも1つ含む呼び出しは、以下に説明する動作をトリガーします。 <word>は、文字、数字、およびアンダースコアで構成できます。

      • <word>=<value with spaces>
      • /<word>:<value with spaces>
      • -<word>:<value with spaces>
    • そのような議論が存在する場合:

      • 埋め込まれた二重引用符がある場合は、すべての引数で( \"ではなく) ""としてエスケープされます。 -これを行うべきではない理由については、 https: //github.com/PowerShell/PowerShell/pull/13482#issuecomment-677813167を参照して<value with spaces>"文字が_埋め込まれているというまれなイベントで<value with spaces>--%使用する必要があることを意味します。 例えば、
        msiexec ... --% PROP="Nat ""King"" Cole"
      • <value with spaces>部分のみが二重引用符で囲まれ、引数全体は含まれません(後者は、PowerShellがデフォルトで実行することです)。 たとえば、PowerShellでfoo=bar noneと見なされる逐語的な引数は、プロセスのコマンドラインで( "foo=bar none"ではなく) foo="bar none"として配置されます。
    • 注意:

      • ターゲットの実行ファイルが_not_があることを起こる場合はmsiexecスタイルCLI大会付着のCLIは、賢明に検討しているため、害は、行われません<word>="<value with spaces>""<word>=<value with spaces>" _equivalent_、逐語的に表すの両方<word>=<value with spaces>

      • 同様に、実行可能ファイルの大部分は、埋め込み"文字をエスケープするために""交換可能に\"を受け入れます。ただし、PowerShell自体のCLI、Ruby、およびPerlは例外です(パフォーマンスは_not_少なくともPowerShellCLIが呼び出されている場合は、調整する価値がありますが、RubyとPerlをハードコーディングすることも理にかなっていると思います)。 https://github.com/PowerShell/PowerShell/pull/13482#issuecomment -677813167は、 CommandLineToArgvW WinAPI関数を使用するすべてのアプリケーションが""エスケープをサポートしていないことを示しています。

Windows上の他のすべてのケースもで扱うことができProcessStartInfo.ArgumentList暗黙的に適用される、マイクロソフトC / C ++条約(特に意味\"するために" -エスケーピングを)。


Nativeモジュールの現在のバージョン( 1.0.7 )のie関数は、 PowerShellバージョン3以降壊れた引数の解析を修正することに加えて)しますInstall-Module Native )。

私はあなたとここにいるすべての人に、それが外部実行可能呼び出しの大部分に対して「正しく機能する」という主張をテストするためにそのペースを試してみることを勧めます

現在避けられない制限:

  • 注:これらの技術的な制限は、 ieが_function_として実装されていることに起因します(エンジン自体を適切に修正しても、これらの問題は発生しません)。

    • $LASTEXITCODEはプロセスの終了コードに適切に設定されていますが、 $?は常に$true -この機能を追加しても、ユーザーコードは現在$?明示的に設定できません緑色に点灯しています-https ください。 残念ながら、これは現在、 &&||&&パイプラインチェーン演算子でie意味のある形で使用できないことを意味します。
      ただし、ゼロ以外の終了コードを検出するスクリプトを_aborting_する必要がある場合は、 ieeラッパー関数を使用できます。

    • 引数としての--は、PowerShellパラメーターバインダーによって常に「食べられ」ます。 --をターゲット実行可能ファイルに渡すには、単に_twice_を渡します( foo -- --代わりにfoo -- )。

    • ,の引用符で囲まれていないトークンは、配列として解釈されて複数の引数として渡されないように引用符で囲む必要があります。 例えば、パス'a,b'ではなくa,b ; 同様に、 -foo:bar (名前付きのPowerShell引数のように見えるもの)を'-foo:bar'として渡します(これは必須ではありませんが、バグが原因です:#6360); 同様に '-foo.bar must be passed as ' -foo.bar'`(外部実行可能ファイルへの直接呼び出しにも影響する別のバグ:#6291)

  • この関数はPowerShell_Core_で堅牢に機能することを期待しています。 時間の経過に伴う_WindowsPowerShell_の変更により、適切に処理されないエッジケースが発生する可能性がありますが、私は2つしか認識していません。

    • msiexecスタイルのCLIの部分引用符は、バージョン3および4では適用できません。これらのバージョンでは、引数全体が追加の二重引用符で囲まれているためです。 ただし、v5.1では機能します。

    • "" -escapingは、問題を回避するためにデフォルトで使用されますが、 \"が必要な場合(PowerShell CLI、Perl、Ruby)、 3" of snowなどのトークンはすべてのWindowsPowerShellバージョンは、そのような引数を二重引用符で囲むことを怠っているため、誤って_3_引数として渡されました。 これは、先頭にスペース文字がない、頭文字以外の"文字を含む引数で発生するようです。


例、Windows10上のPowerShellCore 7.1.0-preview.5からの出力:

注: dbeaDebug-ExecutableArguments )関数は、外部の実行可能ファイル/バッチファイルが引数を受け取る方法を示すために使用されます。

現在の壊れた引数の受け渡し:

  • 規則に準拠したアプリケーション(デフォルトでdbeaバックグラウンドで使用する.NETコンソールアプリケーション)の呼び出し:
# Note the missing 2nd argument and the effective loss of embedded double quotes,
# due to the embedded " chars. not having been escaped.
PS> dbea -- 'a&b' '' '{ "foo": "bar" }'

2 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <{ foo: bar }>

Command line (helper executable omitted):

  a&b  "{ "foo": "bar" }"
  • バッチファイルの呼び出し:

-UseBatchFileを使用して、代わりにdbeaが引数をヘルパー_バッチファイル_に渡すようにすることに注意してください。

# Note that only *part of the first argument* is passed and that the `&` is interpreted as cmd.exe's
# statement separator, causing `b` to be run as a command (which fails).
PS> dbea -UseBatchFile -- 'a&b' '' '{ "foo": "bar" }'

1 argument(s) received (enclosed in <...> for delineation):

  <a>

'b' is not recognized as an internal or external command,
operable program or batch file.
  • msiexecスタイルのCLIを呼び出す、 cmdkey.exe
# The call fails, because `cmdkey.exe` requires the password argument to 
# to be quoted exactly as `/password:"bar none"` (double-quoting of the option value only), 
# whereas PowerShell - justifiably - passes `"/password:bar none"` (double-quoting of the whole argument).
PS> cmdkey.exe /generic:foo /user:foo /password:'bar none'

The command line parameters are incorrect.

ieの問題の修正:

dbea呼び出しで-ieを使用していることに注意してください。これにより、呼び出しにieれます。

  • 規則に準拠したアプリケーション(デフォルトでdbeaバックグラウンドで使用する.NETコンソールアプリケーション)の呼び出し:
# OK
# Note that the empty 2nd argument is correctly passed, and that \" is used for embedded "-escaping.
PS> dbea -ie -- 'a&b' '' '{ "foo": "bar" }'

3 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <>
  <{ "foo": "bar" }>

Command line (helper executable omitted):

  a&b "" "{ \"foo\": \"bar\" }"
  • バッチファイルの呼び出し:
# OK
# - `a&b` was enclosed in "...", due to the presence of metacharacter `&`
# - "" is used for escaping of embedded " chars.
# Note that `echo %1`, for instance, prints the argument exactly as passed on the command line, including quoting.
# `echo %~1` strips the surrounding double quotes, but embedded escaped ones still print as "".
# However, if you pass these arguments (`%*`) through to convention-compliant CLIs, they are parsed correctly.
PS> dbea -ie -UseBatchFile -- 'a&b' '' '{ "foo": "bar" }'

3 argument(s) received (enclosed in <...> for delineation):

  <"a&b">
  <"">
  <"{ ""foo"": ""bar"" }">
  • msiexecスタイルのCLIを呼び出す、 cmdkey.exe
# The call now succeeds, because `ie` ensure the value-only double-quoting that cmdkey.exe requires.
# (Use `cmdkey /del:foo` to remove the credentials again.)
PS> ie cmdkey.exe /generic:foo /user:foo /password:'bar none'

CMDKEY: Credential added successfully.

値のみの二重引用符が実際のコマンドラインでdbea介して適用されたことを示すには:

PS> dbea -ie -- cmdkey.exe /generic:foo /user:foo /password:'bar none'

  <cmdkey.exe>
  </generic:foo>
  </user:foo>
  </password:bar none>

Command line (helper executable omitted):

  cmdkey.exe /generic:foo /user:foo /password:"bar none"

次のコードはデータ損失を引き起こします:

{ PARAM($A) $A } | OUT-FILE A.PS1
PWSH A.PS1 -A:(1,2)

1

@JamesWTruherには修正案があり、この問題で提起された懸念に対処しているかどうかを検証しています

その提案された修正のプルリクエストはありますか? PRについてコメントしていただければ幸いです。 これを修正するのはIMOだったので、決して複雑な部分ではありませんでした。 複雑な部分は、後方互換性をどのように処理するかでした。 そして、提案された修正がそれをどのように処理するかを見ることができれば、それは素晴らしいことです。

@ SteveL-MSFT-ここで説明されているすべての問題の修正ですか? v7.1の場合、結局のところ? そして、 @ TSlivedeの2番目のリクエストです。

@ yecril71pl 、これは良い発見ですが、これはPowerShell自体の解析(外部実行可能ファイル用の特別なケースがあります)に関連しており、ネイティブコマンドラインの解析後の構築方法(前述の問題が発生する場所)には関連していませんから)。

Unixでの問題のより簡潔な再現:

PS> printf '<%s>\n' -a:(1,2,3)
<-a:1>
<2>
<3>

つまり、_first_配列要素のみが-a:に直接アタッチされ、他の要素は個別の引数として渡されました。

PowerShellパラメーターのように見える引数には関連する問題がありますが、そうではありません。

$args / @argsを使用する_PowerShell_コマンドの呼び出しにのみ影響する関連する問題があります:#6360

  • & { $args.Count; $args } -foo:bar2, '-foo:', 'bar'

PowerShellコマンドと外部実行可能ファイルの両方に影響する#6291もあります( .注意してください)。

  • & { $args.Count; $args } -foo.bar2, '-foo', '.bar'

注意すべき点の1つは、ベアワードの一部としての(...)は、通常、 (...)の出力全体が_separate_引数になるため、最初の要素がprintf付加されているという事実です。
& { $args.Count; $args.ForEach({ "$_" }) } foo('bar', 'baz')2, 'foo', 'bar baz'を生成し、2番目の引数は配列'bar', 'baz'の文字列化です。

PowerShellが外部実行可能ファイルに-A:(1,2)を渡す必要がある場合、 -A:は文字列であり、 (1,2)は配列であり、「12」としてマーシャリングする必要があることがわかります。 PowerShellは呼び出しの元の構文を保持しようとするため、すべてをまとめると、「-A:1 2」が得られますが、正しい結果は「-A: "12"」になります。 私には、マーシャリングコードの些細な省略のように見えます。

@ yecril71plの特定の問題が解析に関連しているとは言いません(ただし、この問題で説明されている「配列をコマンドラインに変換する」問題とは何の関係もありません)。

PowerShellが外部実行可能ファイルに-A:(1,2)を渡す必要がある場合、-A:は文字列であり、(1,2)は配列であることがわかります。

ほとんど: -A:は名前付きパラメーターであり、配列はそのパラメーターの値です(これをテストするには、前の-を削除すると、引用が異なることがわかります)。 しかし、問題は、配列が誤って文字列に変換されることではありません-問題は、ネイティブ実行可能ファイルの場合、 @ではなく$を使用している場合でも、引数が(ほとんど)常にスプラットされることです。配列が(1,2)などの式に由来する場合でも。

たとえば、 printf '<%s>\n' -a:('a b',2)テストします。文字列a bにはスペースが含まれているため、正しく引用されますが、 2は次の配列要素にあり、配列はスプラットされているため、 2は最初の引数の一部ではありません。


魔法はNativeCommandParameterBinder.csで起こります

170行目で、 PowerShellは現在の引数値の列挙子を取得しようとします。

IEnumerator list = LanguagePrimitives.GetEnumerator(obj);

listnull場合、PowerShellはリストの各要素(スペースが含まれている場合は引用符で囲まれている可能性があります)をlpCommandLineに追加します。

デフォルトでは、要素はスペースで区切られています( 449行目)。 唯一の例外は、配列がリテラルであった場合です
printf '<%s>\n' -a:1,2 )。
次に、PowerShellは、スクリプト行で使用されたものと同じセパレーターをlpCommandLineで使用しようとします。

準備ができたらPRを期待しています。 7.1になる場合はそれを採用し、そうでない場合は7.2になります。 下位互換性は彼が取り組んでいるものです。 おそらく役立つのは、Pesterテストの作成に役立つことです(build.psm1のpublish-pstesttoolsを使用してビルドできるtestexe -echoargsを使用)。

準備ができたらPRを期待します

それはまさに私が避けたかったことです-準備ができていないコードを見せてください(PRを進行中の作業としてマークしてください)。

または少なくともコメント、彼がやりたいこと。

彼が下位互換性をどのように処理したい

@ TSlivede 、PowerShell _CLI_が呼び出されるため、外部実行可能ファイル- -A:(1,2)は、このトークンが最終的に_named_ -Aパラメーターにバインドされることを知って_前に解析されることに注意してください-そのようなパラメーターは_最終的に_機能します問題に付随するものです。

@ yecril71pl

-A:が文字列であることがわかります

いいえ、PowerShellパラメーターのように見えるため、解析中に特殊なケースになります。

この特殊なケースは、(バインドされた実際の宣言されたパラメーターを持つのではなく) $argsを使用するPowerShellコマンドの呼び出しでも発生しますが、外部実行可能ファイルでは「異なる方法」で発生します(通常は別個の引数がアタッチされたままです。ただし、コレクションの場合は、その_first_要素のみ)。

事前に--を渡すと、実際にはこの特別なケースをオプトアウトできますが、もちろん、 --も渡されます。これは、_PowerShell_コマンドの呼び出しでのみ削除されます。

PS> printf '<%s>\n' -- -a:(1,2,3)
<-->   # !! not removed
<-a:>
<1>    # array elements are *all* now passed as indiv. arguments, because (...) output is separate (splatted) argument
<2>
<3>

引数がPowerShellパラメーターのように見えない場合、通常の動作( (...)からの出力は別の引数になります)は、外部の実行可能ファイル(_array_が飛び散る、つまり外部実行可能ファイルの場合の個々の引数)。

# Note: No "-" before "a:" -> output from `(...)` becomes separate argument(s)
PS> printf '<%s>\n' a:(1,2,3)
<a:>
<1>
<2>
<3>

この振る舞いを_一貫して_適用することは理にかなっています-ベアワードの一部としての(...)式は_常に_別の引数になるはずです-#13488を参照してください。

配列_stringified_で単一の引数'-A:1 2 3'渡すには、(n陰的)_expandable string_を使用します。この場合、 (...) _and_ではなく$(...)が必要です-驚くべきことに-現在も"..."

PS> printf '<%s>\n' "-a:$(1,2,3)"  # quotes shouldn't be needed; `-a:"$(1,2,3)"` would work too.
<a:1 2 3> # SINGLE argument with stringified array.

この場合、 "..."も必要ありません。これも、パラメーターのように見えるトークンに関連する異常のために必要です(これは、一般に、PowerShellと外部実行可能呼び出しの両方に適用されます。#13489を参照してください)。 そうでない場合は、引用する必要はありません。

# Anomaly due to looking like a parameter: $(...) output becomes separate argument
PS> Write-Output -- -a:$(1,2,3)
-a:
1
2
3

# Otherwise (note the absence of "-"): no quoting needed; treated implicitly like 
# "a:$(1,2,3)"
PS> Write-Output -- a:$(1,2,3)
a:1 2 3  # SINGLE argument with stringified array.

引数モードの複合トークンの世界は複雑で、いくつかの矛盾があります。#6467を参照してください。

@ SteveL-MSFT

現在の形式では、 testexe -echoArgsは、.NET Core実行可能ファイルが(Windowsの場合)rawコマンドラインから解析した個々の引数のみを出力し、rawコマンドライン自体は出力しません。

したがって、バッチファイルとmsiexecスタイルのCLIの選択的引用を使用して調整をテストすることはできません。このような調整が実装されると仮定すると、強くお勧めします。 たとえば、 PROP='foo bar'PROP="foo bar"として渡されたことを確認することはできず、値の部分を二重に引用します。

しかし、生のコマンドラインを印刷するために、 testexe .NETのコアが常に使用する架空のコマンドline_を_recreatesので、.NET _Core_実行可能でなければなりません\"組み込み用-エスケーピングを"文字、 ""が使用された場合でも、通常、どの引数が二重引用符で囲まれ、どの引数が使用されなかったかを忠実に反映していません。背景については、 https://github.com/を参照して

.NET _Framework_でコンパイルされた実行可能ファイルのみがEnvironment.CommandLineに実際のコマンドラインを表示するため、 testexeはそのようにコンパイルする必要があります(そして(オプションで)生のコマンドラインを出力するように変更します)。

バッチファイルの調整をテストするには、別のテスト_batch_ファイルが必要です。これは、 'a&b'"a&b"として渡され、 'a"b'"a""b"として渡されることを確認するためです。インスタンス。

.NETFramework用の.NETFrameworkで実行する必要があります。 PSリポジトリからすべてのネイティブコードコンパイルを意図的に削除しましたが、再び追加したいとは思いません... 1つのオプションは、ビルド済みのネイティブtestexe(Windows、macOS、さまざまなLinuxディストリビューション(Alpineを個別に含む)。testexeの作成は簡単で、公開するためのすべての作業を行うには時間がかかります...

あるいは、Linux / macOS用の単純なbashスクリプトに依存して引数を出力することはできますか?

#!/bin/bash
for i; do
   echo $i
done

そして、Windowsではバッチファイルと似たようなものです。

.jsスクリプトでノードを使用するのはどうですか?

console.log(process.execArgv.join('\n')またはあなたを処理する任意の文字列
出力の見栄えを良くしたいですか?

@cspotcode 、生のコマンドラインを取得するには、WinAPI呼び出しが必要です。

@ SteveL-MSFT:

Windowsでは、CLIを介してコンパイルを_Windows PowerShell_に委任できます。これは、 dbeaです。 これは、生のコマンドライン(のみ)をエコーする.NET_Framework_実行可能ファイルを生成する簡単な例です。 ./rawcmdline.exe

powershell.exe -noprofile -args ./rawcmdline.exe -c {

  param([string] $exePath)

  Add-Type -ErrorAction Stop -OutputType ConsoleApplication -OutputAssembly $exePath -TypeDefinition @'
using System;
static class ConsoleApp {
  static void Main(string[] args) {
    Console.WriteLine(Environment.CommandLine);
  }
}
'@

}

サンプルコール:

PS> ./rawcmdline.exe --% "a&b" PROP="foo bar"
"C:\Users\jdoe\rawcmdline.exe"  "a&b" PROP="foo bar"

引数をエコーする_バッチファイル_については、 dbeaもオンデマンドで作成します

Unixでは、コメントに示されているように、単純なシェルスクリプトで十分であり/bin/sh渡すアドホックスクリプトを_argument_として使用することます

@ PowerShell / powershell-委員会は本日これについて話し合いました。@ JamesWTruherにPRを更新して、引数の配列を文字列に再構築して渡すだけのネイティブコマンドプロセッサのステップをスキップする実験機能の一部として含めるように依頼しています。これをProcessStartInfoの新しい配列引数に追加します(パラメーター名と値が適切に一致することを確認するためのコードが少しあります)。 また、提案された変更でまだ失敗し、後で追加できる特殊なケースの既知のコマンドの許可リストが必要になる場合があることも承知しています。

気づかなかったかもしれない人のために:PRは(WIPとして)公開されており、すでに議論されています: https

PS、@ SteveL-MSFT、Windowsでの生のコマンドラインの取得に関して:もちろん、コンパイルをWindows PowerShell / .NET _Framework_に委任する代わりに、既存の.NET _Core_コンソールアプリケーションを拡張して(プラットフォーム条件付き)を作成することもできます。以下に示すように、 GetCommandLine() WinAPI関数のP / Invoke呼び出し。

using System;
using System.Runtime.InteropServices;

namespace demo
{
  static class ConsoleApp
  {
    [DllImport("kernel32.dll")]
    private static extern System.IntPtr GetCommandLineW();

    static void Main(string[] args)
    {
      Console.WriteLine("\n{0} argument(s) received (enclosed in <...> for delineation):\n", args.Length);
      for (int i = 0; i < args.Length; ++i)
      {
        Console.WriteLine("  <{0}>", args[i]);
      }

      // Windows only: print the raw command line.
      if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
      {
        Console.WriteLine("\nCommand line:\n\n  {0}\n", Marshal.PtrToStringUni(GetCommandLineW()));
      }

    }

  }

}

@ SteveL-MSFT

下位互換性は彼が取り組んでいるものです。

ProcessStartInfo.ArgumentList (Unixではそのまま使用され、Windowsでは.NETCore自体によってMSC / C ++-convention準拠のコマンドラインに変換される_verbatim_引数の_collection__であることが後で明らかになることから、 )を使用する必要がありますが、明示的に述べさせてください。

  • この問題を適切に修正することで、_後方互換性への譲歩が排除されます_。

  • @JamesWTruherのPRは、この記事の執筆時点では正しい方向に空の引数がまだ渡されていないことです。

    • それが解決されると、修正は_Unix_で完了しますが、_Windows_でのCLIの重要な調整が不足しています(以下を参照)。

提案された変更でまだ失敗し、後で追加できる特殊なケースの既知のコマンドの許可リストが必要になる場合があります。

後でまでこれを延期しないことをお勧めします

_allowlist_(特定の実行可能ファイルの特別な場合)の代わりに、@ TSlivedeとさらに話し合った後、上記の規則から改良された

これらの宿泊施設は、_Windowsでのみ必要です_:

具体的には、次のとおりです。

  • Unixの場合と同様に、デフォルトではProcessStartInfo.ArgumentListが使用されます。ただし、次の条件の_1つまたは両方_が満たされている場合を除き、_その場合はプロセスコマンドラインを手動で作成する必要があります_(そしてProcessStartInfo.Arguments割り当てられます)。現在):

    • バッチファイル( .cmd.bat )またはcmd.exe直接呼び出されます:
    • その場合、_embedded_ "は( \"ではなく) ""としてエスケープされ、次のcmd.exeメタ文字のいずれかを含むスペースのない引数としてエスケープされます。また、二重引用符で囲まれます(通常、スペースを含む引数のみが二重引用符で囲まれます): " & | < > ^ , ; -これにより、バッチファイルの呼び出しが堅牢に機能します。これは、多くの注目度の高いCLIがバッチファイルを_entryとして使用するため重要です。 points_。
    • 独立して(場合によってはさらに)、正規表現に一致する引数が少なくとも1つある場合
      '^([/-]\w+[=:]|\w+=)(.*? .*)$'が存在する場合、そのような引数はすべて、_value部分のみ_の周りに_partial_二重引用符を適用する必要があります( :または=続くもの)

      • 例:PowerShellで逐語的に見られるmsiexec.exe / msdeploy.exeおよびcmdkey.exeスタイルの引数
        FOO=bar bazおよび/foo:bar baz / -foo:bar bazは、次のようにプロセスコマンドラインに配置されます。
        foo="bar baz"または/foo:"bar baz" / -foo:"bar baz"は、このスタイルの引用を必要とする_any_CLIを満足させます。
    • 引数の逐語的な\文字は、MS C / C ++の規則に従って処理する必要があります。

これらの宿泊施設でカバーされていないもの:

  • msiexec.exe (そしておそらくmsdeploye.exeも)は_only_ "" -_ embedded_ "文字のエスケープをサポートします。 、上記のルールではカバーされません-バッチファイルまたはcmd /c介して呼び出す場合を除きます。

    • これは、最初から十分にまれなはずです(たとえば、
      msiexec.exe /i example.msi PROPERTY="Nat ""King"" Cole" )ですが、 misexec呼び出しは通常、インストールの終了を待つために_同期化_されるため、さらにまれになります。この場合、次のいずれかの問題を回避できます。二つの方法:
    • cmd /c start /wait msiexec /i example.msi PROPERTY='Nat "King' Cole' - cmd.exeへの呼び出しに依存して(その後) ""トリガーします-エスケープ
    • Start-Process -Wait msiexec '/i example.msi PROPERTY="Nat ""King"" Cole"' - -ArgumentList-Args )パラメーターに依存して、プロセスコマンドラインとして逐語的に単一の文字列引数を渡します(このパラメーターが機能するはずの方法ではありませんが- #5576を参照)。
  • 上記の調整では不十分なその他の非従来型のCLI-私は個人的には何も知りません。

1日の終わりには、常に回避策があります。 cmd /cを介して呼び出すか、コンソール以外のアプリケーションの場合はStart-Processを介して呼び出すか、 --%ます。 insInvoke-NativeShell )コマンドレットを提供する場合、それは別のオプションです。 dbeaDebug-ExecutableArguments echoArgs.exeような機能を備えたDebug-ExecutableArgumentsコマンドレットですが、バッチファイルの場合もオンデマンドで問題を_診断_するのに役立ちます。


重大な変更とオプトインへの道について:

  • これを実験的な機能として実装するということは、十分な関心が示された場合、それが_default_の動作になり、したがって(重要な)重大な変更になることを意味しますか?

  • この実験的な機能がその重要性を考慮して広く公表されていることを確認してください。

    • 実験的機能について私が抱く一般的な懸念は、すべての実験的機能がデフォルトでオンになっていることを考えると、プレビューバージョンではそれらの使用がしばしば無意識に行われる可能性があることです。 私たちは、人々にこの機能を意図的に知って実行してもらいたいと思っています。
このページは役に立ちましたか?
0 / 5 - 0 評価