Powershell: لا يتم تخطي الحجج الخاصة بالملفات التنفيذية الخارجية بشكل صحيح

تم إنشاؤها على ٢١ أغسطس ٢٠١٦  ·  170تعليقات  ·  مصدر: PowerShell/PowerShell

خطوات التكاثر

  1. اكتب برنامج C native.exe الذي يستحوذ على ARGV
  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 لإصدار رئيسي جديد يعمل على إصلاح عناصر سطر الأوامر ، ونقل السلوك الحالي خلف أمر cmdlet مدمج.
  • كحل مؤقت ، يحصل إصدار pwsh القادم على أمر cmdlet المدمج الذي يستخدم السلوك الجديد الصحيح لتمرير سطر الأوامر.

الأمر نفسه ينطبق على Start-Process . (في الواقع إنه مرشح جيد جدًا لـ cmdlet "الجديد" مع بعض الخيارات مثل -QuotingBehavior Legacy ...) راجع # 13089.

ال 170 كومينتر

لماذا تعتقد أن "\"a\"" هو السلوك المتوقع؟ يقول فهمي لهروب PowerShell أن السلوك الفعلي هو السلوك الصحيح والمتوقع. " "a "" هو زوج من الاقتباسات يحيط بزوج من الاقتباسات المهرب يحيط بـ a ، لذلك يفسر PowerShell الزوج الخارجي الذي لم يتم تجاوزه على أنه" هذه وسيطة سلسلة "و حتى يسقط ثم يفسر الزوج هرب كما نقلت هرب وحتى يبقيهم، ويترك لك "a" . في أي نقطة كان \ تضاف إلى سلسلة.

حقيقة أن Bash يستخدم \ كحرف هروب ليست ذات صلة. في PowerShell ، يكون حرف الهروب علامة خلفية. انظر أحرف هروب PowerShell.

إذا كنت تريد المرور حرفياً "\"a\"" ، أعتقد أنك ستستخدم:

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

تضمين التغريدة
نعم ، تعمل عمليات الهروب بشكل جيد مع أوامر cmdlets الداخلية ، لكن الأمور تصبح غريبة عند الاتصال بالثنائيات الأصلية ، خاصة على Windows.
عند تشغيل native.exe " "a "" ، يجب أن يكون ARGV [1]

"a"

(ثلاثة أحرف)

بدلا من

a

(حرف واحد).

حاليًا لجعل native.exe يتلقى ARGV بشكل صحيح مع علامتي اقتباس وحرف a ، يجب عليك استخدام هذه المكالمة الغريبة:

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

آه لقد فهمت. إعادة الفتح.

بدافع الفضول الشديد ، ماذا يحدث إذا حاولت إنشاء باستخدام # 1639؟

andschwa نفس الشيء. يجب عليك مضاعفة esacpe لإشباع كل من PowerShell و CommandLineToArgvW . هذا الخط:

native.exe "`"a`""

ينتج عن عملية StartProcess مساوية لـ cmd

native.exe ""a""

@ be5invisdouglaswth هل يتم حل هذه المشكلة عبر https://github.com/PowerShell/PowerShell/pull/2182؟

لا ، ما زلنا بحاجة إلى إضافة شرطة مائلة للخلف قبل علامة اقتباس مزدوجة متجاوزة؟ هذا لا يحل مشكلة الهروب المزدوج. (وهذا يعني أنه يتعين علينا الهروب من اقتباس مزدوج لكل من PowerShell و CommandLineToArgvW.)

نظرًا لأن " "a "" يساوي '"a"' ، هل تقترح أن ينتج عن native.exe '"a"' "\"a\"" ؟

يبدو هذا وكأنه طلب ميزة إذا تم تنفيذه فقد يكسر عددًا كبيرًا من البرامج النصية PowerShell الموجودة بالفعل والتي تستخدم الهروب المزدوج المطلوب ، لذلك ستكون هناك حاجة إلى عناية فائقة مع أي حل.

vors نعم.
douglaswth الهروب المزدوج سخيف حقًا: لماذا نحتاج إلى عمليات الهروب "الداخلية" التي تمت في عصر DOS؟

تضمين التغريدة
هذا هو رمز C المستخدم لإظهار نتائج GetCommandLineW و CommandLineToArgvW:

#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 النصية الحالية.

كم عددهم؟ لا أعتقد أن هناك كتّاب سيناريو يعرفون عن مثل هذا الاقتباس المزدوج. إنه خطأ ، وليس ميزة ، ولم يتم توثيقه.

؟؟؟؟ ايفون

؟ 2016؟ 9؟ 21 ؟؟ 01:58؟ Douglas Thrift < [email protected] [email protected] > ؟؟؟

@ be5i nvishttps: //github.com/be5invis لا أختلف معك بشأن الهروب المزدوج الذي يكون مزعجًا ، لكني أقول فقط أن التغيير في هذا الأمر يجب أن يكون متوافقًا مع الإصدارات السابقة مع ما تستخدمه برامج PowerShell النصية الحالية.

أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذه الرسالة الإلكترونية مباشرةً ، أو قم tHubhttps: //github.com/PowerShell/PowerShell/issues/1995#issuecomment -248381045 ، أو كتم القراءة:

كان PowerShell موجودًا منذ 9 سنوات ، لذا من المحتمل جدًا وجود عدد كبير من البرامج النصية هناك. لقد وجدت الكثير من المعلومات حول الحاجة إلى الهروب المزدوج من StackOverflow ومصادر أخرى عندما واجهت الحاجة إليها ، لذلك لا أعرف ما إذا كنت أتفق مع ادعاءاتك حول عدم معرفة أي شخص بالحاجة إليها أو أنه غير موثق .

بالنسبة للسياق الإضافي ، أود التحدث قليلاً عن التنفيذ.
يستدعي PowerShell .NET API لإنتاج عملية جديدة ، والتي تستدعي Win32 API (في Windows).

هنا ، ينشئ PS StartProcessInfo الذي يستخدم
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L1063

تأخذ واجهة برمجة التطبيقات (API) المقدمة سلسلة واحدة للوسيطات ثم يتم إعادة تحليلها في مصفوفة من الوسائط للقيام بالتنفيذ.
لا يتحكم PowerShell في قواعد إعادة التحليل هذه. إنها Win32 API (ولحسن الحظ ، تتوافق مع قواعد dotnet الأساسية و unix).
على وجه الخصوص ، يصف هذا العقد سلوك \ و " .

على الرغم من أن PowerShell قد يحاول أن يكون أكثر ذكاءً ويوفر تجربة أفضل ، إلا أن السلوك الحالي يتوافق مع cmd و bash: يمكنك نسخ سطر أصلي قابل للتنفيذ منها واستخدامه في بوويرشيل وهو يعمل بنفس الطريقة.

@ be5invis إذا كنت تعرف طريقة لتعزيز انتهاء https://github.com/PowerShell/PowerShell/blob/master/docs/dev-process/breaking-change-contract.md

ينطبق هذا على Windows ، ولكن عند تشغيل الأوامر على Linux أو Unix ، من الغريب أن يحتاج المرء لمضاعفة علامات الاقتباس.

في عمليات Linux ، لا يوجد سطر أوامر واحد ولكن بدلاً من ذلك مجموعة من الوسائط.
لذلك يجب أن تكون الحجج في بوويرشيل هي نفسها التي يتم تمريرها إلى الملف القابل للتنفيذ ، بدلاً من دمج جميع الوسائط ثم إعادة التقسيم.

حتى على النوافذ ، السلوك الحالي غير متسق:
إذا كانت الوسيطة لا تحتوي على مسافات ، فسيتم تمريرها دون تغيير.
إذا احتوت الوسيطة على مسافات ، إذا كانت محاطة بعلامات اقتباس ، لإبقائها معًا من خلال استدعاء CommandLineToArgvW . => تم تغيير الوسيطة لتلبية متطلبات CommandLineToArgvW .
ولكن إذا احتوت الحجة على اقتباسات ، فلن يتم تخطيها. => لم تتغير الوسيطة ، بالرغم من أن CommandLineToArgvW يتطلب ذلك.

أعتقد أنه لا ينبغي تغيير الحجج مطلقًا ، أو تغييرها دائمًا لتلبية متطلبات CommandLineToArgvW ، ولكن ليس في نصف الحالات.

بخصوص فسخ العقد:
نظرًا لأنني لم أتمكن من العثور على أي وثائق رسمية حول الهروب المزدوج ، فقد أعتبر هذا كفئة "الجرافة 2: المنطقة الرمادية المعقولة" ، لذلك هناك فرص لتغيير هذا ، أم أنني مخطئ؟

vors هذا أمر مزعج للغاية إذا كانت الوسيطة الخاصة بك متغيرًا أو أي شيء آخر: يجب عليك الهروب منها يدويًا قبل إرسالها إلى تطبيق محلي.
قد يساعدك عامل "الهروب التلقائي". مثل ^"a " " -> "a\ " "`

أعتقد أن TSlivede يصحح التناقض في السلوك.

أعتقد أنه لا ينبغي تغيير الحجج مطلقًا ، أو تغييرها دائمًا لتلبية متطلبات CommandLineToArgvW ، ولكن ليس في نصف الحالات.

لست متأكدًا من الدلو ، ولكن من المحتمل تغيير دلو "التغيير الواضح". نريد أن نجعل PowerShell أفضل ، لكن التوافق مع الإصدارات السابقة هو أحد أهم أولوياتنا. لهذا السبب ليس الأمر بهذه السهولة.
لدينا مجتمع رائع وأنا واثق من أنه يمكننا إيجاد توافق في الآراء.

هل يرغب أي شخص في بدء عملية RFC؟

سيكون من المفيد التحقق من استخدام P / Invoke بدلاً من .Net لبدء عملية إذا كان ذلك يتجنب الحاجة إلى PowerShell لإضافة علامات اقتباس إلى الوسيطات.

lzybkr بقدر ما أستطيع أن أقول ، فإن PInvoke لن يساعد.
وهذا هو المكان الذي تختلف فيه واجهات برمجة تطبيقات unix و windows:

https://msdn.microsoft.com/en-us/library/20y988d2.aspx (يعامل المسافات كفواصل)
https://linux.die.net/man/3/execvp (لا يعامل المسافات كفواصل)

لم أكن أقترح تغيير تطبيق Windows.

سأحاول تجنب وجود سلوك خاص بالمنصة هنا. سوف يضر نقل البرامج النصية.
أعتقد أنه يمكننا التفكير في تغيير سلوك النوافذ بطريقة غير منقطعة. أي مع متغير التفضيل. ومن ثم يمكن أن يكون لدينا افتراضات مختلفة أو شيء من هذا القبيل.

نحن نتحدث عن استدعاء أوامر خارجية - تعتمد إلى حد ما على النظام الأساسي على أي حال.

حسنًا ، أعتقد أنه لا يمكن أن يكون نظامًا أساسيًا مستقلًا حقًا ، لأن نظامي التشغيل Windows و Linux لديهما طرق مختلفة لاستدعاء الملفات التنفيذية. في Linux ، تحصل العملية على مصفوفة وسيطة بينما في Windows ، تحصل العملية على سطر أوامر واحد (سلسلة واحدة).
(قارن أكثر أساسية
CreateProcess -> سطر الأوامر (https://msdn.microsoft.com/library/windows/desktop/ms682425)
و
execve -> مصفوفة الأوامر (https://linux.die.net/man/2/execve)
)

نظرًا لأن Powershell يضيف هذه الاقتباسات عندما تحتوي الحجج على مسافات فيها ، يبدو لي أن powerhell يحاول ** تمرير الحجج بطريقة ما ، حيث يقسم CommandLineToArgvW سطر الأوامر إلى الحجج التي تم تقديمها أصلاً في powerhell. (بهذه الطريقة يحصل برنامج c النموذجي على نفس الحجج في مصفوفة argv كما تحصل وظيفة بوويرشيل على $ args.)
سيتطابق هذا تمامًا مع مجرد تمرير الوسائط إلى نظام لينكس (كما هو مقترح عبر p / استدعاء).

** (وفشل ، لأنه لا يفلت من الاقتباسات)

ملاحظة: ما هو الضروري لبدء عملية RFC؟

بالضبط - يحاول PowerShell التأكد من أن CommandLineToArgvW ينتج الأمر الصحيح و _ بعد_ إعادة توزيع ما قام PowerShell بتحليله بالفعل.

لقد كانت هذه نقطة ألم طويلة الأمد على نظام التشغيل Windows ، وأرى سببًا لجلب هذه الصعوبة إلى * لا شيء.

بالنسبة لي ، هذا يبدو وكأنه تفاصيل تنفيذ ، لا يحتاج حقًا إلى RFC. إذا قمنا بتغيير السلوك في Windows PowerShell ، فقد يتطلب ذلك RFC ، ولكن حتى ذلك الحين ، يمكن اعتبار التغيير الصحيح إصلاحًا للأخطاء (ربما يكون محفوفًا بالمخاطر).

نعم ، أعتقد أيضًا أن تغييره على Linux لاستخدام مكالمة نظام مباشرة سيجعل الجميع يشعرون بمزيد من السعادة.

ما زلت أعتقد أنه يجب تغييره أيضًا على النوافذ ،
(ربما عن طريق إضافة متغير تفضيل لأولئك الذين لا يريدون تغيير نصوصهم)
لأنه مجرد خطأ الآن - إنه خطأ. إذا تم تصحيح ذلك ، فلن يكون من الضروري إجراء مكالمة syscall مباشرة على نظام التشغيل Linux ، لأن أي وسيطة ستصل إلى العملية التالية دون تغيير.

ولكن نظرًا لوجود ملفات تنفيذية ، تقسم سطر الأوامر بطريقة لا تتوافق مع CommandLineToArgvW ، فأنا أحب فكرة @ be5invis عن عامل تشغيل للحجج - لكنني لن أقوم بإنشاء عامل هروب تلقائي (يجب أن يكون افتراضي لجميع الوسائط) ، ولكن بدلاً من ذلك أضف عامل تشغيل لعدم الهروب من وسيطة (لا تضف علامات اقتباس ، ولا تترك أي شيء).

ظهرت هذه المشكلة لنا اليوم عندما حاول شخص ما الأمر التالي في PowerShell وكان يفسد PowerShell عندما لم ينجح ولكن CMD فعل:

wmic useraccount where name='username' get sid

من echoargs PSCX ، يرى 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

إذن ما API الذي يستخدمه CMD.exe لاستدعاء العملية / تشكيل سطر الأوامر؟ لهذه المسألة ، ماذا يفعل -٪ لجعل هذا الأمر يعمل؟

rkeithhill CreateProcessW . دعوة مباشرة. هل حقا.

لماذا يتصرف 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...

يبدو أنه لا يوجد سبب أو قافية. في الحالة الأولى ، تغلف حجتي بعلامات اقتباس مزدوجة ، لكنها لا تفعل في الحالة الثانية. أحتاج إلى معرفة متى سيتم ذلك بالضبط ولن يتم الالتفاف في علامات اقتباس مزدوجة حتى أتمكن من الالتفاف يدويًا (أو لا) في البرنامج النصي الخاص بي.

يتم إنشاء .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 يضاعف الشرطات المائلة للخلف أمام علامات الاقتباس ويضيف شرطة مائلة للخلف إضافية ، للهروب من qoute.
يضاعف -replace الشرطة المائلة العكسية في نهاية الوسيطة ، بحيث لا يتم تخطي اقتباس الإغلاق.

يستخدم هذا --% (PS v3 والإصدارات الأحدث) ، وهو AFAIK الطريقة الوحيدة الموثوقة لتمرير عروض الأسعار إلى الملفات التنفيذية الأصلية.


تعديل:

نسخة محدثة من 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

شكرًا ، لم أكن أعرف شيئًا عن --% . هل هناك أي طريقة للقيام بذلك دون تسريب متغير البيئة إلى العملية الأصلية؟ (وأي عمليات قد تستدعيها)

هل توجد وحدة PowerShell النمطية التي تنفذ Run-Native Cmdlet ليستخدمها الجميع؟ يبدو هذا وكأنه شيء يجب أن يكون في معرض Powershell. إذا كانت جيدة بما يكفي ، فقد تكون الأساس لـ RFC.

يبدو "التسريب" وكأنك قلق بشأن الأمن. لاحظ مع ذلك أن سطر الأوامر مرئي للعمليات التابعة على أي حال. (على سبيل المثال: gwmi win32_process |select name,handle,commandline|Format-Table على Windows و ps -f على Linux)

إذا كنت لا تزال ترغب في تجنب متغير البيئة ، فقد تتمكن من إنشاء شيء باستخدام تعبير الاستدعاء.

بخصوص RFC:
لا أعتقد أن مثل هذه الأوامر يجب أن تكون ضرورية ، بدلاً من ذلك يجب أن يكون هذا هو السلوك الافتراضي:

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

أوافق على أنه يجب إصلاح السلوك الافتراضي لـ PowerShell. كنت أفترض بشكل متشائم أنه لن يتغير أبدًا لأسباب التوافق العكسي ، ولهذا السبب اقترحت كتابة وحدة. ومع ذلك ، أنا حقًا أحب الطريقة التي يسمح بها RFC الخاص بك بإعادة تمكين سلوك الهروب القديم عبر متغير تفضيل.

اسمحوا لي أن ألخص المناقشة ، فقط بالجرعة الصحيحة من الرأي:

  • من الواضح أن لدينا مشكلة في التوافق مع الإصدارات السابقة ، لذا يجب أن يظل السلوك القديم متاحًا.

  • يراعي اقتراح RFC الخاص بـ TSlivede ذلك بينما يشير بشكل جدير بالثناء إلى الطريق إلى المستقبل.
    لسوء الحظ ، فإن اقتراحه ضعيف باعتباره _PR_ حتى كتابة هذه السطور ، ولم يتم قبوله حتى الآن باعتباره RFC _draft_ حتى الآن.


ب- المستقبل- أعني:

  • PowerShell عبارة عن غلاف في حد ذاته نأمل أن يتخلص قريبًا من أمتعته المرتبطة بـ cmd.exe ، لذا فإن الاعتبارات الوحيدة التي تهم عندما يتعلق الأمر باستدعاء المرافق الخارجية (الملفات التنفيذية التي (عادةً) تطبيقات وحدة التحكم / المحطة الطرفية) هي :

    • يجب تحديد الوسيطات المطلوب تمريرها بواسطة قواعد تحليل وضع الوسيطة _PowerShell_ _ فقط.

    • مهما كانت _literals_ الناتجة عن هذه العملية يجب أن يتم تمريرها _as-is_ إلى الهدف القابل للتنفيذ ، كوسائط _individu.

    • بعبارة أخرى: كمستخدم ، كل ما يجب عليك التركيز عليه هو نتيجة تحليل _PowerShell_ ، ولتتمكن من الاعتماد على هذه النتيجة التي يتم تمريرها كما هي ، مع PowerShell الذي يعتني بأي شيء خلف - ترميز المشاهد - إذا لزم الأمر.


_تنفيذ_ المستقبل:

  • في _Windows_:

    • لأسباب _ تاريخية _ ، لا يسمح Windows بتمرير الوسائط _ كمصفوفة من القيم الحرفية_ إلى الهدف القابل للتنفيذ ؛ بدلاً من ذلك ، هناك حاجة إلى سلسلة _single_ ترميز _all_ وسيطات باستخدام _pseudo shell syntax_. والأسوأ من ذلك ، يعود الأمر في النهاية إلى الهدف الفردي القابل للتنفيذ لتفسير تلك السلسلة المفردة وتقسيمها إلى وسيطات.

    • أفضل ما يمكن أن يفعله PowerShell هو تشكيل تلك السلسلة المفردة - خلف الكواليس ، بعد إجراء تقسيمها _ لأسفل _ لسطر الأوامر إلى وسيطات فردية - بطريقة موحدة يمكن التنبؤ بها.

    • يقترح اقتراح RFC الخاص بـ TSlivede ذلك ، من خلال اقتراح أن يقوم PowerShell بتركيب سطر أوامر shell الزائف بطريقة تؤدي إلى قيام وقت تشغيل Windows C / C ++ باستعادة وسائط الإدخال كما هي عند إجراء التحليل :

      • نظرًا لأن الأمر متروك في النهاية لكل هدف قابل للتنفيذ لتفسير سطر الأوامر ، فليس هناك _ضمان_ أن هذا سيعمل في جميع الحالات ، ولكن القواعد المذكورة هي الخيار الأكثر منطقية ، لأن الأدوات المساعدة الحالية تستخدم هذه الاصطلاحات.

      • الاستثناءات الوحيدة الملحوظة هي الملفات الدفعية ، والتي يمكن أن تتلقى معاملة خاصة ، كما يقترح اقتراح RFC.

  • على منصات _Unix_:

    • بالمعنى الدقيق للكلمة ، فإن المشكلات التي تعصف بتحليل حجة Windows _ لا يجب أن تظهر أبدًا_ ، لأن استدعاءات النظام الأساسي الأصلية لإنشاء عمليات جديدة _ قبول الحجج كمصفوفات من القيم الحرفية _ - أيًا من الحجج التي ينتهي بها PowerShell بعد إجراء التحليل _own_ يجب أن يتم تمريرها فقط على _as-is_ .
      لنقتبس منlzybkr : "لا أرى سببًا

    • للأسف ، نظرًا للقيود الحالية لـ .NET Core (CoreFX) ، فإن هذه المشكلات _do_ تدخل حيز التنفيذ ، لأن CoreFX API يفرض بلا داع فوضى حجة _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-NativeCommand (لأن Run ليس فعلًا صالحًا) وأضفت اسمًا مستعارًا ^ ونشرته كوحدة نمطية على PowerShellGallery:

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

@ SteveL-MSFT:

من الجيد أن يكون لديك فجوة مؤقتة ، ولكن الحل الأقل تعقيدًا سيكون - أثناء انتظار حل CoreFX - لتنفيذ قواعد الاقتباس / تحليل الحجج الرسمية المحددة جيدًا كما هو مفصل في اقتراحTSlivede 's RFC بأنفسنا بشكل مبدئي - وهو لا يبدو من الصعب جدا القيام به.

إذا قمنا بإصلاح مشكلة \" ، فإن تمرير الوسيطة لا يزال معطلاً بشكل أساسي ، حتى في السيناريوهات البسيطة مثل ما يلي:

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

أعتقد في هذه المرحلة أن هناك اتفاقًا كافيًا على ما يجب أن يكون عليه السلوك ، لذلك لا نحتاج إلى عملية RFC كاملة ، أليس كذلك؟

القرار الوحيد المعلق هو كيفية التعامل مع مشكلات التوافق مع الإصدارات السابقة في _Windows_.

@ mklement0 @ SteveL-MSFT
هل كسرنا التوافق بالفعل ؟

القرار الوحيد المعلق هو كيفية التعامل مع مشكلات التوافق مع الإصدارات السابقة في Windows.

نعم ، ولكن هذا هو الجزء الصعب ، أليس كذلك؟

@ be5invis ماذا تقصد ب "التوافق كسر بالفعل"؟

بالإضافة إلى ذلك ، إذا كانت CoreFX على وشك الإصلاح في طبقتها ، فأنا أفضل عدم إنشاء فجوة مؤقتة في طبقتنا قبل القيام بذلك.

وكما قال أحدهم أعلاه في الموضوع ، هذا أمر مزعج ، لكنه أيضًا موثق جيدًا في المجتمع. لست متأكدًا من أننا يجب أن نكسرها مرتين في الإصدارين المقبلين.

joeyaiello :

ليس إصلاح # 4358 تغييرًا جذريًا بالفعل لأولئك الذين عملوا على حل المشكلة بمضاعفة \ الأخير؛ على سبيل المثال ، "c:\tmp 1\\" ؟ بعبارة أخرى: إذا قمت بتقييد التغييرات على هذا الإصلاح ، فسيكون هناك تغييران منفصلان مضمونان: هذا الآن ، وآخر لاحقًا بعد التبديل إلى CoreFx API في المستقبل ؛ وبينما يمكن أن يحدث ذلك أيضًا إذا تم تنفيذ فجوة مؤقتة كاملة الآن ، فمن غير المحتمل ، نظرًا لما نعرفه عن هذا التغيير القادم.

بالمقابل ، قد يعيق التبني على يونكس إذا كانت سيناريوهات الاقتباس الشائعة مثل
bash -c 'echo "hi there"' لا يعمل بشكل صحيح.

لكني أدرك أن إصلاح هذا هو تغيير كبير للغاية.

ناقش @ PowerShell /owershell-Committee هذا ووافقوا على أن استخدام --% يجب أن يكون له نفس سلوك bash في أن يتم إهمال الاقتباسات بحيث يستقبلها الأمر الأصلي. ما لا يزال مفتوحًا للنقاش هو ما إذا كان يجب أن يكون هذا هو السلوك الافتراضي بدون استخدام --%

ملحوظة:

  • أفترض أن استدعاء ملف تنفيذي shell فعلي ضروري عند استخدام --% on _Unix_ ، بدلاً من محاولة محاكاة سلوك shell ، وهو ما يحدث في _Windows_. المحاكاة ليست صعبة على Windows ، ولكنها ستكون أصعب بكثير على Unix ، نظرًا للعديد من الميزات التي قد تحتاج إلى محاكاتها.

  • يثير استخدام الصدفة الفعلية السؤال عن نوع shell الذي يجب استخدامه: في حين أن bash موجود في كل مكان ، فإن سلوكه الافتراضي ليس متوافقًا مع POSIX ولا يتطلبه وجود POSIX ، لذلك بالنسبة لقابلية النقل ، تستدعي لغات البرمجة النصية الأخرى /bin/sh ، الصدفة القابلة للتنفيذ التي أصدرها POSIX (والتي _يمكن_ تشغيل Bash في وضع التوافق (على سبيل المثال ، على macOS) ، ولكن بالتأكيد ليس من الضروري (على سبيل المثال ، Dash على Ubuntu)).

يمكن القول أننا يجب أن نستهدف /bin/sh أيضًا - مما يعني ، مع ذلك ، أن بعض ميزات Bash - لا سيما توسعة القوس ، وبعض المتغيرات التلقائية ، ... - لن تكون متاحة


استخدام --%

سأستخدم الأمر echoargs --% 'echo "hi there"' كمثال أدناه.

نفس سلوك bash في أن علامات الاقتباس يتم تخطيها حتى يستقبلها الأمر الأصلي.

طريقة القيام _ في المستقبل ، بمجرد أن يتم توسيع واجهة CoreFX API_ ستكون عدم إجراء أي هروب على الإطلاق ، وبدلاً من ذلك قم بما يلي:

  • قم بإنشاء عملية على النحو التالي:

    • /bin/sh كملف قابل للتنفيذ ، (فعليًا) تم تعيينه لـ ProcessStartInfo.FileName .

    • المصفوفة التالية من الرموز المميزة _literal_، _individual_ الوسيطة كـ ProcessStartInfo.ArgumentList :

    • -c كوسيطة أولى

    • echoargs 'echo "hi there"' كوسيطة ثانية - على سبيل المثال ، استخدم سطر الأوامر الأصلي _literally_ ، تمامًا كما هو محدد ، باستثناء أنه تمت إزالة --% .

في الواقع ، يتم تمرير سطر الأوامر من خلال _as-is_ إلى shell القابل للتنفيذ ، والذي يمكنه بعد ذلك إجراء تحليل _its_.

أفهم أنه في ظل الغياب الحالي لطريقة قائمة على المصفوفة لتمرير الوسائط الحرفية ، نحتاج إلى دمج -c و echoargs 'echo "hi there"' في سلسلة _single_ _ مع escaping_ ، للأسف _ فقط لصالح CoreFX API_ ، والتي عندما يحين وقت إنشاء العملية الفعلية ، ثم _عكس_ هذه الخطوة وتقسيم السلسلة الفردية مرة أخرى إلى رموز حرفية - والتأكد من أن هذا الانعكاس يؤدي دائمًا إلى القائمة الأصلية للرموز الحرفية هو الجزء الصعب.

مرة أخرى: السبب الوحيد وراء الهروب هنا على الإطلاق يرجع إلى قيود CoreFX الحالية.
للعمل مع هذا القيد، ولذلك يجب ان يتم تعيين واحدة، سلسلة هرب التالية إلى .Arguments ممتلكات ProcessStartInfo المثال، مع مهربا يؤديها على النحو الذي يحدده توزيع 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 بإجراء التحليل المعتاد وينتهي بالوسيطتين التاليتين:

    • echoargs
    • echo "hi there" (علامات الاقتباس المفردة - التي تحتوي فقط على وظيفة نحوية لـ _PowerShell_ ، تمت إزالتها)
  • يتم بعد ذلك ملء ProcessStartInfo النحو التالي ، مع امتداد CoreFX القادم:

    • echoargs كقيمة العقار (الفعالة) .FileName
    • _Literal_ echo "hi there" كعنصر واحد يتم إضافته إلى مثيل Collection<string> عرضه بواسطة .ArgumentList .

مرة أخرى ، في حالة عدم وجود .ArgumentList فهذا ليس خيارًا _ حتى الآن_ ، ولكن _ في غضون ذلك _ يمكن استخدام الهروب الإضافي المتوافق مع MS C ++ كما هو موضح أعلاه.

@ SteveL-MSFT
كما ذكرت بالفعل في عمل رمز إيقاف التحليل (-٪) على Unix (# 3733) أنصح بشدة بعدم تغيير سلوك --% .

إذا كانت هناك حاجة لبعض الوظائف الخاصة لـ /bin/sh -c فالرجاء استخدام رمز مختلف وترك --% كما هو!

TSlivede :

_If_ شيء ما --% -like_ تم تنفيذه على Unix - ومع خاصية globbing الأصلية وحشد أكثر ذكاءً في سطر الأوامر بشكل عام على Unix ، أدرك أن الحاجة إليه أقل - ثم اختر _رمز مختلف _ - مثل --$ - ربما يكون منطقيًا (آسف ، لقد فقدت تتبع جميع جوانب هذا النقاش المطول متعدد القضايا).

قد تعمل الرموز المختلفة أيضًا كتذكيرات مرئية بشكل واضح بأنه يتم استدعاء السلوك غير المحمول الخاص بالنظام الأساسي.

هذا يترك السؤال عما يجب أن يفعله PowerShell عندما يأتي عبر --% على Unix و --$ على Windows.

أنا بخير ترك --% كما هو. تقديم شيء مثل --$ الذي يستدعي / bin / sh وأعتقد أن cmd.exe على Windows قد يكون طريقة جيدة لحل هذه المشكلة.

لا توجد فرصة لإنشاء أمر cmdlet لهذه السلوكيات؟

iSazonov هل تقترح شيئًا مثل Invoke-Native ؟ لست متأكدًا من أنني معجب بذلك.

نعم ، مثل Start-Native .
على سبيل المزاح :-) ، ألا تحب cmdlets في PowerShell؟

في Build.psm1 لدينا Start-NativeExecution مع رابط إلى https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right/

@ SteveL-MSFT

أنا بخير ترك -٪ كما هي.

أعتقد أننا نتفق جميعًا على أن --% يجب أن يستمر في التصرف بالطريقة التي يعمل بها في _Windows_.

في _ Unix_ ، على النقيض من ذلك ، هذا السلوك لا معنى له ، كما حاولت أن أوضح هنا - باختصار:

  • لم يتم التعامل مع علامات الاقتباس الفردية بشكل صحيح
  • الطريقة الوحيدة للإشارة إلى متغير البيئة هي _cmd.exe-style_ ( %var% )
  • الميزات الأصلية المهمة مثل globbing وتقسيم الكلمات لا تعمل.

كان الدافع الأساسي لتقديم --% ، إذا فهمت بشكل صحيح ، هو تمكين _ إعادة استخدام أسطر الأوامر cmd.exe الحالية _ كما هي.

  • على هذا النحو ، --% عديم الفائدة على Unix بسلوكه الحالي.
  • _If_ أردنا ميزة _analogous_ على يونكس ، يجب أن تكون على أساس /bin/sh -c كما هو مقترح أعلاه ، ربما باستخدام رمز مختلف

لا أعتقد أن هناك حاجة لميزة تستند إلى cmd /c على نظام التشغيل Windows ، حيث أن --% قد تمت تغطيتها غالبًا ، ويمكن القول بطريقة _جيد بما فيه الكفاية_.
أشار TSlivede إلى أنه لا يتم محاكاة جميع ميزات shell ، ولكن من الناحية العملية لا يبدو ذلك مصدر قلق (على سبيل المثال ، البدائل ذات القيمة المتغيرة مثل %envVar:old=new% غير مدعومة ، ^ ليس حرف هروب ، واستخدام --% يقتصر على أمر _single_ - لا يوجد استخدام لمشغلي إعادة التوجيه والتحكم cmd.exe ؛ ومع ذلك ، لا أعتقد أن --% كان المقصود من

على هذا النحو ، شيء مثل --$ - إذا تم تنفيذه - سيكون Unix _counterpart_ إلى --% .

على أي حال ، يستحق موضوع المساعدة about_Parsing على الأقل تحذيرًا واضحًا من أن --% سيكون عديم الفائدة على Unix في جميع الحالات باستثناء حالات قليلة.

iSazonov قد يكون من المنطقي أن يكون لديك Start-Native للتعامل مع بعض السيناريوهات المحددة ، ولكن يجب أن نحاول تحسين PowerShell بحيث يكون استخدام exes الأصلي أمرًا طبيعيًا ويمكن التنبؤ به

راجعت @ PowerShell / powershell-Committee هذا ووافقت على أن --% يجب أن يعني التعامل مع الحجج كما يفعلون في الأنظمة الأساسية ذات الصلة مما يعني أنهم يتصرفون بشكل مختلف على Windows و Linux ، ولكن متسق داخل Windows ومتسق داخل Linux. سيكون الأمر أكثر إرباكًا للمستخدم لتقديم سيجيل جديد. سنترك التنفيذ للمهندس حول كيفية تمكين ذلك.

يجب أن تكون البرامج النصية عبر الأنظمة الأساسية على دراية بهذا الاختلاف في السلوك ، ولكن يبدو من غير المحتمل أن يصل المستخدمون إلى هذا. إذا كانت ملاحظات المستخدم تشير إلى أن هناك حاجة بسبب المزيد من الاستخدام عبر النظام الأساسي ، فيمكننا إعادة النظر في تقديم سيجيل جديد.

للمرة الثانية ، لقد تعرضت للعض من قبل هذه الاختلافات في تحليل سلسلة وسيطة أوامر cmdlets الأصلية عند استخدام ripgrep .

إليك نتيجة استدعاءات بوويرشيل (يأتي echo.exe من "C: \ Program Files \ Git \ usrbin \ echo.exe")

الآن أعلم أنه يجب علي الانتباه إلى هذا " المراوغة:

> echo.exe '"test'
test

لكن هذا الشذوذ خارج عني ...

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

في الحالة الثانية ، أحتاج إلى وضع ضعف \ لتمرير \ إلى الأمر الأصلي ، في الحالة الأولى لست بحاجة إلى القيام بذلك 😑

أفهم أن هذا سيكون تغييرًا جذريًا لتغيير هذا السلوك ، لذلك لن يكون هناك فرق بين أوامر cmlets والأوامر الأصلية. ومع ذلك ، أعتقد أن المراوغات مثل هذا شيء يمنع الناس من استخدام بوويرشيل كصدفة افتراضية.

mpawelski لقد جربت هذا للتو على صندوق Windows الخاص بي باستخدام البوابة 2.20.1.vfs.1.1.102.gdb3f8ae ولا يعيد برورو بالنسبة لي مع 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 الأصلية التي لا تعمل بشكل صحيح بسبب هذه المشكلة ، فربما يمكنني اصطحاب الأسرة لتناول العشاء في Qoba. :-) على سبيل المثال ، يسمح tf.exe ; كجزء من تحديد مساحة العمل. يسمح Git بدولار {} و @~1 ، إلخ ، إلخ.

--% لإخبار PowerShell بعدم تحليل بقية سطر الأوامر - فقط أرسلها "كما هي" إلى exe الأصلي. باستخدام فرك واحد ، اسمح بالمتغيرات باستخدام بناء جملة cmd env var. إنه قبيح بعض الشيء لكن الرجل ، حقًا ، لا يزال مفيدًا على Windows.

إعادة جعل هذا الأمر cmdlet ، لست متأكدًا من أنني أرى كيف يعمل ذلك. --% هو إشارة إلى المحلل اللغوي لتخفيض التحليل حتى موسوعة الحياة ..

بصراحة ، باعتباري مستخدمًا لفترة طويلة لـ PowerShell وهذه الميزة على وجه الخصوص ، فمن المنطقي بالنسبة لي استخدام نفس المشغل على منصات أخرى لأعني ببساطة - خفض التحليل حتى موسوعة الحياة. هناك مسألة كيفية السماح ببعض أشكال استبدال المتغير. على الرغم من أن الأمر يبدو قبيحًا بعض الشيء ، إلا أنه في نظام التشغيل macOS / Linux يمكنك أخذ٪ envvar٪ واستبدال قيمة أي env var مطابق. ثم يمكن أن تكون محمولة بين المنصات.

الشيء هو ، إذا لم تفعل ذلك ، فستنتهي برمز شرطي - ليس بالضبط ما أسميه محمول:

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

أفضل هذا العمل على جميع المنصات:

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

يجب أن يظل السلوك على Windows كما هو بسبب التوافق.

تضمين التغريدة

هذا ليس كل شيء.

من خلال عملية الإزالة ، تمت كتابة أسطر الأوامر التي سبقت PowerShell في عالم Windows مقابل cmd.exe - وهذا بالضبط ما ينوي --% محاكاته ، كما يتضح من ما يلي:

  • --% يوسع cmd.exe -style %...% مراجع البيئة المتغيرة ، مثل %USERNAME% (إذا لم يكن هناك shell ولا منطق خاص ، فسيتم تمرير هذه الرموز المميزة _حرفي_)

  • مباشرة من فم PowerShell (إذا

الويب مليء بأسطر الأوامر المكتوبة لـ Cmd.exe . تعمل سطور الأوامر هذه غالبًا بشكل كافٍ في PowerShell ، ولكن عندما تتضمن أحرفًا معينة ، على سبيل المثال فاصلة منقوطة (؛) علامة الدولار ($) ، أو الأقواس المتعرجة ، يجب عليك إجراء بعض التغييرات ، وربما إضافة بعض علامات الاقتباس. يبدو أن هذا هو مصدر العديد من الصداع الطفيف.

للمساعدة في معالجة هذا السيناريو ، أضفنا طريقة جديدة "للهروب" من تحليل سطور الأوامر. إذا كنت تستخدم المعامل السحري -٪ ، فإننا نوقف التحليل الطبيعي لسطر الأوامر الخاص بك ونتحول إلى شيء أبسط بكثير. نحن لا نطابق الاقتباسات. نحن لا نتوقف عند الفاصلة المنقوطة. لا نقوم بتوسيع متغيرات PowerShell. نقوم بتوسيع متغيرات البيئة إذا كنت تستخدم بناء جملة Cmd.exe (مثل٪ TEMP٪). بخلاف ذلك ، يتم تمرير الوسيطات حتى نهاية الخط (أو الأنبوب ، إذا كنت تقوم بتوصيل الأنابيب) كما هي. هنا مثال:

لاحظ أن هذا الأسلوب غير ملائم لعالم Unix (للتلخيص من الأعلى):

  • لن ينجح البحث عن وسيطات غير مسعرة مثل *.txt .

  • الأصداف التقليدية (مثل POSIX) في عالم Unix لا تستخدم %...% للإشارة إلى متغيرات البيئة ؛ يتوقعون بناء جملة $... .

  • لا يوجد (لحسن الحظ) سطر أوامر _raw_ في عالم Unix: يجب تمرير أي ملف تنفيذي خارجي _ مجموعة _ من _literals_ ، لذلك لا يزال على PowerShell أو CoreFx تحليل سطر الأوامر إلى وسيطات أولاً.

  • تقبل الأصداف التقليدية (مثل POSIX) في عالم Unix سلاسل '...' (ذات الاقتباس الفردي) ، والتي لا يتعرف عليها --% - راجع # 10831.

ولكن حتى في عالم Windows ، فإن --% له قيود شديدة وغير واضحة :

  • من الواضح أنه لا يمكنك الإشارة مباشرة إلى متغيرات PowerShell في سطر الأوامر إذا كنت تستخدم --% ؛ الحل الوحيد - المرهق - هو تحديد متغيرات البيئة مؤقتًا ، والتي يجب الرجوع إليها بعد ذلك بـ %...% .
  • لا يمكنك إحاطة سطر الأوامر بـ (...) - لأن الإغلاق ) يتم تفسيره على أنه جزء حرفي من سطر الأوامر.
  • لا يمكنك متابعة سطر الأوامر باستخدام ; وكشف آخر - لأن ; يتم تفسيره على أنه جزء حرفي من سطر الأوامر.
  • لا يمكنك استخدام --% داخل كتلة نصية من سطر واحد - لأن إغلاق } يتم تفسيره على أنه جزء حرفي من سطر الأوامر.
  • لا يمكنك استخدام عمليات إعادة التوجيه - لأنها تعامل كجزء حرفي من سطر الأوامر - ومع ذلك ، يمكنك استخدام cmd --% /c ... > file للسماح لـ cmd.exe بالتعامل مع إعادة التوجيه.
  • لا يمكنك استخدام أحرف متابعة السطر - لا PowerShell's ( ` ) ولا cmd.exe ( ^ ) - سيتم التعامل معها على أنها _literals_.

    • يوزع --% فقط (على الأكثر) حتى نهاية السطر.


لحسن الحظ ، لدينا بالفعل بناء جملة عبر الأنظمة الأساسية: بناء جملة PowerShell الخاص.

نعم، وذلك باستخدام هذا يتطلب منك أن تعرف ما تعتبره _PowerShell_ الأحرف الخاصة، وهو _superset_ كل ما cmd.exe وPOSIX مثل قذائف مثل باش تعتبر الأحرف الخاصة، ولكن هذا هو الثمن الذي دفع لأكثر ثراء، وبرمجيات منصات تجربة سطر الأوامر الحيادية.

كم هو مؤسف إذن أن تتعامل PowerShell مع اقتباس أحرف " بشكل سيء للغاية - وهو موضوع هذه المشكلة ، والذي تم تلخيصه في مشكلة المستندات هذه .

rkeithhill ، لقد بدأت قليلاً من الظل ؛ دعني أحاول إغلاقه:

  • --% بالفعل بالفعل اعتبارًا من PowerShell Core 6.2.0 ؛ على سبيل المثال ،
    /bin/echo --% %HOME% يطبع قيمة متغير البيئة HOME ؛ على نقيض ذلك،
    /bin/ls --% *.txt كما هو متوقع ، لأن *.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-likes. يحتوي تطبيق CoreFx لـ Unix SDProcess على شيء يسمى ParseArgumentsIntoList ، والذي يقوم بتنفيذ CommandLineToArgvW تقريبًا بدون مفاتيح _setargv (ومع "" في الاقتباسات → " ميزة). لا ينبغي أن يكون نظام Unix نقطة ألم إضافية في هذا الأمر لأنه في الشكل الحالي يتم كسره بنفس طريقة Windows.

_setargv ليس شيئًا يستخدمه كل برنامج بعد كل شيء ، وربما لا يستحق التفكير فيه لأنه ، حسنًا ، يتأثر نوعًا ما بالتغييرات السلوكية بين إصدارات CRT . أفضل ما يمكننا ويجب علينا فعله هو إحاطة كل شيء بعلامات اقتباس مزدوجة ، وإضافة بعض التعليقات الخلفية اللطيفة ، وهذا كل شيء.

مثال آخر حيث لا يتم تحليل args بشكل صحيح:

az "myargs&b"

في هذه الحالة ، يحصل az على myargs b ويتم محاولة تنفيذ

الحل هو: az -٪ "myargs & b"

@ SteveL-MSFT ، يمكننا إزالة التصنيف Waiting - DotNetCore ، نظرًا لأن الميزة المطلوبة - خاصية ProcessStartInfo.ArgumentList القائمة على التجميع متاحة منذ .NET Core 2.1

TSlivede تدرك الطريقة الجديدة وتخطط لاستخدامها ، لكن RFC المرتبط ، https://github.com/PowerShell/PowerShell-RFC/pull/90 ، ضعيف ، للأسف.

أقترح أن نواصل مناقشة _implementation_ هناك.

ومع ذلك ، في مناقشة RFC ، يتحدث joeyaiello عن جعل التغييرات ميزة تجريبية ، ولكن أصبح من الواضح لي بشكل متزايد أنه لا يمكنك إصلاح سلوك الاقتباس دون كسر الشفرة الحالية بشكل كبير:

أي شخص كان عليه _ العمل حول_:

  • عدم القدرة على تمرير وسيطة سلسلة فارغة ( foo.exe "" يمر حاليًا _no_ وسيطات)
  • الإزالة الفعالة غير المتوقعة لعلامات الاقتباس المزدوجة بسبب عدم وجود الهروب التلقائي من علامات الاقتباس المزدوجة المضمنة (تم تمرير foo.exe '{ "foo": "bar" }' أنه تم تجاوزه بشكل غير صحيح "{ "foo": "bar" }" )
  • المراوغات الخاصة ببعض CLIs مثل msiexec التي لا تقبل وسيطات معينة ذات علامات اقتباس مزدوجة _ ككل_ ( foo.exe foo="bar none" يتم تمريرها كـ "foo=bar none" ).
    ملاحظة: يقع اللوم هنا على msiexec ، ومع تطبيق التغييرات المقترحة ، فإن تمرير نموذج التسعير المطلوب foo="bar none" سيتطلب --% .

سيكون في مشكلة ، لأن الحلول سوف _كسر_ مع تطبيق التغييرات المقترحة.

لذلك فإن السؤال الإضافي هو:

  • كيف يمكننا إتاحة السلوك الصحيح على الأقل كميزة _opt-in_؟

    • في حالة اقتباس الأسعار مع Start-Process ، لدينا على الأقل خيار إدخال معامِل جديد للسلوك الصحيح ، ولكن مع الاستدعاء المباشر الذي من الواضح أنه ليس خيارًا (ما لم نقدم "رمزًا سحريًا" آخر مثل كـ --% ).
  • المشكلة الأساسية في مثل هذه الآليات - عادةً ، من خلال متغير التفضيل ، ولكن أيضًا بشكل متزايد بواسطة عبارات using - هي النطاق الديناميكي لـ PowerShell ؛ أي أن سلوك الاشتراك سيتم بشكل افتراضي تطبيقه على الكود الذي يُطلق عليه _from_ رمز الاشتراك أيضًا ، وهو أمر يمثل مشكلة.

    • ربما حان الوقت لتقديم نطاق _lexical_ للميزات بشكل عام ، وهو تعميم للمقترح المعجمي using strict في RFC - وضع الضيق المعجمي بشكل متساوٍ -

    • شيء مثل المعجمية using preference ProperArgumentQuoting ؟ (من الواضح أن الاسم قابل للنقاش ، لكنني أجد صعوبة في الخروج بواحد).

بالنظر إلى كل هذه المحاذير ، وأن هذه مشكلة تتعلق بالاستدعاء المباشر أيضًا ، حيث لا يمكننا ببساطة إضافة معامل جديد ، فأنا أؤيد بشدة كسر السلوك القديم.

نعم ، من المحتمل أن ينكسر كثيرًا. لكن في الحقيقة ، فقط لأنه تم كسره تمامًا في البداية. لا أعتقد أنه من المجدي بشكل خاص إعطاء الأولوية للحفاظ على ما يرقى إلى كومة ضخمة من الحلول للسلوك المعطل على امتلاك ميزة تعمل بالفعل.

كإصدار رئيسي جديد ، أعتقد أن الإصدار 7 هو حقًا الفرصة الوحيدة لتصحيح هذا الوضع بشكل صحيح لبعض الوقت ، ويجب أن ننتهز الفرصة. شريطة توعية المستخدمين ، لا أعتقد أن النقل سيُلقى بشكل سيء بشكل عام.

إذا شعرنا أن كسر التغيير أمر لا مفر منه ، فربما يجب علينا تصميم الحل المثالي ، مع الأخذ في الاعتبار أنه يجب أن يكون من السهل طباعته في جلسة تفاعلية ومن الممكن الحصول على نسخة نصية تعمل بشكل أفضل على جميع الأنظمة الأساسية.

متفق عليه ، @ vexx32 : الاحتفاظ بالسلوك الحالي ، حتى لو كان بشكل افتراضي فقط ، سيظل نقطة ألم دائمة. لا يتوقع المستخدمون الحاجة إلى الاشتراك في التواجد معهم ، وبمجرد أن يفعلوا ذلك ، فمن المحتمل أن ينسوا في بعض الأحيان و / أو يستاءون من الحاجة إلى تطبيقه في كل مرة.

فالصدفة التي لا تمرر الحجج بشكل موثوق إلى البرامج الخارجية تفشل في أحد مهامها الأساسية.

لديك بالتأكيد _my_ صوتك لإجراء التغيير المفاجئ ، لكنني أخشى أن يشعر الآخرون بشكل مختلف ، لأسباب ليس أقلها أن الإصدار 7 يُوصف بأنه يسمح لمستخدمي WinPS على المدى الطويل بالانتقال إلى PSCore.


iSazonov : https://github.com/PowerShell/PowerShell-RFC/pull/90 يصف الحل الصحيح.

لتلخيص روحها:

يحتاج PowerShell ، كصدفة ، إلى تحليل الحجج وفقًا لقواعد _its_ ثم تمرير قيم الوسيطة الموسعة الناتجة _verbatim_ إلى البرنامج المستهدف - يجب ألا يضطر المستخدمون أبدًا إلى التفكير في كيفية قيام PowerShell بذلك ؛ كل ما يجب أن يقلقوا بشأنه هو الحصول على صياغة _PowerShell_ بشكل صحيح.

  • على الأنظمة الأساسية الشبيهة بـ Unix ، يمنحنا ProcessStartInfo.ArgumentList الآن طريقة لتنفيذ ذلك بشكل مثالي ، نظرًا لأنه يمكن تمرير _صفيف_ قيم الوسيطات الموسعة _ as-is_ إلى البرنامج المستهدف ، لأن هذه هي الطريقة التي يعمل بها تمرير الحجة - بشكل معقول - في هذا العالم.

  • على نظام التشغيل Windows ، يتعين علينا التعامل مع الواقع المؤسف للفوضى التي تتمثل في تحليل سطر أوامر Windows ، ولكن ، كقذيفة ، يتعين علينا أن نجعلها تعمل خلف الكواليس قدر الإمكان ، وهو ما تقوم به RFC يصف - على الرغم من أنني اكتشفت للتو ProcessStartInfo.ArgumentList ليس جيدًا بما فيه الكفاية ، للأسف (نظرًا لانتشار ملفات _ الدفعات _ كنقاط دخول CLI ، كما هو موضح بواسطة @ SteveL-MSFT az[.cmd] مثال --% .

ربما يمكن أن تساعد PSSA في التخفيف من التغيير المفاجئ من خلال تحذير المستخدمين من استخدام تنسيق وسيطة سيتم تغييره.

أعتقد أننا بحاجة إلى التفكير في اعتماد شيء مثل الميزات الاختيارية للمضي قدمًا في هذا التغيير المفاجئ جنبًا إلى جنب مع بعض التغييرات

هل حقا هناك أي قيمة في الحفاظ على السلوك الحالي؟ بخلاف التوافق مع الإصدارات السابقة ، أعني.

لا أعتقد أنه من المجدي الاحتفاظ بمسارين للتعليمات البرمجية لهذا الغرض ، ببساطة للاحتفاظ بتنفيذ معطل لأن بعض التعليمات البرمجية القديمة قد تحتاجه من حين لآخر. لا أعتقد أنه من غير المعقول توقع أن يقوم فوليكس بتحديث الكود الخاص بهم من حين لآخر. 😅

مضاعفة ذلك إذا كنا نتوقع أن يكون PS7 بديلاً احتياطيًا لـ WinPS ؛ السبب الوحيد الذي يمكنني رؤيته للاحتفاظ بسلوك الإصدار 7 هو إذا كنا نتوقع أن يستخدم الأشخاص نفس البرنامج النصي لتشغيل الأوامر على كل من 5.1 و 7 ، والتي (نأمل) أن تكون حالة نادرة جدًا إذا كان PS7 هو بديل جيد لـ 5.1.

وحتى مع ذلك ، لن يكون من الصعب للغاية على المستخدمين حساب كلا الأمرين. شريطة ألا نغير بنية اللغة الفعلية ، يجب أن يكون من السهل جدًا القيام بشيء مثل هذا:

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

شريطة أن نجعل المستخدمين على دراية بالفرق ، أعتقد أنه سيكون بمثابة راحة مرحب بها من الألم الذي كان عليه التعامل مع الملفات التنفيذية الأصلية الغريبة في PS حتى الآن. 😄

كما ذكر TylerLeonhardt في مناقشة الميزات الاختيارية - يعني التنفيذ أنك تحافظ الآن على _متعدد_التطبيقات المميزة التي يحتاج كل منها إلى الصيانة والاختبار ، بالإضافة إلى صيانة واختبار إطار عمل الميزة الاختيارية. لا يبدو حقًا يستحق كل هذا العناء ، tbh.

يعد التوافق مع الإصدارات السابقة من https://github.com/PowerShell/PowerShell/issues/1761 و https://github.com/PowerShell/PowerShell/issues/10675. "إصلاح" إمكانية التشغيل البيني مع الأوامر الأصلية هو شيء أود حله لـ vNext. لذلك إذا رأى أي شخص أي مشكلات حالية أو جديدة في هذه الفئة ، فقم بنسختي إليّ وسأضع علامة عليها بشكل مناسب (أو إذا كان لديك إذن فرز ، فضع علامة عليها مثل المشكلات الأخرى).

الميزات الاختيارية هي الوحدات النمطية :-) وجود ميزات اختيارية في المحرك يمثل صداعًا كبيرًا للدعم ، خاصة لدعم Windows. يمكننا تعديل المحرك عن طريق تقليل التبعيات الداخلية واستبدال واجهات برمجة التطبيقات الداخلية بواجهات عامة - بعد ذلك يمكننا تنفيذ ميزات اختيارية في المحرك بطريقة سهلة.

iSazonov أحد الأشياء التي سينظر إليها فريقي في vNext هو جعل المحرك أكثر نمطية :)

ما هو الحل الموصى به هنا للمستخدمين النهائيين؟

هذا مُبتكر ، لكنه الطريقة الأكثر مباشرة للوصول إلى معالجة ArgumentList الصحيحة من .NET framework نفسه:

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 .

* إخلاء المسؤولية: لا توجد طريقة صحيحة واحدة لتحليل cmdline على Windows. أعني بـ "الصحيح" نمط MSVCRT لـ cmdline من / إلى تحويل argv ، والذي تم تنفيذه بواسطة .NET على جميع الأنظمة الأساسية لمعالجة ArgumentList ومعالجة main (string[] args) والمكالمات الخارجية على Unix. يتم توفير هذه العينة كما هي مع عدم وجود ضمان لقابلية التشغيل البيني العام. راجع أيضًا قسم "سطر أوامر Windows" لوثائق عملية الطفل NodeJS المقترحة .

@ Artoria2e5 هذا بالضبط الاستنتاج الذي جئت إليه. System.Diagnostics.Process هي الطريقة الوحيدة الموثوقة لتشغيل الملف التنفيذي الخارجي ، ولكن الهروب من الحجج يمكن أن يكون صعبًا بسبب قواعد stdlib:

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 وحتى الآن بدون مشاكل ويمكنني تمرير الحجج باستخدام سطر جديد وأحرف خاصة أخرى فيها.

جيثب
ساهم في choovick / ps-invoke-externalcommand development عن طريق إنشاء حساب على GitHub.
جيثب
ساهم في choovick / ps-invoke-externalcommand development عن طريق إنشاء حساب على GitHub.

choovick كل شيء إعادة توجيه stdio رائع!

أنا أختلف قليلاً بشأن الجزء الهارب ، حيث يوجد بالفعل شيء يفعل ذلك من أجلك يسمى ArgumentList. أنا أفهم أنها إضافة حديثة نسبيًا (؟) ، وهي مخيبة للآمال لأن MS نسيت وضع مُهيئ String ، String [] لـ SDProcessStartInfo. (هل هناك مكان لهذه المقترحات… واجهة .NET؟)


لغو

تختلف وظيفة الهروب الخاصة بي من مثال NodeJS هذا قليلاً عن وظيفتك: فهي تستخدم الهروب غير الموثق (ولكن الموجود في .NET core و MSVCRT) "" لعلامات الاقتباس. القيام بذلك نوع من تبسيط عمل انتقاء الشرطة المائلة العكسية. لقد فعلت هذا بشكل أساسي لأنه تم استخدامه لكل شيء cmd العظيم ، والذي لا يفهم أن \" لا يجب أن يقتبس بقية السلسلة. بدلاً من الكفاح من أجل \^" أدركت أنني سأكون أفضل حالًا مع شيء كان قيد الاستخدام السري منذ بداية الوقت.

@ Artoria2e5 للأسف لا يتوفر ArgumentList في PowerShell 5.1 على windows ، باستخدام المثال الخاص بك الذي

"" لا يمكنك استدعاء طريقة على تعبير بقيمة خالية.
في C: Users \ yser \ dev \ test.ps1: 7 char: 37

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

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

    • FullyQualifiedErrorId: InvokeMethodOnNull

      ""

منذ منطق الهروب الوسيطة المخصصة ....

لغو

فيما يتعلق بـ "\ ^" `في NodeJS ، أعتقد أنه كان علي القيام بذلك قبل عدة سنوات :) وأعتقد أنه نجح

System.Diagnostics.Process هي الطريقة الوحيدة الموثوقة لتشغيل الملف التنفيذي الخارجي

تكمن المشكلة في أنك لن تحصل على تكامل مع تدفقات إخراج PowerShell ولن تحصل على سلوك دفق في خط الأنابيب.

فيما يلي ملخص الحلول المطلوبة إذا كنت لا تزال تريد السماح لـ PowerShell بتنفيذ الاستدعاء (وهو أمر مفضل بالتأكيد) :

  • إذا كنت بحاجة إلى تمرير الوسيطات مع _embedded_ " chars. ، _ مضاعفة_هم على Windows ، إن أمكن ، أو \ -ابتعد عنهم:

    • في نظام التشغيل Windows ، عند استدعاء _batch files_ وإذا كنت تعلم أن البرنامج الهدف يفهم "" أنه هروب " ، فاستخدم $arg -replace '"', '""'

      • يُفضل استخدام "" على Windows (فهو يتجنب مشكلة Windows PowerShell ويعمل مع CLIs التي تستخدم ملفات دفعية مثل _stubs_ ، مثل Node.js و Azure) ، ولكن لا تدعمها جميع الملفات التنفيذية (لا سيما Ruby و بيرل).
    • بخلاف ذلك (دائمًا على نظام Unix) ، استخدم $arg -replace '"', '\"'

    • ملاحظة: في _Windows PowerShell_ ، لا يزال هذا لا يعمل دائمًا بشكل صحيح إذا كانت القيمة تحتوي أيضًا على مسافات_ ، لأن وجود \" في القيمة لا يؤدي ظاهريًا إلى تضمين الاقتباس المزدوج ، على عكس PowerShell Core ؛ على سبيل المثال ، اجتياز فواصل '3\" of snow' .

    • بالإضافة إلى ذلك ، قبل الهروب أعلاه ، يجب عليك مضاعفة مثيل \ يسبق " ، إذا كان سيتم التعامل معها على أنها حرفية:

      • $arg = $arg -replace '(\\+)"', '$1$1"'
  • إذا كنت بحاجة إلى تمرير وسيطة _empty_ ، فمرر '""' .

    • '' -eq $arg ? '""' : $arg (بديل WinPS: ($arg, '""')['' -eq $arg]
  • Windows PowerShell فقط ، لا تفعل ذلك في PS Core (حيث تم إصلاح المشكلة):

    • إذا _contains حجتك spaces_ و_ends in_ (واحد أو أكثر) \ ، مضاعفة زائدة \ المثيل (ق).

      • if ($arg -match ' .*?(\\+)$') { $arg = $arg + $Matches[1] }
  • إذا تم استدعاء ملف دفعي cmd / مع وسيطات لا تحتوي على مسافات (لذلك _ لا _ يتم تشغيل الاقتباس المزدوج التلقائي بواسطة PowerShell) ولكنها تحتوي على أي من &|<>^,; (على سبيل المثال ، a&b ) ، استخدم تضمين اقتباس مزدوج مضمّن للتأكد من أن PowerShell يمرر رمزًا مميزًا مزدوج الاقتباس ، وبالتالي لا يكسر استدعاء الملف الدفعي cmd /:

    • $arg = '"' + $arg + '"'
  • إذا كنت بحاجة إلى التعامل مع ملفات تنفيذية سيئة التصرف مثل msiexec.exe ، فاقتبس من الوسيطة مرة واحدة:

    • 'foo="bar none"'

كما ذكرنا ، فإن هذه الحلول سوف _كسر_ ، بمجرد إصلاح المشكلة الأساسية.


يوجد أدناه وظيفة بسيطة (غير متقدمة) iep (لـ "استدعاء البرنامج الخارجي") ، والتي:

  • يؤدي كل عمليات الهروب الموضحة أعلاه ، بما في ذلك الغلاف التلقائي الخاص لـ msiexec ويفضل الهروب "" على \" اعتمادًا على البرنامج المستهدف.

    • الفكرة هي أنه يمكنك تمرير أي وسيطة من خلال التركيز على بناء جملة سلسلة _PowerShell_ فقط ، والاعتماد على الوظيفة لأداء الهروب الضروري بحيث يتم أيضًا رؤية القيمة الحرفية التي يراها PowerShell بواسطة البرنامج الهدف.

    • في PowerShell _Core_ ، يجب أن يعمل هذا بقوة كبيرة ؛ في Windows PowerShell ، لا يزال لديك حالات حافة بعلامات اقتباس مزدوجة مضمنة تنقطع إذا كان يجب استخدام الهروب \" (كما تمت مناقشته أعلاه)

  • يحافظ على صيغة استدعاء أوامر shell.

    • ببساطة قم بإرفاق iep  إلى سطر الأوامر.

  • كما أن الاحتجاج المباشر:

    • يتكامل مع تدفقات PowerShell

    • يرسل خط الإخراج سطراً عبر خط الأنابيب

    • يحدد $LASTEXITCODE بناءً على كود الخروج من البرنامج الخارجي ؛ ومع ذلك ، لا يمكن الاعتماد على $? .

ملحوظة: الوظيفة هي أضيق الحدود عن قصد (لا توجد تصريحات للمعلمات ، لا تعليمات لسطر الأوامر ، اسم قصير (غير منتظم)) ، لأنه من المفترض أن تكون غير مزعجة قدر الإمكان: ببساطة قم بإرفاق iep إلى سطر الأوامر ، والأشياء يجب أن تعمل.

مثال على استدعاء باستخدام EchoArgs.exe (قابل للتثبيت عبر Chocolatey من جلسة _elevated_ مع choco install echoargs -y ):

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"

ما سبق يوضح إخراج PowerShell Core. لاحظ كيف تم تمرير جميع الوسائط بشكل صحيح كما يراها PowerShell حرفيًا ، بما في ذلك الوسيطة الفارغة.

في Windows PowerShell ، لن يتم تمرير الوسيطة 3" of snow بشكل صحيح ، لأنه يتم استخدام الهروب \" بسبب استدعاء ملف تنفيذي غير معروف (كما تمت مناقشته أعلاه).

للتحقق من أن الملفات الدفعية تمرر الوسائط بشكل صحيح من خلالها ، يمكنك إنشاء echoargs.cmd كغلاف لـ echoargs.exe :

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

استدعاء كـ iep .\echoargs.cmd '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'

نظرًا لأنه يتم استدعاء ملف دفعي الآن ، يتم استخدام "" -escaping ، والذي يعمل على إصلاح مشكلة 3" of snow عند الاتصال من Windows PowerShell.

تعمل الوظيفة بشكل متساوٍ على الأنظمة الأساسية الشبيهة بـ Unix ، والتي يمكنك التحقق منها عن طريق إنشاء برنامج نصي شل sh باسم echoargs :

@'
#!/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'


هام : تم نشر نسخة أكثر اكتمالا من هذه الوظيفة على أنها ie ( أنا nvoke (خارجي) E xecutable) في لقد قمت للتو بنشر وحدة Native ، والتي أشجعك على استخدامها في حين أن. قم بتثبيت الوحدة باستخدام
Install-Module Native -Scope CurrentUser .
تحتوي الوحدة أيضًا على أمر ins ( Invoke-NativeShell ) الذي يعالج حالة الاستخدام التي تمت مناقشتها في # 13068 - راجع 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 مثير للإعجاب ، ولكن إليك زوجين لا يعملان معي على النوافذ:

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: العملية لها قيود عند استخدامها ، ولهذا السبب اضطررت إلى كتابة عداء خاص بي لديه القدرة على التقاط STDOUT STDERR والجمع بين المتغيرات ، وهو ما يكفي لحالات الاستخدام الخاصة بي ، ولكن ليس مثاليًا.

تحرير: كما قلت ، يبدو أنه يحتوي على حالات متطورة في Windows Poweshell 5. لقد اختبرت على Powershell 6 وهو يعمل بشكل رائع! لسوء الحظ ، لا بد لي من التعامل مع Poweshell 5 حتى يتولى 7 في حالة Windows ...

مثير للإعجاب ، ولكن إليك زوجين لا يعملان معي على النوافذ:

نعم ، تنطبق هذه القيود على _Windows PowerShell_ ، مع الملفات التنفيذية التي لا يمكن افتراض دعمها لـ "" المضمن لـ " ، كما هو مفصل في تعليقي السابق.
إذا كنت على استعداد لتحمل دعم هروب "" في جميع استدعاءاتك (_ most_ ، ولكن لا تدعمها جميع الملفات التنفيذية على Windows) ، يمكنك بسهولة تعديل الوظيفة.

ولتأكيد ما قلته في تعديلك: في PowerShell _Core_:

  • iep echoargs 'somekey="value with spaces"' 'te\" st' يعمل بشكل جيد.
  • يبدو أن مجموعة الحجج الاختبارية الخاصة بك تعمل بشكل جيد أيضًا.

إذا كنت على استعداد لتحمل دعم هروب "" في جميع استدعاءاتك (_ most_ ، ولكن لا تدعمه جميع الملفات التنفيذية على Windows) ، يمكنك بسهولة تعديل الوظيفة.

شكرا سأجربها في المستقبل القريب. أنا أتعامل مع sqlcmd في بعض الأحيان وهو لا يدعم "" بالتأكيد. لهذه الحالة - من السهل توفير خيار لتخطي منطق الهروب لوسائط محددة.

يمكن العثور على الطريقة التي يوزع بها Windows وسيطات سطر الأوامر في Parsing C ++ وسيطات سطر الأوامر :

يستخدم رمز بدء تشغيل Microsoft C / C ++ القواعد التالية عند تفسير الوسائط الواردة في سطر أوامر نظام التشغيل:

  • يتم تحديد الوسيطات بمسافة بيضاء ، والتي تكون إما مسافة أو علامة تبويب.

  • لم يتم التعرف على حرف الإقحام (^) كحرف هروب أو محدد. تتم معالجة الحرف بالكامل بواسطة محلل سطر الأوامر في نظام التشغيل قبل تمريره إلى المصفوفة argv في البرنامج.

  • يتم تفسير سلسلة محاطة بعلامات اقتباس مزدوجة (" سلسلة ") على أنها وسيطة واحدة ، بغض النظر عن المسافة البيضاء الموجودة داخلها. يمكن تضمين سلسلة مقتبسة في وسيطة.

  • يتم تفسير علامة الاقتباس المزدوجة المسبوقة بشرطة مائلة للخلف (\ ") على أنها حرف علامة اقتباس مزدوج حرفي (").

  • يتم تفسير الخطوط المائلة للخلف حرفياً ، إلا إذا كانت تسبق مباشرةً علامة اقتباس مزدوجة.

  • إذا تبع عدد زوجي من الشرطات المائلة للخلف علامة اقتباس مزدوجة ، يتم وضع خط مائل عكسي واحد في المصفوفة argv لكل زوج من الشرطات المائلة للخلف ، ويتم تفسير علامة الاقتباس المزدوجة على أنها محدد سلسلة.

  • إذا كان عدد فردي من الشرطات المائلة للخلف متبوعًا بعلامة اقتباس مزدوجة ، فسيتم وضع شرطة مائلة للخلف واحدة في المصفوفة argv لكل زوج من الشرطات المائلة للخلف ، وعلامة الاقتباس المزدوجة يتم "تجاوزها" بواسطة الشرطة المائلة العكسية المتبقية ، مما يؤدي إلى ظهور حرفية علامة اقتباس مزدوجة (") توضع في argv .

تمت مناقشة هذا أيضًا في دالات _exec، _wexec :

قد تتسبب المسافات المضمنة في السلاسل في حدوث سلوك غير متوقع ؛ على سبيل المثال ، تمرير _exec السلسلة "hi there" سيؤدي إلى حصول العملية الجديدة على وسيطين ، "hi" و "there" . إذا كان القصد من العملية الجديدة فتح ملف باسم "hi there" ، فستفشل العملية. يمكنك تجنب ذلك باقتباس الجملة: "\"hi there\"" .

كيف تستدعي بايثون الملفات التنفيذية الأصلية

يمكن لـ Python subprocess.Popen معالجة الهروب بشكل صحيح عن طريق تحويل args إلى سلسلة بطريقة موصوفة في تحويل تسلسل وسيطة إلى سلسلة في Windows . التنفيذ هو subprocess.list2cmdline .

آمل أن يلقي هذا بعض الضوء على كيفية تعامل PowerShell مع هذا بطريقة أكثر أناقة ، بدلاً من استخدام --% أو الهروب المزدوج (باتباع كل من بناء جملة PowerShell و CMD). الحلول الحالية تزعج عملاء Azure CLI (الذي يعتمد على python.exe).

ما زلنا لا نملك طريقة واضحة وموجزة لفهم كيفية التعامل مع هذه المشكلة. هل يمكن لأي شخص من فريق Powershell إلقاء الضوء إذا تم تبسيط ذلك باستخدام الإصدار 7؟

حسنًا ، الخبر السار هو أن أحد نقاط التركيز في PS 7.1 هو تسهيل استدعاء الأوامر الأصلية. من منشور مدونة على 7.1 استثمارات :

تعمل معظم الأوامر الأصلية بشكل جيد من داخل PowerShell ، ومع ذلك ، هناك بعض الحالات التي يكون فيها تحليل الوسيطة غير مثالي (مثل التعامل مع علامات الاقتباس بشكل صحيح). القصد من ذلك هو تمكين المستخدمين من قص عينة من أسطر الأوامر لأي أداة أصلية شائعة ، ولصقها في PowerShell ، وهي تعمل فقط دون الحاجة إلى هروب معين من PowerShell.

لذلك ربما (نأمل) أن تتم معالجة هذا في 7.1

شكرًا ، rkeithhill ، لكن لا:

القصد من ذلك هو تمكين المستخدمين من قص عينة من أسطر الأوامر لأي أداة أصلية شائعة ، ولصقها في PowerShell ، وهي تعمل فقط دون الحاجة إلى هروب معين من PowerShell.

يبدو وكأنه أخذ آخر على مشكلة من الناحية المفاهيمية --% (رمز إيقاف التحليل)؟

@ SteveL-MSFT ، هل يمكنك إخبارنا بالمزيد حول هذه الميزة القادمة؟


للتلخيص ، الإصلاح المقترح هنا هو أن كل ما تحتاج إلى التركيز عليه هو تلبية متطلبات بناء جملة _PowerShell_ وأن PowerShell يتولى جميع عمليات الهروب خلف الكواليس (على نظام التشغيل Windows ؛ لم يعد هذا ضروريًا في نظام التشغيل Unix ، والآن بعد أن تسمح .NET لتمرير مجموعة من الرموز المميزة الحرفية إلى البرنامج المستهدف) - ولكن ليس هناك شك في أن الإصلاح يجب أن يكون مشتركًا ، إذا كان يجب الحفاظ على التوافق مع الإصدارات السابقة.

مع هذا الإصلاح ، _ بعض_ العبث بسطر الأوامر للقذائف الأخرى سيظل مطلوبًا ، ولكنه سيكون أكثر وضوحًا - وبالمقارنة بـ --% ، فإنك تحتفظ بالقوة الكاملة لبناء جملة PowerShell والتوسعات المتغيرة (التعبيرات بـ (...) ، إعادة التوجيه ، ...):

  • تحتاج إلى استبدال \" بـ `" ، لكن _فقط داخل "..." _.

  • يجب أن تدرك أن _ no (other) shell_ متضمنة ، لذا فإن مراجع متغير البيئة على غرار Bash مثل $USER لن تعمل (سيتم تفسيرها على أنها متغيرات _PowerShell_) ، ما لم تستبدلها بمتغيرات صيغة PowerShell المكافئة ، $env:USER .

    • جانبا: يحاول --% تعويض ذلك عن طريق - على وجه الخصوص _ بشكل متغير_ - توسيع cmd.exe -style لمراجع البيئة مثل %USERNAME% ، لكن لاحظ أنه ليس فقط ' المراجع ر الدعم على غرار باش ( $USER ) يتم تمرير _verbatim_ لبرنامج الهدف، ولكن أيضا توسع بشكل غير متوقع cmd.exe على غرار ما يشير على يونكس مثل المنصات، ولا تعترف '...' الاقتباس.
    • انظر أدناه للحصول على بديل _does_ يتضمن shell الأصلي للنظام الأساسي المعني.
  • يجب أن تدرك أن PowerShell يحتوي _إضافية_ على أحرف أولية تتطلب الاقتباس / الهروب للاستخدام الحرفي ؛ هذه هي (لاحظ أن @ يمثل مشكلة فقط لأن الوسيطة _first_ char.):

    • للأصداف التي تشبه 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

هذا يعني أنك لن تقلق بشأن تضمين " داخل '...' ، وداخل "..." عليك سوى الهروب منهم لجعل _PowerShell_ سعيدًا (وهو ما يمكنك فعله أيضًا مع "" هنا).

تقوم وظيفة 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

مع الإصلاح في مكانه ، سيتمكن أولئك الذين يرغبون في استخدام سطر أوامر معين _as-is_ عبر _الصدفة الافتراضية للنظام الأساسي _ من استخدام حرف _ هنا سلسلة_ للتمرير إلى sh -c (أو bash -c ) / cmd /c ؛ على سبيل المثال:

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

لاحظ أن استخدام --% _not_ يعمل هنا ( printf --% '"%s"\n' "3\" of snow" ) ، والميزة المضافة للنهج القائم على السلسلة هنا هي أن القيود المختلفة --% لا تنطبق ، ولا سيما عدم القدرة على استخدام إعادة توجيه الإخراج ( > ).

إذا قمت بالتبديل إلى سلسلة هنا _double_ مقتبسة ( @"<newline>....<newline>"@ ) ، يمكنك حتى تضمين _متغيرات وتعبيرات PowerShell _ ، على عكس --% ؛ ومع ذلك ، فأنت بحاجة بعد ذلك إلى التأكد من أن القيم الموسعة لا تكسر بناء جملة الغلاف الهدف.

يمكننا التفكير في أمر cmdlet مخصص مع اسم مستعار مختصر (على سبيل المثال ، Invoke-NativeShell / ins ) لمثل هذه المكالمات (بحيث لا تحتاج sh -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"")'

بناء جملة باش:

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

اقتراح باورشيل:

إذا كان فريق PowerShell يبحث فقط عن رمز ، لا يكسر الصيغة السابقة ، فلماذا لا تستخدم شيئًا مثل backticks `` لسلوك يشبه Bash ، والذي يهرب من القيم الحرفية تلقائيًا ، ويسمح باستيفاء السلسلة. هذا يشبه بناء جملة JavaScript أيضًا.

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

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

لماذا لا تستخدم شيئًا مثل backticks لسلوك يشبه Bash

يتم استخدام Backticks بالفعل للهروب من الأشياء ، وهو نفس الغرض مثل backslahions في POSIX shell. التعليق الذي تشير إليه يشير إلى ذلك بالفعل. لقد استخدمنا جميع الأشياء التي تشبه اقتباس ASCII. قد تنجح إضافة بادئة $ إلى السلاسل الحرفية العادية ، لكنني لا أعتقد أنها منطقية بما فيه الكفاية.


يمكن العثور على الطريقة التي يوزع بها Windows وسيطات سطر الأوامر في ...

تكمن المشكلة في أن Windows MSVCR لا يفعل ذلك فقط: إنه يتعامل مع الحالات الجانبية بطرق غير موثقة . تم ضبط العناصر "" بقوة لدرجة أنهم وضعوها في CoreFX عندما قاموا بنقل .NET إلى Unix. لكن على أي حال ، دائمًا ما يكون جيدًا بما يكفي للهروب ، على الأقل حتى يسأل شخص ما عن الالتفاف.

هناك أيضًا المشكلة الكلاسيكية المتمثلة في قيام كل شخص بذلك بشكل مختلف ، ولكن لا داعي للقلق بشأن ذلك لأن لدينا دائمًا .NET لـ cmdline الخام.

لماذا لا تستخدم شيئًا مثل backticks لسلوك يشبه Bash

يتم استخدام Backticks بالفعل للهروب من الأشياء ، وهو نفس الغرض مثل backslahions في POSIX shell. لقد استخدمنا جميع الأشياء التي تشبه اقتباس ASCII.

هناك إمكانية لاستخدام مجموعة من الرموز إذا لم يتمكن المحلل اللغوي من اكتشاف ذلك هنا `` يتم إدخال سلسلة. قد يعمل شيء مثل '' .

aminya هنا حل هو أنك لا تستخدم Windows Powershell 5.1 القديم و 6+:
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606

نظرًا لأنني مضطر للتعامل مع PowerShell 5،1 على نظامي التشغيل windows و 6+ على نظام التشغيل Linux / mac ، فإنني أمتلك تطبيقًا خاصًا بي يعمل دون مشاكل لسنوات يتيح لي العمل مع أدوات مثل kubectl و helm و terraform وغيرها من تمرير JSON المعقدة كائنات ضمن المعلمات:
https://github.com/choovick/ps-invoke-externalcommand

جيثب
ساهم في choovick / ps-invoke-externalcommand development عن طريق إنشاء حساب على GitHub.

choovick تم تقديم تنفيذ أقصر إلى حد ما أعلاه في هذا الموضوع ، ما زلت أعثر على حالة قد تفشل فيها بالنسبة لي. يعمل هذا في PS بدءًا من الإصدار 3.

إن وظيفة AndrewSavTSlivede Run-Native ذكية وموجزة للغاية ، وتستحق الثناء أيضًا في _Windows PowerShell_؛ بعض الأشياء الجديرة بالملاحظة: بالضرورة ، ترى عملية الطفل المساعدة. commandlineargumentstring متغير بيئة (نادرًا ما يكون هناك مشكلة في الممارسة العملية) ، لا يتم التعامل مع الاستدعاءات الخالية من الحجج حاليًا بشكل صحيح (يتم إصلاحها بسهولة ولا حتى مشكلة ، إذا تأكدت من أنك تستخدم الوظيفة فقط _ مع _ الوسيطات ، وهي ما هي عليه) ، _جميع_ الوسيطات يتم اقتباسها مرتين (على سبيل المثال ، يتم تمرير شيء مثل 42 كـ "42" ) ، والذي يمكن (للأسف) أن يكون له آثار جانبية لـ البرامج التي تفسر الوسيطات ذات الاقتباس المزدوج (أو ذات علامات الاقتباس المزدوجة جزئيًا ، كما في حالة msiexec ) بشكل مختلف.

aminya ، السبب الوحيد وراء نجاح node -e 'console.log(`"hey`")' (نوعًا ما) هو السلوك الحالي المكسور (انظر أدناه). أفترض أن ما كنت تقصده هو _verbatim_ console.log("hey") ، والذي ، إذا نجا PowerShell على Windows من الأشياء بشكل صحيح كما هو مقترح هنا ، فستمرر كما هو في علامات الاقتباس الفردية: node -e 'console.log("hey")' . يجب أن يترجم هذا _automatically_ إلى node -e "console.log(\"hey\")" (اقتباس مزدوج ، \ -فلت حرفيا " ) _ خلف الكواليس_.

بالنظر إلى المدة التي استغرقها هذا الموضوع ، اسمحوا لي أن أحاول تلخيص:
لا يجب أن تقلق أبدًا إلا بشأن متطلبات بناء جملة _PowerShell_ ، وهي مهمة PowerShell _ as a shell_ للتأكد من أن _قيم الوسيطة الحركية_ التي تنتج من تحليل PowerShell الخاص يتم تمريرها إلى البرنامج الخارجي _as-is_.

  • على الأنظمة الأساسية الشبيهة بـ Unix ، بعد أن أصبح لدينا الآن دعم .NET Core لها ، يعد القيام بذلك أمرًا تافهًا ، حيث يمكن تمرير القيم الحرفية كما هي كعناصر مجموعة من الوسيطات ، وهي الطريقة التي تستقبل بها البرامج الحجج بشكل أصلي هناك .
  • في نظام التشغيل Windows ، تتلقى البرامج الخارجية وسيطات كسلسلة سطر أوامر واحدة_ (قرار تصميم تاريخي مؤسف) ويجب أن تقوم بتحليل سطر الأوامر الخاص بها. يتطلب تمرير وسيطات متعددة كجزء من سلسلة واحدة اقتباس وقواعد تحليل من أجل تحديد الحجج بشكل صحيح ؛ في حين أن هذا في النهاية مجاني للجميع (البرامج مجانية التحليل كيفما تشاء) ، فإن أكثر قواعد التركيب استخدامًا (كما هو مذكور من قبل والمقترح أيضًا في RFC التي تم التخلي عنها الآن للأسف) هي ما ينفذه مترجمي C / C ++ من Microsoft ، لذلك فمن المنطقي أن تتماشى مع ذلك.

    • _Update_: حتى على نظام التشغيل Windows ، يمكننا الاستفادة من خاصية جمع الرموز الحرفية ArgumentList System.Diagnostics.ProcessStartInfo في .NET Core: في Windows تتم ترجمتها تلقائيًا إلى أمر مقتبس ومهرب بشكل صحيح - سلسلة الخط عند بدء العملية ؛ ومع ذلك ، بالنسبة لملفات الدفعات ، قد لا نزال بحاجة إلى معالجة خاصة - راجع https://github.com/PowerShell/PowerShell-RFC/pull/90#issuecomment -552231174

إن تنفيذ ما ورد أعلاه هو بلا شك تغيير هائل ، لذلك من المفترض أنه يتطلب الاشتراك.
أعتقد أن المضي قدمًا في هذا أمر لا بد منه ، إذا أردنا التخلص من كل صداع الاقتباس الحالي ، والذي يستمر في الظهور ويعيق اعتماد PowerShell ، خاصة في عالم Unix.


بالنسبة إلى node -e 'console.log(`"hey`")' : داخل '...' ، لا تستخدم ` - إلا إذا كنت تريد تمرير هذا الحرف كما هو. لأن PowerShell _doesn't_ حاليًا يتخطى الحرف الحرفي " chars. في حجتك مثل \" خلف الكواليس ، ما يراه node في سطر الأوامر هو console.log(`"hey`") ، والذي يتم تحليله كسلسلتين حرفيتين متجاورتين مباشرة: console.log(` و "hey`" مزدوج الاقتباس. بعد تجريد " ، الذي يحتوي على وظيفة _syntactic_ نظرًا لأنه لم يتم إلغاء \ ، يتم تنفيذ شفرة JavaScript في النهاية console.log(`hey`) ، وهذا يحدث فقط للعمل بسبب `....` هو شكل من أشكال السلسلة الحرفية في JavaScript ، أي _template literal_.

AndrewSav لقد اختبرت مع كائن الاختبار المجنون الخاص بي وقد نجح معي! حل أنيق للغاية ، تم اختباره على نظامي التشغيل Windows 5.1 و PS 7 على نظام التشغيل Linux. أنا موافق على اقتباس كل شيء بشكل مزدوج ، فأنا لا أتعامل مع msiexec أو sqlcmd المعروف أيضًا أنه يعامل " صراحة.

يحتوي تطبيقي الشخصي أيضًا على منطق هروب بسيط مشابه لما ذكرته: https://github.com/choovick/ps-invoke-externalcommand/blob/master/ExternalCommand/ExternalCommand.psm1#L278

لكنني كتبت مجموعة من التعليمات البرمجية لعرض والتقاط سلاسل STDOUT و STDERR في الوقت الفعلي ضمن تلك الوحدة ... ربما يمكن تبسيطها إلى حد كبير ، لكن لم أكن بحاجة ...

@ mklement0 لن ينتهي هذا الموضوع أبدًا (: إما أننا نحتاج إلى توفير وحدة PowerShell منشورة في معرض ps والتي ستناسب معظم حالات الاستخدام وستكون بسيطة بما يكفي للاستخدام ، أو ننتظر تحسينات shell القادمة.

جيثب
ساهم في choovick / ps-invoke-externalcommand development عن طريق إنشاء حساب على GitHub.

@ mklement0 لن ينتهي هذا الموضوع أبدًا (: إما أننا نحتاج إلى توفير وحدة PowerShell منشورة في معرض ps والتي ستناسب معظم حالات الاستخدام وستكون بسيطة بما يكفي للاستخدام ، أو ننتظر تحسينات shell القادمة.

إذا قرر فريق PowerShell عدم إصلاح هذا في البرنامج نفسه ، فسأقول بعد ذلك أن الصدفة التي لا يمكنها تشغيل البرامج الخارجية أصلاً وبشكل صحيح لن تكون الصدفة التي اخترتها! هذه هي الأشياء الأساسية المفقودة من PowerShell.

aminya نعم ، لقد وجدت أن هذه المشكلة مزعجة جدًا لنفسي عندما اضطررت إلى الانتقال إليها. لكن الميزات مثل أدناه جعلت الأمر يستحق ذلك.

  • إطار عمل مرن للمعلمات ، من السهل بناء CMDlets موثوقة.
  • الوحدات النمطية ومستودعات الوحدات النمطية الداخلية لتبادل المنطق المشترك عبر المؤسسة ، وتجنب الكثير من تكرار التعليمات البرمجية ومركزية الوظائف الأساسية التي يمكن إعادة بنائها في مكان واحد.
  • عبر منصة. لدي أشخاص يقومون بتشغيل أدواتي على Windows في PS 5.1 وعلى نظام Linux / mac في PS 6/7

آمل حقًا أن يقوم فريق PS بتحسين هذا في المستقبل لجعله أقل تعقيدًا.

إن تنفيذ ما ورد أعلاه هو بلا شك تغيير هائل ، لذلك من المفترض أنه يتطلب الاشتراك.

هل تقصد تنفيذ https://github.com/PowerShell/PowerShell-RFC/pull/90؟

aminya ، أوافق على أن استدعاء البرامج الخارجية بالحجج هو تفويض أساسي

لا تزال الوحدة النمطية ، كما اقترح choovick ، منطقية بالنسبة إلى _Windows PowerShell_ ، والتي تعمل في وضع صيانة الإصلاحات الأمنية فقط.
بشكل تكميلي ، إذا / عندما تتم كتابة موضوع المساعدة التصوري المقترح حول استدعاء البرامج الخارجية - راجع https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152 - وظيفة مساعدة تصحح المشكلات في الإصدارات السابقة / Windows PowerShell's ، مثل TSlivede أعلاه ، يمكن نشرها هناك مباشرة.

iSazonov نعم ، تنفيذ https://github.com/PowerShell/PowerShell-RFC/pull/90 هو ما قصدته.

أما بالنسبة للخيط الذي لا ينتهي أبدًا ومخاوف التكسر:

كان آخر رد رسمي على طلب التعليقات المرتبط هو هذا التعليق من joeyaiello اعتبارًا من 8 يوليو 2019 (تم إضافة التأكيد):

لكننا نعتقد أن هذا [RFC] له معنى كبير بغض النظر عن السلوك الحالي وبغض النظر عن كسره . الآن بعد أن أصبح لدينا ميزات تجريبية ، نعتقد أنه من المعقول تمامًا أن نبدأ وننفذ هذا اليوم خلف علامة ميزة تجريبية ، ويمكننا معرفة ما إذا كان هذا هو سلوك التمكين أم عدم الاشتراك ، وما إذا كان هناك بعض الانتقال المسار ، وإذا كان متغير التفضيل هو الآلية الصحيحة لتشغيله وإيقاف تشغيله.

_ شخصيًا_ ، لا أمانع في إصلاح السلوك _ افتراضيًا_ ، على الرغم من أنه تغيير جذري ؛ يقترح RFC ذلك بالفعل ويقترح الاشتراك إذا كنت تريد السلوك _old_ (المعطل).

أظن أن الأشخاص الذين لديهم تعليمات برمجية قديمة يجب صيانتها سيعترضون ، على الرغم من ذلك ، لأن _ جميع الحلول الحالية ستتوقف عن العمل _ - انظر أعلاه - ولا يزال الحفاظ على التوافق مع الإصدارات السابقة هو الهدف الشامل.

إذا تم اختيار السلوك الجديد الثابت ، فلا يزال لديك الإحراج من الاضطرار إلى القيام بشيء ما فقط للحصول على السلوك الصحيح ، ولكن على الأقل لن يتم كسر الشفرة الحالية.

لكن آليات التقيد الحالية هي نفسها إشكالية:
الآن بعد أن تم رفض ميزات RFC الاختيارية لـ KirkMunro ، فإن هذا يترك إلى حد كبير _متغير تفضيل_ ، والتحدي هو النطاق الديناميكي لـ PowerShell: يتم استدعاء رمز جهة خارجية من نطاق تم اختياره ولم يتم تصميمه لاستخدام قد يتعطل التنفيذ عندئذٍ (ما لم تتم إعادة تعيين متغير التفضيل مؤقتًا).

مطلوب هنا تحديد نطاق _Lexical_ للاشتراك ، وهو ما لا نملكه حاليًا. يقترح RFC لتحديد نطاق _lexical_ للوضع المتشدد تنفيذ ميزة ذات نطاق معجمي (أو غرض مختلف) ، عبر عبارة using (والتي ، بشكل ملحوظ ، بشكل عام _ ديناميكي_ النطاق). باتباع هذا النمط ، فإن عبارة using ProperExternalArgumentQuoting النطاق المعجمي (الاسم هو WIP :) - إذا كان ذلك ممكنًا من الناحية الفنية - يستحق النظر.

نحن بحاجة إلى لجنة PowerShell للتأثير (مرة أخرى) وتقديم إرشادات واضحة بشأن الطريق إلى الأمام ، مع تعليقات - في الوقت المناسب - على الأسئلة عند ظهورها. @ SteveL-MSFT؟


لاحظ أن الحل الذي يشبه --% التلميح إليه من خلال منشور مدونة 7.1 (انظر أعلاه ) (والذي أعتقد شخصيًا أنه لا يستحق المتابعة - انظر أعلاه وهذا التعليق ) ، سيكون ميزة _ منفصلة_ - إصلاح أصل PowerShell (عدم مضاهاة) السلوك لا يزال ضروريًا.

أنا شخصياً لا أمانع في إصلاح السلوك بشكل افتراضي ، على الرغم من أنه تغيير جذري ؛ يقترح RFC ذلك بالفعل ويقترح الاشتراك إذا كنت تريد السلوك القديم (المعطل).

إذا تم اختيار السلوك الجديد الثابت ، فلا يزال لديك الإحراج من الاضطرار إلى القيام بشيء ما فقط للحصول على السلوك الصحيح ، ولكن على الأقل لن يتم كسر الشفرة الحالية.

متفق عليه ، في الحقيقة سأجادل بأنه من غير المنطقي أن يكون لديك تقصير معطل ، ثم تقصير تم تنفيذه بشكل صحيح. بالنظر إلى حقيقة أن التطبيق الحالي هو في الواقع خطأ ، يجب أن يكون السلوك الجديد هو الاشتراك وليس إلغاء الاشتراك ، لأنه ليس من المنطقي الاستمرار في تشجيع استدعاءات shell الخارجية المعطلة التي تكون عرضة للانقطاع غير المتوقع طرق. على أي حال ، يجب أن يسعى PowerShell 7 إلى تحسين نظام Windows PowerShell القديم.

@ SteveL-MSFT وأنا اتفقت على أننا يجب أن نغلق هذا لصالح # 13068. أي شيء نتطرق إليه هنا هو مجرد تغيير كبير للغاية ، ويجب أن نعالج المشكلة مع مشغل جديد يعمل بمثابة وضع الاشتراك.

لا أرى مطلقًا كيف سيحل # 13068 هذا: إذا تم تقديم هذا العامل على النحو المنشود ، فلا يزال لدينا طريقة لاستدعاء أي ملف أصلي قابل للتنفيذ بشكل صحيح مع مجموعة معينة من الوسائط أو مع بعض الوسائط الصريحة التي ينشأ محتواها من المتغيرات.

المثال ، الذي قدمهJustinGrote في سلسلة الرسائل هذه لا يعمل حاليًا بشكل موثوق (إذا كانت علامات الاقتباس المضمنة ممكنة في حمولة الوسيطة) وإضافة هذا العامل لن يعطي أي بديل يحسن أي شيء.

joeyaiello هل يمكنك على الأقل ترك هذه المشكلة مفتوحة حتى يتواجد هذا المشغل بالفعل ويمكن لأي شخص أن يوضح ، كيف يمكن لهذا العامل أن يحسن أي شيء تم ذكره في هذا الموضوع؟

أوه وأيضًا ماذا عن Linux؟ هذه المشكلة غبية وغير متوقعة على نظام التشغيل Windows ، ولكنها تبدو أقل منطقية على نظام Linux ، خاصةً أنه لا يوجد تاريخ طويل لنصوص لينكس بوويرشيل التي ستتوقف.

إن إنشاء مشغل خاص لهذا الأمر غير منطقي على الإطلاق بالنسبة لقذيفة سطر الأوامر ، نظرًا لأن وظيفتها الرئيسية هي إطلاق البرامج وتمرير الحجج لها. إن تقديم عامل تشغيل جديد يقوم بـ system() لهذه الوظيفة يشبه تقديم Matlab طريقة للاتصال بـ calc.exe لأنه يحتوي على خطأ في علم الحساب. ما يجب فعله بدلاً من ذلك هو:

  • يستعد فريق pwsh لإصدار رئيسي جديد يعمل على إصلاح عناصر سطر الأوامر ، ونقل السلوك الحالي خلف أمر cmdlet مدمج.
  • كحل مؤقت ، يحصل إصدار pwsh القادم على أمر cmdlet المدمج الذي يستخدم السلوك الجديد الصحيح لتمرير سطر الأوامر.

الأمر نفسه ينطبق على Start-Process . (في الواقع إنه مرشح جيد جدًا لـ cmdlet "الجديد" مع بعض الخيارات مثل -QuotingBehavior Legacy ...) راجع # 13089.

لماذا يتصرف Powershell بشكل مختلف في هاتين الحالتين؟ على وجه التحديد ، يتم تغليف الوسائط التي تحتوي على مسافات بين علامات الاقتباس المزدوجة بشكل غير متسق.

أحصل على نتائج متسقة في الإصدار 7. يبدو أنه ثابت.

PING 'A \"B'

تعذر على طلب Ping العثور على المضيف أ "ب.

PING 'A\" B'

تعذر على طلب Ping العثور على المضيف أ "ب.

لم يتم إصلاحه ، لأن أسماء المضيف الحرفية التي يجب أن يراها ping هي A \"B و A\" B - _ مع_ \ أحرف.

يجب أن يقوم PowerShell ، كقذيفة ، بتحليل الوسائط وفقًا لقواعد _its_ - فقط - ثم التأكد بشفافية من أن العملية المستهدفة ترى نفس القيم الحرفية التي كانت نتيجة تحليل PowerShell الخاص.

تلك الأصداف الأخرى - وتلك البرامج الضعيفة التي تعمل على Windows والتي يجب أن تعمل مثل الصدفة الخاصة بها ، بطريقة التحدث ، من خلال الاضطرار إلى تحليل سطر الأوامر فقط لاستخراج الوسيطات الفردية التي تم تمريرها - استخدم \ كمهرب لا يجب إدخال الحرف إلى الصورة هنا - استيعاب ذلك (ضروري على Windows فقط ، في نظام Unix يمكنك تمرير الحجج الحرفية مباشرة كمصفوفة) هو عمل PowerShell كقذيفة ، _خلف المشاهد_.

جانبا ، تماما مثل PowerShell نفسها لا تتطلب الهروب من " _inside '...' _ (سلاسل مقتبسة منفردة) ، ولا القذائف المتوافقة مع POSIX مثل bash : تم تنفيذه من bash ، على سبيل المثال ، /bin/echo 'A \"B' (بشكل منطقي) يطبع A \"B (يتم التعامل مع \ على أنها حرفية في سلاسل ذات علامات اقتباس مفردة) - التي تنفذ نفس الأمر من PowerShell (بشكل غير متوقع) ينتج
A "B - \ مفقود - هو مظهر من مظاهر المشاكل التي نوقشت هنا.

يجب أن أوضح:

  • من منظور الرغبة في اجتياز A "B _verbatim_ ، يجب أن تكون قادرًا على استخدام 'A "B' من PowerShell.

  • سطر الأوامر الذي ينشئه PowerShell حاليًا خلف الكواليس يحتوي على "A "B" - والذي تراه العملية المستهدفة A B - أي ، العلبة العمياء في "..." ، دون الهروب من _embedded_ " إلى الخسارة الفعلية لـ " المضمّن. ما يستخدمه PowerShell _should_ في سطر أوامر ما وراء الكواليس في هذه الحالة هو "A \"B" - أي أن " المضمن يحتاج \ -escaping.

  • وبالمثل ، يتسبب الغلاف الأعمى نفسه في تمثيل 'A \"B' كـ "A \"B" في سطر أوامر ما وراء الكواليس ، والذي يحدث تمامًا لتحويل _embedded_ \" إلى _ escaped_ حرف " ، والذي تراه العملية المستهدفة على أنه A "B ؛ أي ، أدى عدم وجود هروب _ تلقائي_ إلى الخسارة الفعلية لـ \ المضمن. ما يستخدمه PowerShell _should_ في سطر أوامر ما وراء الكواليس في هذه الحالة هو "A \\\"B" - أي أن كلا من \ و " بحاجة إلى الهروب.

لم يتم إصلاحه ، لأن أسماء المضيف الحرفية التي يجب أن يراها ping هي A \"B و A\" B - _ مع_ \ أحرف.

تشير كلمة "إنها" هنا إلى الشكوى المذكورة ، والتي لحسن الحظ لا يمكنني إعادة إنتاجها.

@ yecril71pl ، أرى: الورق بشكل غير متسق الذي يحتوي على مسافات بين علامتي اقتباس" يشير إلى _ الافتقار إلى الهروب التلقائي للحروف المضمنة " و \ حرف_ ، مثل أوضح في تعليقي السابق - وهذا هو جوهر هذه القضية.

كانت هناك إصلاحات طفيفة في PowerShell Core لا يمتلكها Windows PowerShell ؛ لا يسعني سوى التفكير في واحدة الآن:

  • يستخدم Windows PowerShell اقتباسًا مزدوجًا أعمى في حالة \ : 'A B\' يتحول إلى (مكسور) "A B\" - يعالج PS Core ذلك بشكل صحيح ( "A B\\" ) .

بالنظر إلى أن هذا الريبو مخصص لـ PS Core فقط ، يكفي التركيز على ما لا يزال مكسورًا في PS Core (قد يكون من المفيد ذكر الاختلافات _ كمنحي_لوح_ ، ولكن من الأفضل توضيح هذا الجانب)


وحتى السيناريو الذي كنت تفكر فيه _ هو _ لا يزال معطلاً في PS Core - ولكن فقط _ إذا حذفت \ من الوسيطة_:

لا يزال تمرير 'A" B' يؤدي إلى _non_- نقل مزدوج A" B خلف الكواليس (بينما 'A\" B' ينتج "A\" B" - وهو كسر أيضًا ، كما تمت مناقشته - فقط بشكل مختلف).

بالنظر إلى أن هذا الريبو مخصص لـ PS Core فقط ، يكفي التركيز على ما لا يزال مكسورًا في PS Core (قد يكون من المفيد ذكر الاختلافات _ كمنحي_لوح_ ، ولكن من الأفضل توضيح هذا الجانب)

ميتا جانبا: أجد أنه من المفيد معرفة أن السلوك الخاطئ المذكور في تعليق مستخدم آخر لا ينطبق. بالطبع ، كان بإمكاننا رفض التعليق لمجرد أن المستخدم لم يكلف نفسه عناء التحقق من الإصدار الحالي. حسنا. المراسلين الجامحين كونهم جامحين ، فمن الأفضل أن نكون متأكدين. برأيي المتواضع.

لا توجد حجة هناك ، ولكن تقديم _ تأطير مناسب وسياق _ لمثل هذه _ الجوانب _ أمر مهم ، خاصة في سلسلة طويلة حيث تم نشر التعليق الأصلي - المطلوب للسياق - منذ فترة طويلة وهو ، في الواقع ، الآن _ مخفي_ بشكل افتراضي (لا يضر أبدًا في الواقع _link_ بالتعليق الأصلي الذي تم اقتباسه).

أتساءل ما إذا كانت هذه المناقشات تحدث فرقا. من الواضح أن قرارات اللجنة مستقلة عما يريده المجتمع. انظر إلى العلامات: Resolution- won't fix. ولكن نظرًا لأن PowerShell مفتوح المصدر (MIT) ، يمكن للمجتمع إصلاح هذا في مفترق منفصل ، واستدعاء PowerShellCommunity ( pwshc ) باختصار.

هذا يزيل الحاجة إلى التوافق مع الإصدارات السابقة. لاحقًا في PowerShell 8 ، قد تقوم اللجنة بدمج الشوكة.

حول التوافق مع الإصدارات السابقة: لا يأتي PowerShell مثبتًا مسبقًا على أي نظام تشغيل ، وبالنسبة لي ، فإن مشقة تثبيت PowerShellCommunity هي نفسها PowerShell . أفضل تثبيت إصدار المجتمع واستخدامه على الفور بدلاً من انتظار إصدار 8 في المستقبل (أو جعل الكود أكثر تعقيدًا مع مشغل جديد).

منذ أن قررت اللجنة إبقاء الأمور معطلة ، فمن الأفضل معرفة مدى سوء تعطلها. أعتقد أن المجتمع يمكن أن يعيش مع Invoke-Native الذي يفعل الشيء الصحيح. ليست مهمة المجتمع الحفاظ على ماء الوجه ضد إرادتهم.

من الأفضل معرفة مدى سوء حالة كسرهم

أتفق تمامًا - حتى لو لم يكن بالإمكان حل المشكلة في الوقت الحالي ، فإن معرفة كيفية القيام بالأشياء بشكل صحيح - من حيث المبدأ ، إذا ومتى يحين الوقت - أمر مهم - حتى لو لم يكن ذلك الوقت - أبدًا - في سياق لغة معينة.

يمكن للمجتمع أن يعيش مع Invoke-Native الذي يفعل الشيء الصحيح

للتوضيح:

  • هناك شيء مثل Invoke-NativeShell يعالج _حالة استخدام مختلفة _ - والتي هي موضوع # 13068 - ولحالة الاستخدام هذه ، فإن الأمر cmdlet ليس _فجوة مؤقتة: إنه الحل المناسب - راجع https://github.com/ PowerShell / PowerShell / Issues / 13068 # issuecomment -656781439

  • _تتعلق هذه المشكلة بإصلاح _PowerShell نفسها_ (لا يتعلق الأمر بوظيفة النظام الأساسي _ الأصلية) ، والحل _stopgap_ لذلك هو تقديم حفل منخفض _opt-in_ - ومن ثم اقتراح شحن الوظيفة iep كمبني في الوظيفة ، في انتظار إصلاح _proper_ في إصدار مستقبلي يُسمح له إلى حد كبير بقطع التوافق مع الإصدارات السابقة.

ليست مهمة المجتمع الحفاظ على ماء الوجه

لا أعتقد أن اهتمامaminya يتعلق بالحفاظ على ماء الوجه - إنه يتعلق بإصلاح السلوك للـ shell.

ومع ذلك ، لست متأكدًا من أن تجزئة نظام PowerShell البيئي باستخدام مفترق هو الطريقة الصحيحة للذهاب.

ليس لدي وقت للرد على كل هذا في هذه اللحظة فقط ، لكنني أعتقد أن هذا معقول ، لذلك سأعيد فتح:

هل يمكنك على الأقل ترك هذه المشكلة مفتوحة حتى يتواجد هذا العامل بالفعل ويمكن لأي شخص أن يوضح ، كيف يمكن لهذا العامل أن يحسن أي شيء ، تم ذكره في هذا الموضوع؟

كما قلت في قضية ذات صلة ، يضيف مشغل الاتصال الأصلي تعقيدًا في UX وهو اتجاه خاطئ.
في الوقت نفسه ، تدور المناقشة الكاملة هنا حول تبسيط التفاعل مع التطبيقات المحلية.

الشيء الوحيد الذي يوقفنا هو أنه تغيير جذري. لقد قلنا مرات عديدة أن هذا دمار كبير للغاية ، ولكن دعونا نزنه.

لنلق نظرة على جلسة تفاعلية. سيقوم المستخدم بتثبيت إصدار PowerShell جديد واكتشاف سلوك جديد عند استدعاء التطبيقات الأصلية. ماذا سيقول؟ سوف أتوقع أنه سيقول - "شكرًا - أخيرًا يمكنني الكتابة فقط وهو يعمل!".

لنلق نظرة على سيناريو تنفيذ / استضافة البرنامج النصي. يمكن لأي إصدار جديد من التطبيق (حتى مع إجراء تغيير طفيف واحد!) كسر عملية تجارية. نتوقع هذا دائمًا ونتحقق من عملياتنا بعد أي تحديثات. إذا وجدنا مشكلة ، فلدينا عدة طرق:

  • التراجع عن التحديث
  • تحضير الإصلاح السريع للنص
  • قم بإيقاف تشغيل الميزة التي تكسر البرنامج النصي الخاص بنا حتى يتم إصلاح هذه الأشياء أو تموت هي نفسها بمرور الوقت.

_ نظرًا لأن تحديثات الإصدار تكسر دائمًا شيئًا ما ، فإن الخيار الأخير هو أفضل ما يمكن أن يكون لدينا ونقبله.

(أريد أن أشير إلى أن PowerShell Core في الوقت الحالي ليس مكونًا من مكونات Windows ، وبالتالي لا يمكن أن يفسد تطبيقات الاستضافة مباشرة ، فقط البرامج النصية تتأثر مباشرة.)

أريد أن أذكرك بما قاله جايسون أعلاه - هذا إصلاح للأخطاء.
دعونا نصلحها ونبسط كل شيء للجميع. السماح للمستخدمين بالعمل powershell-y .

كما قلت في قضية ذات صلة ، يضيف عامل الاتصال المحلي التواطؤ في UX وهو اتجاه خاطئ.

تعقيد ❓

لنلق نظرة على جلسة تفاعلية. سيقوم المستخدم بتثبيت إصدار PowerShell جديد واكتشاف سلوك جديد عند استدعاء التطبيقات الأصلية. ماذا سيقول؟ سوف أتوقع أنه سيقول - "شكرًا - أخيرًا يمكنني الكتابة فقط وهو يعمل!".

لدي سيناريو مختلف: يقوم المستخدمون الجدد بتجربة PowerShell ، وإدراك أنه يفشل في ظروف غامضة ، ونقله إلى سلة المحذوفات وعدم العودة أبدًا. هذا ما كنت سأفعله.

تحضير الإصلاح السريع للنص

هذا شيء يجب علينا القيام به لتغطية الحالات الواضحة في البرامج النصية للمستخدم.

ندرك أنه فشل في ظروف غامضة

النقاش كله هنا هو مجرد التخلص من هذا "فشل غامض". ومن الأفضل القيام بذلك عن طريق التبسيط وليس إضافة أشياء غامضة جديدة.

النقاش كله هنا يدور حول التخلص من هذا. ومن الأفضل القيام بذلك عن طريق التبسيط وليس إضافة أشياء غامضة جديدة.

لا نريد التخلص من القدرة على استدعاء البرامج القابلة للتنفيذ مباشرة.

الاستدعاء القديم ليس مباشرًا إلى cmdline سواء بتخمينه لما إذا كان هناك شيء مقتبس بالفعل. بالإضافة إلى ذلك ، سيتم الاحتفاظ بالقدرة المذكورة مع -٪.

بالإضافة إلى ذلك ، سيتم الاحتفاظ بالقدرة المذكورة مع -٪.

يتطلب --% سطر أوامر محدد مسبقًا ، لذا فإن فائدته محدودة.

@ yecril71pl

لا نريد التخلص من القدرة على استدعاء البرامج القابلة للتنفيذ مباشرة.

أعتقد أن iSazonov يعني التخلص من سلوك عربات التي تجرها الدواب ، أي إصلاح الخطأ بشكل صحيح (بدون التمكين عبر بناء جملة إضافي) ، على الرغم من أنه يعني كسر الحلول الحالية. صحيح ،iSazonov؟

@ Artoria2e5 :

الاستدعاء القديم ليس مباشرًا إلى cmdline سواء بتخمينه لما إذا كان هناك شيء مقتبس بالفعل

[_تحديث_: لقد أخطأت في قراءة السطر المقتبس ، لكن آمل أن المعلومات لا تزال موضع اهتمام.]

  • لست مضطرًا إلى التخمين ، والقاعدة بسيطة:

    • _فقط if_ اسم الأمر أو المسار هو _ quoted_ - '/foo bar/someutil '- و / أو يحتوي على مراجع متغيرة (أو تعبيرات) - $HOME/someutil - & _ مطلوب_.
  • على الرغم من أنك قد تعتبر هذه الحاجة أمرًا مؤسفًا ، إلا أنها في صميم لغة PowerShell ، وهي ضرورية من خلال الحاجة إلى القدرة على التمييز النحوي بين وضعي التحليل الأساسيين ، ووضع الوسيطة ووضع التعبير.

    • لاحظ أن المشكلة ليست خاصة باستدعاء _external Programs_ ؛ تتطلب أوامر PowerShell الأصلية أيضًا & للاستدعاء إذا تم تحديدها عبر سلسلة مقتبسة / مرجع متغير.
  • إذا كنت لا تريد حفظ هذه القاعدة البسيطة ، فالقاعدة الأبسط هي: _ دائمًا_ استخدم & ، وستكون بخير - إنها أقل ملاءمة إلى حد ما من _لا_تحتاج إلى أي شيء ، ولكن ليس بالضبط مشقة (وأقصر) من --% ، وهو حل خاطئ تمامًا - انظر أدناه)

القدرة المذكورة ستظل محتفظ بها مع -٪.

[_تحديث_: لقد أخطأت في قراءة السطر المقتبس ، ولكن آمل أن المعلومات لا تزال موضع اهتمام.]

لا ، على الرغم من الخلط المؤسف لـ # 13068 مع _ هذه المشكلة ، --% - القدرة على استدعاء shell الأصلي_ ، باستخدام _its_ ، بنية النظام الأساسي المحددة دائمًا - ليست بأي حال من الأحوال حلاً للمشكلة المطروحة والمناقشة على هذا النحو يضيف فقط إلى الارتباك .

--% حالة استخدام مختلفة تمامًا ، كما هو مقترح حاليًا ، لها قيود شديدة ؛ إذا كان هناك شيء _لا _ يستحق تقديم _ مشغل _ لـ (أو تغيير سلوك واحد موجود) ، فهو القدرة على تمرير سطر أوامر حرفي إلى الصدفة الأصلية (والتي ، بالطبع ، يمكنك فعلها بنفسك ، باستخدام sh -c '...' على Unix ، و cmd /c '...' ، _ ولكن بقوة فقط إذا تم إصلاح هذه المشكلة _ ؛ تنفيذ ثنائي Invoke-NativeShell / ins cmdlet ، مع استخلاص تفاصيل الغلاف الهدف بشكل أساسي بناء جملة CLI ، سيتجنب المشكلة المطروحة (عن طريق الاستخدام المباشر لـ System.Diagnostics.ProcessStartInfo.ArgumentList ) ، وبالتالي يمكن تنفيذه بشكل مستقل).

@ yecril71pl

لدي سيناريو مختلف: يقوم المستخدمون الجدد بتجربة PowerShell ، وإدراك أنه يفشل في ظروف غامضة ، ونقله إلى سلة المحذوفات وعدم العودة أبدًا. هذا ما كنت سأفعله.

هل يمكنك توضيح: هل تخشى أن يؤدي السلوك الحالي لـ بوويرشيل إلى تجربة مستخدم غير سارة ، أم أنك تخشى أن يؤدي التغيير المقترح إلى تجربة المستخدم هذه.

لأنه في رأيي ، من المرجح أن يولد السلوك الحالي لـ بوويرشيل مثل هذه التجربة. كما ذكر أعلاه: يجب اعتبار السلوك الحالي لـ بوويرشيل خطأ.

لماذا قد يقوم مستخدم جديد ، بدون أي معرفة ببرنامج PowerShell ، بإصدار أوامر باستخدام وسيطات ملتوية؟

@ yecril71pl فقط للتأكد تمامًا: يجب أن تفكر في النموذج المطلوب حاليًا للوسيطات "ملتوية" ، وليس الإصلاح المقترح.

أنا أعتبر أن سؤالك ينبع من افتراضك بأنني شخص مهووس لا يمكن علاجه. هذا صحيح - لكنني احتفظت بالقدرة على تخيل ما قد يعتبره فصل عشوائي عادي ملتويًا.

ههههههههه
أعتقد أن @ Artoria2e5 كان يتحدث عن تحويل مجموعة الوسائط إلى سلسلة واحدة lpCommandLine عند قول

الاستدعاء القديم ليس مباشرًا إلى cmdline سواء بتخمينه لما إذا كان هناك شيء مقتبس بالفعل

لأنه عند الاتصال

echoargs.exe 'some"complicated_argument'

عليك بالفعل أن تخمن أكثر أو أقل ، ما إذا كان بوويرشيل يضيف اقتباسات حول some"complicated_argument .

مثال 1: في معظم إصدارات بوويرشيل
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)
echoarg.exe 'a\" b' إلى "C:\path\to\echoarg.exe" a\" b و
echoarg.exe '"a\" b"' إلى "C:\path\to\echoarg.exe" ""a\" b"" .

مثال 2: ترجمة إصدارات بوويرشيل الأقدم
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 ، شكرًا

أما بالنسبة لمشكلة حقيقية: نحن 100٪ في الاتفاق - في الواقع، يجب أن لا يكون حتى للتفكير في ما 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
باريورد
القيمة
/ CSTART = عنوان WINDOW
نوع ثابت
النظام
مدى
/ 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 True Way ™ لترميز الحجج ، دون الالتفات إلى ترتيب الاقتباس الذي استخدمه المستخدم بالفعل ، ليس مفيدًا ، إذا قلته بشكل معتدل.

@ yecril71pl أنا حقا في حيرة من تعليقاتكم. ما الذي تقترحه بالضبط هنا؟ تتم تغطية جميع حالات الاستخدام الخاصة بك --% . لقد رفضته في وقت سابق بالقول

يتطلب --% سطر أوامر محددًا مسبقًا ، لذا فإن فائدته محدودة.

لكن في الحقيقة يمكنك استخدام متغيرات البيئة بـ --% . جرب هذا:

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

إذن ما الذي أفتقده؟

ينقصنا بناء الجملة CMD /CSTART="$mytitle" ، بدون تسريب الأشياء إلى ENV: .

كفكرة رهيبة ، لدينا خيار استبدال Environment.ExpandEnvironmentVariables بشيء آخر. لا يوجد تطبيق أصلي على Unix على أي حال ، ولا أعتقد أن الأشياء التي تعالجها ستصبح ذات أهمية بالغة للأداء عند إعادة كتابتها في C #.

نظرًا لأن علامات التساوي غير مسموح بها في أسماء env var على أي حال ، فيمكن أن يكون لدينا %=$a% يعني $a . لن يؤدي هذا إلى كسر أي شيء موجود مع السماح ببعض الامتدادات المرنة للغاية (وربما السيئة) مثل جعلها تعمل مثل سلاسل قالب JS. بحق الجحيم ، يمكننا تحديد %VARNAME=$var% كنوع من بناء الجملة الاحتياطية أيضًا.

أما بالنسبة للجحيم التوثيقي فإن هذا قد يسبب ... أعتذر.

  • نحن لا نواجه مشكلة تحليل.

  • ما لدينا هو مشكلة في _كيف يمرر PowerShell الوسيطات الحرفية والمسلسلة التي نتجت عن _its_ التحليل إلى الملفات التنفيذية الخارجية (الأصلية )_:

    • على _Windows_، والمشكلة هي أن سطر الأوامر لاستدعاء الملف القابل للتنفيذ الخارجي مع التي يتم بناؤها وراء الكواليس لا _not_ الانضمام إلى اتفاقية الأكثر استخداما على نطاق واسع نقلا عن الحجج، كما هو مفصل في مايكروسوفت C / C ++ مترجم الوثائق و توزيع C ++ األوامر قسم

    • ما يحدث حاليًا ليس حتى أنه يتم استخدام اصطلاح _different_: من المفترض أنه بسبب سهو ، فإن سطور الأوامر التي تم إنشاؤها تكون متقطعة بشكل ظاهري - بناءً على تفاصيل الوسيطات ، المتعلقة بمجموعة من علامات الاقتباس المزدوجة والمسافات بالإضافة إلى وسيطات السلسلة الفارغة.

    • في النهاية ، تكمن المشكلة في البنية الأساسية لإنشاء العملية على Windows: فأنت مجبر على ترميز الوسائط لتمريرها إلى عملية _ كسطر أوامر _ - سلسلة واحدة تمثل _ جميع _ الوسيطات - بدلاً من تمريرها كمجموعة _ من الوسائط (والتي هي الطريقة التي تقوم بها المنصات الشبيهة بـ Unix). تتطلب الحاجة إلى تمرير سطر أوامر تنفيذ _قواعد الاقتباس والهروب_ ، وفي النهاية _ يتعلق الأمر بكل برنامج _ كيفية تفسير سطر الأوامر المعطى. في الواقع ، هذا يرقى إلى إجبار البرامج بلا داع على أن تكون غلافًا صغيرًا من نوع ما: إنهم مجبرون على إعادة أداء المهمة التي نفذتها الصدفة بالفعل ، وهي مهمة يجب أن تكون من اختصاص _ shell only_ (كما هو الحالة على يونكس) ، أي تحليل سطر الأوامر إلى وسيطات فردية. باختصار ، هذه هي الفوضى التي تمر عبر Windows .

    • في الممارسة العملية ، يتم تخفيف الفوضى من خلال _ most__ البرامج الملتزمة بالاتفاقية المذكورة أعلاه ، ومن المرجح جدًا أن تلتزم البرامج الجديدة التي يتم تطويرها بهذه الاتفاقية ، وذلك أساسًا لأن أوقات التشغيل المستخدمة على نطاق واسع والتي تدعم تطبيقات وحدة التحكم تنفذ هذه الاتفاقيات (مثل Microsoft C / C ++ / أوقات تشغيل .NET). لذلك فإن الحل المعقول هو:

      • اجعل PowerShell يلتزم بهذه الاتفاقية عند إنشاء سطر الأوامر خلف الكواليس.
      • بالنسبة للبرامج "المارقة" التي _ لا _ تلتزم بهذا الاصطلاح - والتي تتضمن بشكل ملحوظ cmd.exe والملفات الدفعية وأدوات Microsoft المساعدة مثل msiexec.exe و msdeploy.exe - توفر آلية بشكل صريح السيطرة على سطر الأوامر الذي تم تمريره إلى الهدف القابل للتنفيذ ؛ هذا ما --% ، و رمز-تحليل وقف يوفر - وإن كان ذلك مؤلم جدا .
    • في _Unix_ ، تكمن المشكلة في أنه يتم إنشاء سطر أوامر على الإطلاق _ - بدلاً من ذلك ، يجب تمرير مصفوفة الوسائط الحرفية _as-is_ ، والتي يدعمها .NET Core الآن (منذ ProcessStartInfo.ArgumentList ؛ كان يجب أن يدعم هذا _ دائمًا _ نظرًا لأنه - بشكل منطقي - _ لا توجد سطور أوامر_ ، فقط مصفوفات وسيطة ، عندما يتم إنشاء عملية على منصات تشبه نظام يونكس)

    • بمجرد استخدامنا ProcessStartInfo.ArgumentList ، تختفي جميع مشاكل Unix.

إصلاح هذه المشكلات هو ما يدور حولهTSlivede https://github.com/PowerShell/PowerShell-RFC/pull/90 .

في https://github.com/PowerShell/PowerShell-RFC/pull/90#issuecomment -650242411 ، اقترحت بالإضافة إلى ذلك تلقائيًا التعويض التلقائي عن "الخداع" لملفات الدُفعات ، نظرًا لاستخدامها على نطاق واسع جدًا كنقاط دخول CLI للارتفاع -برنامج الملف الشخصي مثل Azure (CLI az يتم تنفيذه كملف دفعي ، az.cmd ).
وبالمثل ، يجب أن نفكر في فعل الشيء نفسه مقابل msiexec.exe و msdeploy.exe وربما أخرى Microsoft CLIs "المارقة" رفيعة المستوى.


لقد قمت للتو بنشر وحدة ، Native ، ( Install-Module Native -Scope CurrentUser ) تتناول كل ما سبق عبر وظيفتها ie (اختصار لـ i nvoke (خارجي) e قابل للتنفيذ ؛ هو تنفيذ أكثر اكتمالا للوظيفة iep المعروضة أعلاه ).

يتضمن أيضًا ins ( Invoke-NativeShell ) ، الذي يتناول # 13068 ، و dbea ( Debug-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" ، والذي يعمل بالفعل):

إنها في جوهرها نفس المشكلة كما هو الحال مع الوسائط prop="<value with spaces>" لـ msiexec / msdeploy : PowerShell - بشكل مبرر - يحول /CSTART="WINDOW TITLE" إلى "/CSTART=WINDOW TITLE" ، والذي ، مع ذلك ، يخرق استدعاء cmd.exe .

هناك طريقتان لحل هذا:

  • تفويض إلى 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 يقسم سطور الأوامر إلى وسيطات ، الشيء الوحيد المطلوب هو معرفة الملف القابل للتنفيذ الذي يجب الاتصال به والباقي هو مجرد سطر أوامر كما كتبه المستخدم (بعد استبدالات متغير البيئة ، والتي تتم دون أي اعتبار لحدود الحجة). بالطبع ، أوامر shell الداخلية استثناء هنا.

إنها في جوهرها نفس المشكلة كما هو الحال مع وسيطات prop="<value with spaces>" لـ msiexec / msdeploy

أنا لست مستخدمًا واثقًا من أي منهما ، لذا فضلت طرح شيء أعرفه أكثر.

لنكون واضحين: ما يلي ليس له تأثير على النقاط الواردة في تعليقي السابق.

لا أعتقد أن CMD.EXE يقسم أسطر الأوامر إلى وسيطات

  • يمكن أن يفلت من دون تقسيم _ صريح_ عند استدعاء _ملفات تنفيذية خارجية_ (أوامر يتم تشغيلها بواسطة ملف تنفيذي آخر في عملية فرعية) ، ولكن يجب أن تفعل ذلك لملفات الدفعات.

  • حتى عند استدعاء الملفات التنفيذية الخارجية ، يجب أن تكون مدركًا لحدود الوسيطة ، وذلك لتحديد ما إذا كان الحرف الأولي المحدد (على سبيل المثال & ) يحتوي على وظيفة _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_ " chars. في "..." يتم التعرف على السلاسل إذا هربت كـ "" :

:: 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 فقط) "" وليس أيضًا \" الأكثر استخدامًا (وهو ما يشبه POSIX _shells_ على Unix _exclusively_ ملاحظة: _shells_ ، وليس _programs_ ، لأن البرامج ترى فقط مجموعة الوسائط الحرفية التي تنتج من تحليل shell).

بينما تدعم معظم CLIs على Windows _both_ "" و \" ، البعض _ فقط_ يفهم \" (لا سيما 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 مباشرة ، إن أمكن.

    • اتصل بالملفات التنفيذية الخارجية مباشرةً (بمجرد إصلاح هذه المشكلة) أو عبر ie (حاليًا) ، باستخدام بنية _PowerShell's_.
  • إذا كان عليك الاتصال بـ cmd.exe ، فاستخدم ins / Invoke-NativeShell للبساطة العامة ، وعلى وجه التحديد ، لمدى سهولة تضمين متغير PowerShell وقيم التعبير في سطر الأوامر .

    • من الأسباب المشروعة للاستمرار في الاتصال بـ cmd.exe مباشرة هو التعويض عن عدم دعم PowerShell لبيانات البايت الخام في خط الأنابيب - راجع إجابة SO هذه للحصول على مثال.

أعلم أنني سألتقط الكثير من الانتقادات هنا ، وأنا أقدر حقًا عمق المناقشة التي تحدث ، لكن ... البط ... هل لدى أي شخص مثال على أي من هذا الأمر مهم بالفعل في سيناريو العالم الحقيقي ؟

من وجهة نظري أننا غير مفوضين في PowerShell لحل "الفوضى" الموجودة حاليًا مع تحليل حجة Windows. وللكثير من الأسباب نفسها التي تجعلنا لا نستطيع حل المشكلة ، هناك سبب وجيه أن 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 الصعب التكيف مع cmd.exe من PowerShell بطريقة تسبب مشاكل في الواجهة_. حاول PowerShell معالجة هذه المشكلة عن طريق تكرار معظم الوظائف في cmd.exe ، وذلك لجعل cmd.exe زائداً عن الحاجة. هذا ممكن أيضًا للأدوات الأخرى ، على سبيل المثال يمكن تشغيل MSI عبر ActiveX ، على الرغم من أن القيام بذلك يتطلب معرفة كبيرة. فهل هناك أي شيء أساسي لم تتم تغطيته؟

ناقشت لجنة @ 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 ، وهذا ليس شيئًا على الإطلاق نحن على يقين من أننا سنقوم به في النهاية.

أحتاج أيضًا إلى البحث في المزيد لفهم جانب السماح بقائمة / "تطبيق rouge" في RFC ، @ mklement0 ، لأنني لست متأكدًا من مدى رغبتنا في الاشتراك للحفاظ على قائمة من هذا القبيل.

joeyaiello و @ SteveL-MSFT ، اسمحوا لي أن أقدم ملاحظة تعريفية أولاً:

في حين أنه من الجيد أن نرى أنcspotcode الصورة سبيل المثال منحكم _glimpse_ المشكلة، ردودكم لا يزال يخون نقص أساسي في فهم وتقدير (حجم) المشكلة الكامنة (وسوف يقولون هذه النقطة في تعليق لاحق) .

هذا ليس حكمًا "شخصيًا": إنني أدرك تمامًا مدى صعوبة أن تكون مرهقًا جدًا وأن تضطر إلى اتخاذ قرارات بشأن مجموعة واسعة جدًا من الموضوعات في فترة زمنية قصيرة.

ومع ذلك ، يشير هذا إلى مشكلة _ تركيبية: بالنسبة لي يبدو أن القرارات يتم اتخاذها بشكل روتيني من قبل @ PowerShell / بوويرشيل-لجنة على أساس فهم سطحي للمشاكل التي تتم مناقشتها ، على حساب المجتمع ككل.

بالنسبة لي ، فإن استجابة اللجنة للقضية التي تتم مناقشتها هنا هي المثال الأكثر أهمية لهذه المشكلة الهيكلية حتى الآن.

لذلك أطلب منكم النظر في هذا:

ماذا عن تعيين اللجان الفرعية الخاصة بالموضوع والتي تتشاور معها اللجنة والتي تمتلك الفهم المطلوب للقضايا المطروحة؟

يمكنك مشاركة محتوى testexe SteveL-MSFT ، فقط تريد التأكد!

لخص TSlivede المشكلة بشكل مناسب في https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -665125375:

من ناحية أخرى ، تدعي PowerShell أنها قذيفة (حتى يتم حل 1995 # ، لن أقول إنها _ is_ a shell)

كما ذكرنا عدة مرات من قبل ، فإن التفويض الأساسي للقذيفة هو استدعاء الملفات التنفيذية الخارجية بالحجج.

فشل PowerShell حاليًا في تنفيذ هذا التفويض ، نظرًا لأن الحجج ذات علامات الاقتباس المزدوجة المضمنة ووسيطات السلسلة الفارغة لا يتم تمريرها بشكل صحيح.

كما ذكرنا من قبل ، قد يكون هذا أقل مشكلة في أيام Windows فقط ، حيث نادرًا ما ظهر نقص CLIs الخارجية القادرة على ظهور هذه المشكلة ، ولكن هذه الأيام ولت ، وإذا أراد PowerShell أن يثبت نفسه كمنصة مشتركة موثوقة شل ، يجب أن تعالج هذه المشكلة.

مثالcspotcode هو git مثال جيد ؛ أي ملف تنفيذي تريد تمرير سلسلة 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 خلف الكواليس.

    • بالنسبة لحالات الحافة (CLIs "المارقة") ، هناك (تم التنفيذ بشكل سيئ ) --%
    • بصفتنا _courtesy_ ، يمكننا تعويض بعض حالات الحواف المعروفة لتقليل الحاجة إلى --% - انظر أدناه.

لذلك ، _ في أقرب وقت ممكن_ ، يجب اتخاذ أحد الخيارات التالية:

  • ندرك أهمية جعل تمرير الحجة يعمل بشكل صحيح وإصلاحه على حساب التوافق مع الإصدارات السابقة.

  • إذا كان التوافق مع الإصدارات السابقة أمرًا بالغ الأهمية حقًا ، فقم بتوفير عامل تشغيل جديد أو وظيفة مثل الوظيفة ie من الوحدة النمطية Native التي تعمل على حل المشكلة ، ونشرها على نطاق واسع باعتبارها الطريقة الوحيدة الموثوقة لاستدعاء خارجي الملفات التنفيذية.

إن اقتراح ميزة _تجريبية_ لمعالجة سمة أساسية معطلة بشكل سيئ غير ملائم على الإطلاق.


@ SteveL-MSFT

حتى التفكير في استخدام --% كحل لهذه المشكلة يعد أمرًا مضللًا بشكل أساسي:

إنها ميزة _Windows-only_ لا تعرف سوى "..." الاقتباس و %...% -style - متغير البيئة.

في نظام التشغيل Unix ، لا ينطبق مفهوم "إيقاف التحليل" بشكل أساسي: _ لا يوجد سطر أوامر_ لتمريره إلى العمليات الفرعية ، فقط مصفوفات من الوسائط.

وبالتالي ، يتعين على الشخص _someone_ تحليل سطر الأوامر إلى وسيطات _before_ ، والتي يتم تفويضها ضمنيًا إلى فئة ProcessStartInfo ، عبر خاصية .Arguments الخاصة بها ، والتي تستخدم في نظام التشغيل Unix اصطلاحات _Windows_ لتحليل سطر الأوامر - وبالتالي يتعرف على اقتباس "..." (مع هروب " المضمّن كـ "" أو \" ) فقط.

--% هي ميزة خاصة بنظام التشغيل Windows فقط والغرض المشروع الوحيد هو استدعاء CLIs "المارقة".


تضمين التغريدة

أن Windows و VC ++ compilers اختاروا عدم قطع هذا السلوك.

المترجم VC ++ _ يفرض اصطلاحًا معقولًا وملاحظًا على نطاق واسع_ ، لإحداث النظام في الفوضى.

إن الالتزام بهذه الاتفاقية بالتحديد هو ما تتم الدعوة إليه هنا ، والذي يمنحنا استخدام ProcessStartInfo.ArgumentList تلقائيًا.

_ هذا وحده سيغطي الغالبية العظمى من المكالمات. تغطية ALL المكالمات أمر مستحيل وفي الواقع ليست مسؤولية PowerShell.

كما هو مذكور ، بالنسبة إلى CLIs "المارقة" التي تتطلب أشكالًا غير تقليدية من الاقتباس ، يجب استخدام --% (أو ins / Invoke-NativeShell من الوحدة النمطية Native ).

_ على سبيل المجاملة_ ، يمكننا التعويض تلقائيًا عن السيناريوهات "المارقة" المعروفة ، أي استدعاء الملفات الدفعية وبعض برامج Microsoft CLI عالية المستوى:

  • حالة الملف الدفعي هي حالة عامة ، ويمكن شرحها وتصورها بسهولة (على سبيل المثال ، مرر a&b كـ "a&b" ، على الرغم من أنه لا يجب أن يتطلب عرض أسعار) - سوف يتجنب الحاجة إلى الاستخدام من --% مع جميع CLIs التي تستخدم الملفات المجمعة كنقطة دخول لها (وهو أمر شائع جدًا) ، مثل Azure az.cmd

  • يتمثل البديل لاستثناءات الترميز الثابت لواجهات سطر الأوامر (CLIs) المحددة - والتي يمكن أن تكون مربكة - في اكتشاف _ النمط التالي في الحجج التي تنتج عن تحليل PowerShell - <word>=<value with spaces> - وبدلاً من تمرير "<word>=<value with spaces>" ، كما يحدث حاليًا ، لتمرير <word>="<value with spaces>" ؛ يفي الأخير بـ CLIs "المارقة" ، بينما يتم قبوله أيضًا من قِبل CLIs الملتزمة بالاتفاقية ؛ على سبيل المثال ، يرى echoArgs "foo=bar baz" النهاية نفس الوسيطة الأولى مثل echoArgs --% foo="bar baz"

musm يمكنك العثور على الكود المصدري لـ TestExe على https://github.com/PowerShell/PowerShell/blob/master/test/tools/TestExe/TestExe.cs.

جيثب
بوويرشيل لكل نظام! ساهم في تطوير PowerShell / PowerShell عن طريق إنشاء حساب على GitHub.

أعتقد أن استيعاب الاستثناءات افتراضيًا سيؤدي فقط إلى موقف مشابه للوضع الحالي ، حيث يحتاج الأشخاص إلى التراجع عن "فائدة" 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>" ، كما يحدث حاليًا ، لتمرير <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" |

يعرض الصف الأخير مثالاً حيث لن يكون من الممكن الاحتفاظ بعلامات الاقتباس المزدوجة الأصلية.

أعلم أنني سألتقط الكثير من الانتقادات هنا ، وأنا أقدر حقًا عمق المناقشة التي تحدث ، لكن ..._ البط _... هل لدى أي شخص مثال على أي من هذا الأمر مهم بالفعل في سيناريو العالم الحقيقي ؟

لقد ذكرت بالفعل في هذا الموضوع قبل عام (مخفي الآن ...) أنني اعتدت الحصول على الكثير من لحظات WFT عند استخدام ripgrep في Powershell. لم أستطع فهم سبب عدم تمكني من البحث عن السلاسل المقتبسة. لقد تجاهلت اقتباساتي:

rg '"quoted"'

وفي git bash لم يحدث ذلك.

الآن أحصل على لحظات أقل من WTF لأنني وجدت للأسف مشكلة github الطويلة ووجدت أن تمرير " إلى Powershel مكسور تمامًا. مثال "git.exe" الأخير رائع أيضًا.

لأكون صريحًا ، الآن لا أجرؤ حتى على استخدام Powershell للاتصال بالأمر الأصلي عندما أعلم أنني قد أمرر " في سلسلة كمعامل. أعلم أنني قد أحصل على نتيجة خاطئة أو خطأ.

حقًا ، @ mklement0 لخص الأمر بشكل رائع (يجب نقش هذا في الحجر في مكان ما)

كما ذكرنا عدة مرات من قبل ، فإن التفويض الأساسي للقذيفة هو استدعاء الملفات التنفيذية الخارجية بالحجج .
فشل PowerShell حاليًا في تنفيذ هذا التفويض ، نظرًا لأن الحجج ذات علامات الاقتباس المزدوجة المضمنة ووسيطات السلسلة الفارغة لا يتم تمريرها بشكل صحيح.
كما ذكرنا من قبل ، قد يكون هذا أقل مشكلة في أيام Windows فقط ، حيث نادرًا ما ظهر نقص CLIs الخارجية القادرة على ظهور هذه المشكلة ، ولكن هذه الأيام ولت ، وإذا أراد PowerShell أن يثبت نفسه كمنصة مشتركة موثوقة شل ، يجب أن تعالج هذه المشكلة .

وحول كسر التغييرات.
كتب لي زميل في العمل مؤخرًا أن النص الخاص بي لم يعمل على جهازه. كنت أقوم بتشغيله فقط على Powershel Core وكان يديره على Windows Powershell. تم إخراج ملف Out-File -Encoding utf8 مشفر باستخدام "BOM" على Windows Powershell وبدون BOM على Powershel Core. هذا مثال غير محذوف إلى حد ما ، لكنه يوضح أن هناك بالفعل تغييرات خفية في Powershel وهذا أمر جيد لأننا نتخلص من المراوغات والسلوك البديهي من لغة مشهورة بها. سيكون من الرائع لو كان فريق Powershel أكثر تساهلاً عندما يتعلق الأمر بتغيير التغييرات الآن بعد أن أصبح لدينا منصة Powershell التي يتم شحنها خارج Windows وأننا نعلم أن Windows Powershell في وضع "الصيانة" وسيكون قابلاً للاستخدام إلى الأبد تريد حقًا تشغيل بعض البرامج النصية القديمة التي اندلعت في الإصدار الأحدث من Powershell.

إعادة: تلك النقطة الأخيرة من كسر التغييرات - أوافق تمامًا. هناك العديد من التغييرات العاجلة التي تحملناها لأسباب مختلفة. ومع ذلك ، في كثير من الأحيان يبدو أن بعض التغييرات الفاصلة يتم استهجانها ببساطة لأسباب التفضيل وعدم إعطاء الأهمية المناسبة لقيمتها الفعلية.

هناك بعض التغييرات من هذا القبيل والتي من شأنها - بشكل كبير - تحسين تجربة shell الشاملة لأي شخص يحتاج إلى الوصول إلى خارج PowerShell لإنجاز المهام ، وهو ما يحدث طوال الوقت. لقد تم الاتفاق مرارًا وتكرارًا على أن السلوك الحالي لا يمكن الدفاع عنه ، وهو بالفعل معطل إلى حد كبير لأي شيء عدا أبسط الاستخدامات. ومع ذلك ، ما زلنا نواجه هذا التحفظ على تغيير التغييرات حتى مع وجود عشرات من التغييرات العاجلة المقبولة بالفعل ، وبعضها له تأثير كبير مماثل.

لأولئك الذين يطلبون أمثلة - خذ دقيقة لزيارة Stack Overflow لمرة واحدة. أنا متأكد من أن @ mklement0 لديه مجموعة من الأمثلة التي تتطلب مساعدة المجتمع للمساعدة في شرح تغيير جذري في الإصدارات الأحدث. يحدث ذلك في كل وقت_. ليس لدينا

كلما كرر فريق MSFT نفس الشيء مرارًا وتكرارًا ، يمكننا التأكد من أنهم يعرفون أكثر مما يمكنهم قوله علنًا. يجب علينا احترام انضباطهم الداخلي وعدم الضغط عليهم. _ربما يمكننا إيجاد حل وسط. _ آمل أن يكون لدي الوقت اليوم لوصف مسار بديل مع الهجرة البطيئة.

إنني أدرك ذلك ، ولهذا السبب نادرًا ما أجد نقطة في التشكيك فيه.

ومع ذلك ، هذا مشروع مفتوح المصدر ؛ إذا لم يكن هناك رؤية محتملة لهذه القرارات ، فسيكون الناس محبطين حتمًا. لا يوجد لوم على أي من جانبي تلك العملة ، هذه فقط حقيقة الوضع هنا ، IMO. حسنًا ، قد يؤدي وجود مسار للهجرة إلى تخفيف هذا الألم إلى حد ما ، لكننا نحتاج إلى سياسات واضحة محددة حول كيفية عمل ذلك والتي ستجعل الأمور تعمل لصالح أكبر عدد ممكن من الأشخاص. من الصعب الوصول إلى حل وسط عند نقص المعلومات.

إنني أتطلع إلى رؤية ما لديك في جعبتك. 😉

@ mklement0 أنت محق تمامًا ، لدرجة أنني أستطيع فقط الرد على نقطة التعريف الخاصة بك الآن. لسوء الحظ ، في الحالات التي لا نتمكن فيها من الوصول إلى مستوى العمق المطلوب للإجابة على سؤال كهذا ، غالبًا ما يكون النهج الأكثر أمانًا هو إرجاء أو رفض التغيير العاجل حتى يكون لدينا المزيد من الوقت

أرغب في إجراء نقطة تعريف أخرى حول تغيير التغييرات ، على الرغم من ذلك: يشير القياس عن بُعد لدينا إلى أن معظم مستخدمي PowerShell 7 لا يديرون إصداراتهم الخاصة. إنهم يشغلون نصوصًا برمجية آلية في بيئة مُدارة مريحة ، على سبيل المثال ترقية مستخدميهم من 6.2 إلى 7.0 (انظر قفزة يومين إلى 6.2 مستخدمين ليصبحوا 7.0 مستخدمين بدءًا من 8/3 ؛ هذه ليست نقطة البيانات الوحيدة لدينا هنا ، ولكن إنه أمر مناسب الآن يوضح النقطة). بالنسبة لهؤلاء المستخدمين ، من غير المقبول إجراء تغيير فاصل يحول نصًا يعمل بكفاءة إلى نص لا يعمل.

أنا أيضًا مدين للمجتمع بمدونة حول كيف أفكر في تأثير التغييرات المفاجئة: أي مقايضة انتشار الاستخدام الحالي وشدة الاختراق مقابل سهولة تحديد الفاصل وتصحيحه. هذا هو السائد للغاية في البرامج النصية الموجودة ، ومربك لتحديد وإصلاح ، وسلوك كسر من النجاح التام إلى الفشل التام ، ومن ثم التحفظ الشديد لفعل أي شيء هنا.

أعتقد أنه من العدل أن نقول إننا لن نفعل أي شيء هنا في 7.1 ، لكنني بالتأكيد منفتح على جعل هذا أولوية استقصائية لـ 7.2 (أي أننا نقضي أكثر من مجرد وقت لجنتنا في مناقشة هذا.)

ماذا عن تعيين اللجان الفرعية الخاصة بالموضوع والتي تتشاور معها اللجنة ولديها الفهم المطلوب للقضايا المطروحة؟

نحن نعمل على هذا. أعلم أنني قلت ذلك من قبل ، لكننا قريبون جدًا (كما هو الحال في ، أنا أقوم بصياغة المدونة ومن المحتمل أن ترى بعض التصنيفات الجديدة تظهر قريبًا والتي سنلعب بها).

إنني أقدر صبر الجميع وأدرك أنه من المزعج الحصول على رد بليغ من اللجنة كل أسبوعين عندما يبذل الأشخاص قدرًا هائلاً من التفكير والتفكير في المناقشة. أعلم أن هذا يعني أننا لا نفكر بعمق في الأشياء ، لكنني أعتقد أننا لا نعبر عن عمق مناقشاتنا بقدر من التفاصيل كما يفعل الناس هنا. في الأعمال المتراكمة الخاصة بي ، لدي مجموعة كاملة من موضوعات المدونة مثل التغيير العاجل الذي يدور حول طريقة تفكيري في اتخاذ القرارات من داخل اللجنة ، لكنني لم تتح لي الفرصة للجلوس وضخها. لكن يمكنني أن أرى هنا أنه ربما سيجد الناس الكثير من القيمة في ذلك.

آمل ألا أكون بعيدة جدًا في هذه المناقشة. لا أريد أن تتحول هذه المشكلة تمامًا إلى قضية تعريفية حول إدارة المشروع ، لكنني أردت معالجة بعض الإحباط المفهوم الذي أراه هنا. أناشد أي شخص يريد التحدث عن هذا بمزيد من التفصيل معنا للانضمام إلى " مكالمة المجتمع" الأسبوع المقبل (أضف أسئلتك وأفكارك هنا وسأحرص على معالجتها في المكالمة).

مجرد ملاحظة سريعة حول نقطة التعريف: أقدر الاستجابة المدروسة ،joeyaiello.

بالنسبة لخطورة التغيير المفاجئ: يبدو أن العبارات التالية متضاربة:

هل لدى أي شخص مثال على أي من هذا مهم بالفعل في سيناريو العالم الحقيقي

ضد.

هذا واحد منتشر للغاية في البرامج النصية الموجودة ، ومربك تحديده وإصلاحه

إذا كان سائدًا بالفعل ، فإن الاحراج والغموض في الحلول الضرورية هما سبب إضافي لإصلاح هذا الأمر أخيرًا ، لا سيما بالنظر إلى أننا يجب أن نتوقع زيادة عدد الحالات.

أنا أدرك أن جميع الحلول الحالية سوف تنكسر.

إذا كان تجنب ذلك أمرًا بالغ الأهمية ، فإن هذا النهج المقترح مسبقًا هو السبيل للذهاب:

توفير عامل تشغيل جديد أو دالة _ مثل الوظيفة ie من الوحدة النمطية Native التي تعمل على حل المشكلة ، ونشرها على نطاق واسع باعتبارها الطريقة الوحيدة الموثوقة لاستدعاء الملفات التنفيذية الخارجية.

وظيفة _ مثل ie ستسمح للأشخاص باختيار السلوك الصحيح مع _ الحد الأدنى من الجلبة _ ، _ كحل مؤقت_ ، بدون إثقال _language_ بعنصر نحوي جديد (عامل تشغيل) ، يكون سبب وجوده الوحيد هو للتغلب على خطأ قديم يعتبر كسرًا يصعب إصلاحه:

  • ستوفر الفجوة المؤقتة وصولاً _ رسميًا _ إلى السلوك الصحيح (عدم الاعتماد على الميزات التجريبية_).
  • وطالما كانت الفجوة المؤقتة ضرورية ، فستحتاج إلى الإعلان عنها على نطاق واسع وتوثيقها بشكل صحيح.

إذا / عندما يتم إصلاح السلوك الافتراضي:

  • يمكن تعديل الوظيفة لمجرد الرجوع إليها ، حتى لا يتم كسر الكود الذي يستخدمها.
  • يمكن كتابة رمز جديد دون الحاجة إلى الوظيفة بعد الآن.

تسمح وظيفة مثل ie للأشخاص باختيار السلوك الصحيح بأقل قدر من الجلبة ، كحل مؤقت

يمكننا تبسيط الاعتماد عن طريق المتوسط ​​# 13428. يمكننا حقن هذا في تحقيقات @ mklement0 في المحرك بشفافية.

تضمين التغريدة

أعتقد أن استيعاب الاستثناءات افتراضيًا سيؤدي فقط إلى موقف مشابه للوضع الحالي ، حيث يحتاج الأشخاص إلى التراجع عن "فائدة" PowerShell. إذا كانت هناك استثناءات ، فيجب أن يكون واضحًا أنها مطبقة.

التسهيلات التي أقترحها تجعل الغالبية العظمى من المكالمات "تعمل فقط" - إنها تجريدات مفيدة من فوضى سطر أوامر Windows التي يجب أن نبذل قصارى جهدنا لحماية المستخدمين منها.

الافتراض المبرر هو أن هذا التدريع هو جهد لمرة واحدة للملفات التنفيذية _legacy_ ، وأن تلك التي تم إنشاؤها حديثًا ستلتزم باتفاقيات Microsoft C / C ++.

ومع ذلك ، من المستحيل القيام بذلك في جميع الحالات ؛ بالنسبة لأولئك الذين لا يمكن استيعابهم تلقائيًا ، هناك --% .

أنا شخصياً لا أريد أن أفكر في ما إذا كان يتم تنفيذ أداة معينة foo على أنها foo.bat أو foo.cmd ، أو ما إذا كانت تتطلب foo="bar none" الوسيطات "foo=bar none" ، والتي تكون مكافئة للملفات التنفيذية المتوافقة مع الاتفاقية.

وأنا بالتأكيد لا أريد نموذج بناء جملة منفصل لاستثناءات مختلفة ، مثل &[bat]
بدلاً من ذلك ، --% هي الأداة الشاملة (لنظام التشغيل Windows فقط) لصياغة سطر الأوامر كما تريد تمامًا - مهما كانت المتطلبات المحددة وغير التقليدية للبرنامج المستهدف.

على وجه التحديد ، أماكن الإقامة المقترحة هي كما يلي:

ملحوظة:

  • كما هو مذكور ، فهي مطلوبة _ على Windows_ فقط ؛ على Unix ، التفويض إلى ProcessStartInfo.ArgumentList كافٍ لحل جميع المشكلات.

  • على الأقل على مستوى عالٍ ، من السهل تصور هذه التسهيلات وتوثيقها.

  • لاحظ أنه سيتم تطبيقها _after_ تحليل PowerShell المعتاد ، في خطوة سطر أوامر الترجمة (Windows فقط) إلى العملية. وهذا يعني أن تحليل المعلمة الخاص بـ PowerShell لن يتم تضمينه - ويجب

  • سيتعين على المستخدمين أنفسهم التعامل مع أي حالات غريبة حقًا لا تغطيها هذه التسهيلات
    --% - أو ، مع تثبيت الوحدة النمطية Native ، مع ins / Invoke-NativeShell ، مما يسهل تضمين متغير PowerShell وقيم التعبير في المكالمة.

    • يمكن أن يساعد الأمر dbea ( Debug-ExecutableArguments ) من الوحدة النمطية Native تشخيص وفهم سطر أوامر العملية المستخدم في النهاية - راجع المثال أدناه.

قائمة أماكن الإقامة:

  • بالنسبة لملفات الدُفعات (كما هو مذكور ، تكمن أهمية استيعاب هذه الحالة تلقائيًا في انتشار CLIs عالية المستوى التي تستخدم الملفات الدفعية _ كنقطة دخولها_ ، مثل az.cmd لـ Azure).

    • علامات الاقتباس المزدوجة المضمنة ، إن وجدت ، يتم تخطيها كـ "" (بدلاً من \" ) في جميع الوسائط.
    • أي وسيطة لا تحتوي على cmd.exe أحرف أولية مثل & تكون محاطة بعلامات اقتباس مزدوجة (بينما يقوم PowerShell افتراضيًا فقط بتضمين الوسيطات بمسافات بين علامات الاقتباس المزدوجة) ؛ على سبيل المثال ، يتم وضع الوسيطة الحرفية التي يراها PowerShell كـ a&b كـ "a&b" في سطر الأوامر الذي تم تمريره إلى ملف دفعي.
  • بالنسبة إلى CLIs رفيعة المستوى مثل msiexec.exe / msdeploy.exe و cmdkey.exe (بدون استثناءات الترميز الثابت):

    • يؤدي أي استدعاء يحتوي على وسيطة واحدة على الأقل من النماذج التالية إلى تشغيل السلوك الموضح أدناه ؛ يمكن أن يتكون <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> _embedded_ " chars. ، يجب استخدام --% ؛ على سبيل المثال ،
        msiexec ... --% PROP="Nat ""King"" Cole"
      • يتم وضع الجزء <value with spaces> فقط بين علامتي اقتباس مزدوجتين ، وليس الوسيطة ككل (الأخير هو ما يفعله PowerShell - بشكل مبرر - افتراضيًا) ؛ على سبيل المثال ، يتم وضع الوسيطة الحرفية التي يراها PowerShell كـ foo=bar none كـ foo="bar none" في سطر أوامر العملية (بدلاً من "foo=bar none" ).
    • ملحوظة:

      • إذا حدث أن الملف القابل للتنفيذ الهدف _ لا _ يكون msiexec -style CLI ، فلن يحدث أي ضرر ، لأن CLIs الملتزمة بالاتفاقية تعتبر بشكل معقول <word>="<value with spaces>" و "<word>=<value with spaces>" _ Equivalent_ ، وكلاهما يمثلان حرفيا <word>=<value with spaces> .

      • وبالمثل ، تقبل الغالبية العظمى من الملفات التنفيذية "" بالتبادل مع \" للهروب من الأحرف المضمنة " chars. ، مع استثناء ملحوظ لـ PowerShell CLI و Ruby و Perl (_ not_ Performing) الإقامة جديرة بالاهتمام على الأقل إذا تم استدعاء PowerShell CLI ، لكنني أعتقد أن الترميز الثابت لـ Ruby و Perl سيكون منطقيًا أيضًا). https://github.com/PowerShell/PowerShell/pull/13482#issuecomment -677813167 يوضح أن جميع التطبيقات التي تستخدم وظيفة WinAPI CommandLineToArgvW لا تدعم "" الهروب.

يمكن أيضًا معالجة جميع الحالات الأخرى على Windows باستخدام ProcessStartInfo.ArgumentList ، والذي يطبق ضمنيًا اصطلاح Microsoft C / C ++ (والذي يعني بشكل خاص \" مقابل " -escaping).


تقوم الوظيفة ie من الإصدار الحالي ( 1.0.7 ) للوحدة Native بتنفيذ هذه التسهيلات (بالإضافة إلى إصلاح تحليل الوسيطة المعطلة) ، لإصدارات PowerShell 3 وما فوق ( Install-Module Native ).

أدعوك أنت والجميع هنا لتخطي خطواته لاختبار الادعاء بأنه "يعمل فقط" للغالبية العظمى من المكالمات الخارجية القابلة للتنفيذ

قيود لا يمكن تجنبها حاليًا:

  • ملاحظة: تأتي هذه القيود الفنية من تنفيذ ie كوظيفة _ (الإصلاح المناسب في المحرك نفسه لن يكون له هذه المشكلات):

    • بينما يتم تعيين $LASTEXITCODE بشكل صحيح على رمز الخروج للعملية ، ينتهي الأمر بـ $? دائمًا $true - لا يمكن حاليًا تعيين رمز المستخدم $? بشكل صريح ، على الرغم من إضافة هذه الإمكانية مضاء باللون الأخضر - راجع https://github.com/PowerShell/PowerShell/issues/10917#issuecomment -550550490. لسوء الحظ ، هذا يعني أنه لا يمكنك حاليًا استخدام 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_. نظرًا للتغييرات في _Windows PowerShell_ بمرور الوقت ، قد تكون هناك حالات حافة لا يتم التعامل معها بشكل صحيح ، على الرغم من أنني على دراية بحالتين فقط:

    • لا يمكن تطبيق تسهيل الاقتباس الجزئي لـ CLIs على النمط msiexec في الإصدارين 3 و 4 ، لأن هذه الإصدارات تلف الوسيطة بأكملها في مجموعة إضافية من علامات الاقتباس المزدوجة ؛ إنه يعمل في الإصدار 5.1 ، مع ذلك.

    • "" -escaping افتراضيًا للتغلب على المشكلات ، ولكن في الحالات التي يكون فيها \" ضروريًا (PowerShell CLI ، Perl ، Ruby) ، يكون الرمز المميز مثل 3" of snow تم تمريرها بالخطأ على أنها وسائط _3_ ، لأن جميع إصدارات Windows PowerShell تتجاهل تضمين مثل هذه الوسيطة بين علامتي اقتباس ؛ يبدو أن هذا يحدث للوسيطات ذات الأحرف غير المبدئية " التي لا يسبقها حرف مسافة.


أمثلة ، مع الإخراج من PowerShell Core 7.1.0-preview.5 على نظام التشغيل Windows 10:

ملاحظة: يتم استخدام الوظيفة dbea ( Debug-ExecutableArguments ) لتوضيح كيفية تلقي الوسائط بواسطة الملفات التنفيذية / الدُفعات الخارجية.

تمرير الحجة المكسورة الحالية:

  • استدعاء تطبيق متوافق مع الاصطلاحات (تطبيق .NET console يستخدم بواسطة dbea خلف الكواليس افتراضيًا):
# 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 يمرر الوسيطات إلى _batch file_ المساعد بدلاً من ذلك.

# 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.
  • استدعاء CLI على غرار msiexec ، 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 :

لاحظ استخدام -ie في مكالمات dbea ، مما يؤدي إلى استخدام ie للاستدعاءات.

  • استدعاء تطبيق متوافق مع الاصطلاحات (تطبيق .NET console يستخدم بواسطة dbea خلف الكواليس افتراضيًا):
# 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"" }">
  • استدعاء CLI على غرار msiexec ، 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 لديه إصلاح مقترح ويتم التحقق من

هل هناك طلب سحب لهذا الإصلاح المقترح؟ سيكون من الرائع لو تمكنا من التعليق على العلاقات العامة. لأن إصلاح هذا لم يكن IMO أبدًا الجزء المعقد. كان الجزء المعقد هو كيفية التعامل مع التوافق العكسي. وسيكون من الرائع لو استطعنا أن نرى كيف يعالج الإصلاح المقترح ذلك ....

من الجيد سماع ، @ SteveL-MSFT - إصلاح لجميع المشكلات التي نوقشت هنا؟ بالنسبة للإصدار 7.1 ، بعد كل شيء؟ وأنا ثاني طلب TSlivede .

@ yecril71pl ، هذا اكتشاف جيد ، على الرغم من أن هذا الاكتشاف يتعلق حقًا بالتحليل الخاص بـ PowerShell (والذي يحتوي بالفعل على غلاف خاص للملفات التنفيذية الخارجية) ، وليس كيفية إنشاء سطر الأوامر الأصلي _after_ التحليل (حيث تأتي المشكلات التي تمت مناقشتها سابقًا من عند).

وصف أكثر إيجازًا للمشكلة ، على يونكس:

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

أي أنه تم ربط عنصر المصفوفة _first_ فقط مباشرة بـ -a: ، وتم تمرير العناصر الأخرى كوسائط منفصلة.

هناك مشاكل ذات صلة بالوسيطات التي تبدو مثل معلمات PowerShell ، ولكنها ليست كذلك:

هناك مشكلة ذات صلة تؤثر فقط على المكالمات لأوامر _PowerShell_ التي تستخدم $args / @args : # 6360

  • عوائد & { $args.Count; $args } -foo:bar 2, '-foo:', 'bar'

هناك أيضًا # 6291 ، والذي يؤثر على كل من أوامر PowerShell والملفات التنفيذية الخارجية (لاحظ . ).

  • عوائد & { $args.Count; $args } -foo.bar 2, '-foo', '.bar'

شيء واحد يجب ملاحظته هو أن (...) كجزء من الكلمة الحادة يؤدي عادةً إلى تحويل مخرجات (...) ككل إلى وسيطة _ منفصلة_ ، وبالتالي فإن حقيقة أن العنصر الأول _هو_ مرفق في printf الأمر أعلاه
& { $args.Count; $args.ForEach({ "$_" }) } foo('bar', 'baz') ينتج 2, 'foo', 'bar baz' ، الوسيطة الثانية هي ترتيب المصفوفة 'bar', 'baz' .

عندما يتعين على PowerShell تمرير -A:(1,2) لملف تنفيذي خارجي ، فإنه يتضح أن -A: عبارة عن سلسلة و (1,2) عبارة عن مصفوفة يجب تنظيمها كـ "1 2". يحاول PowerShell الحفاظ على الصيغة الأصلية للاستدعاء ، لذلك ، بتجميعها معًا ، نحصل على "-A: 1 2" ، في حين أن النتيجة الصحيحة ستكون "-A:" 1 2 ". يبدو لي أنه إغفال تافه في رمز التنظيم.

لن أقول ، أن مشكلة @ 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 ، يحاول بوويرشيل الحصول على عداد لقيمة الوسيطة الحالية.

IEnumerator list = LanguagePrimitives.GetEnumerator(obj);

إذا كان list ليس null ، فإن بوويرشيل يضيف كل عنصر من عناصر القائمة (ربما يكون مقتبسًا إذا كان يحتوي على مسافات) إلى lpCommandLine.

يتم فصل العناصر بمسافات ( السطر 449 ) افتراضيًا. الاستثناء الوحيد هو إذا كانت المصفوفة حرفية
(كما في printf '<%s>\n' -a:1,2 ).
ثم يحاول بوويرشيل استخدام نفس الفاصل في lpCommandLine ، والذي تم استخدامه في سطر البرنامج النصي.

أتوقع العلاقات العامة عندما تكون جاهزًا. إذا وصل إلى 7.1 ، فسنأخذها ، وإلا فسيكون 7.2. التوافق مع الإصدارات السابقة هو شيء يتناوله. ربما يساعد بعض المساعدة في كتابة اختبارات Pester (باستخدام testexe -echoargs والتي يمكن إنشاؤها باستخدام publish-pstesttools من build.psm1).

أتوقع العلاقات العامة عندما تكون جاهزًا

هذا هو بالضبط ما أردت تجنبه - يرجى إظهار الكود غير الجاهز (ضع علامة على العلاقات العامة على أنها قيد التنفيذ).

أو على الأقل التعليق ، ماذا يريد أن يفعل.

سيكون من الرائع أن نناقش كيف يريد التعامل مع التوافق مع الإصدارات السابقة.

TSlivede ، لاحظ أنه نظرًا لاستدعاء PowerShell _CLI_ - ملف تنفيذي خارجي - تم تحليل -A:(1,2) _before_ مع العلم أن هذا الرمز المميز سيرتبط في النهاية بـ _ named_ -A - أن مثل هذه المعلمة _في النهاية_ تدخل حيز التنفيذ هو عرضي للمشكلة.

@ yecril71pl :

يتضح أن -A: عبارة عن سلسلة

لا ، إنه ذو غلاف خاص أثناء التحليل ، لأنه يحدث _ يشبه _ معلمة PowerShell.

يحدث هذا الغلاف الخاص للاستدعاءات لأوامر PowerShell التي تستخدم $args أيضًا (على عكس وجود معلمات معلنة فعلية ملزمة) ، ولكنها تحدث _ بشكل مختلف _ للملفات التنفيذية الخارجية (ما هو عادة وسيطة منفصلة تظل مرفقة ، ولكن في حالة المجموعة فقط عنصرها _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>

إذا كانت الوسيطة _doesn't_ تبدو وكأنها معلمة 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.

لتمرير وسيطة واحدة '-A:1 2 3' ، مع المصفوفة _stringified_ ، استخدم (n ضمني) _ سلسلة قابلة للتوسيع_ ، وفي هذه الحالة تحتاج إلى $(...) بدلاً من (...) _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.

_لا يجب أن تحتاج_ أيضًا إلى "..." في هذه الحالة - إنه ضروري مرة أخرى بسبب الانحرافات المتعلقة بالرموز التي تشبه المعلمات _ look_ (والتي تنطبق بشكل عام على _both_ 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) ، وليس سطر الأوامر الأولي نفسه.

لذلك لا يمكن اختبار وسائل الراحة مع عرض أسعار انتقائي لملفات الدُفعات و CLIs على غرار msiexec - بافتراض تنفيذ هذه التسهيلات ، وهو ما أوصي به بشدة ؛ على سبيل المثال ، لن تتمكن من التحقق من أن PROP='foo bar' قد تم تمريره كـ PROP="foo bar" ، مع اقتباس مزدوج حول جزء القيمة.

ومع ذلك ، لطباعة سطر الأوامر الخام ، يجب ألا يكون testexe ملفًا تنفيذيًا لـ .NET _Core_ ، لأن .NET Core _recreates a default command line_ that always use \" -escaping for embedded " chars. ، حتى لو تم استخدام "" ، وعمومًا لا يعكس بأمانة أي الحجج التي تم اقتباسها مرتين والتي لم تكن - للخلفية ، راجع https://github.com/ dotnet / وقت التشغيل / قضايا / 11305 # issuecomment -674554010.

فقط .NET _Framework_-compiled قابل للتنفيذ يعرض سطر الأوامر الحقيقي في Environment.CommandLine ، لذلك يجب تجميع testexe بهذه الطريقة (وتعديله (اختياريًا) إلى طباعة سطر الأوامر الخام).

لاختبار وسائل الراحة للملفات الدفعية ، يلزم ملف اختبار منفصل _batch_ ، للتحقق من أن 'a&b' تم تمريره كـ "a&b" و 'a"b' كـ "a""b" ، لـ نموذج.

لن يكون التحويل البرمجي لـ

بدلاً من ذلك ، هل يمكننا الاعتماد فقط على برنامج نصي بسيط لنظام التشغيل Linux / macOS لإصدار Args؟

#!/bin/bash
for i; do
   echo $i
done

وعلى Windows شيء مشابه مع ملف دفعي.

ماذا عن استخدام العقدة مع نص .js؟

console.log(process.execArgv.join('\n') أو أي سلسلة تتعامل معك
هل تريد أن تجعل الإخراج يبدو لطيفًا؟

cspotcode ، للحصول على سطر الأوامر

@ SteveL-MSFT:

في Windows ، يمكنك تفويض التجميع إلى _Windows PowerShell_ عبر CLI الخاص به ، وهو ما أفعله في 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"

أما بالنسبة إلى _batch file_ الذي يحاكي وسيطاته ، فإن dbea يقوم أيضًا بإنشاء واحدة عند الطلب .

في نظام التشغيل Unix ، يعد برنامج نصي بسيط ، كما هو موضح في تعليقك ، كافيًا حقًا ، ويمكنك حتى استخدام نص برمجي مخصص تقوم بتمريره إلى /bin/sh باعتباره _حجة_

ناقش @ PowerShell / Powershell- Committee هذا اليوم ، فنحن نطلب من

بالنسبة لأولئك الذين قد لا يكون لاحظت: تم نشر PR (كما WIP) ويجري بالفعل مناقشتها: https://github.com/PowerShell/PowerShell/pull/13482

PS ، @ SteveL-MSFT ، فيما يتعلق بالحصول على سطر أوامر أولي على Windows: بالطبع ، البديل لتفويض التجميع إلى Windows PowerShell / .NET _Framework_ هو تحسين تطبيق وحدة التحكم .NET _Core_ الحالي لإنشاء (النظام الأساسي المشروط) P / استدعاء استدعاء لوظيفة WinAPI GetCommandLine() ، كما هو موضح أدناه.

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 (مجموعة _ من الوسيطات_verbatim_ لاستخدامها كما هي على Unix وترجمتها إلى MS C / C ++ - سطر أوامر متوافق مع الاتفاقية على Windows بواسطة .NET Core نفسها ) ، ولكن دعني أوضح ذلك صراحة:

  • إصلاح هذه المشكلة بشكل صحيح ، نهائيًا ، يستبعد أي تنازلات للتوافق مع الإصدارات السابقة.

  • العلاقات العامة لـ JamesWTruher على المسار الصحيح حتى كتابة هذه السطور ، يبدو أن المشكلة الوحيدة هي أن الحجج الفارغة لم يتم تمريرها بعد .

    • بمجرد معالجة ذلك ، يكتمل الإصلاح في _ Unix_ - ولكنه يفتقر إلى وسائل الراحة المهمة لـ CLIs على _Windows_ (انظر أدناه).

قد نحتاج إلى قائمة سماح للأوامر المعروفة لحالة خاصة والتي لا تزال تفشل مع التغيير المقترح وهذا شيء يمكن إضافته لاحقًا.

أنا أحثك ​​على عدم تأجيل هذا حتى وقت لاحق .

بدلاً من _allowlist_ (غلاف خاص لملفات تنفيذية محددة) ، يمكن أن تحكم القواعد العامة البسيطة وسائل الراحة ، بعد تنقيحها من تلك المذكورة أعلاه بعد مزيد من المناقشة معTSlivede .

هذه التسهيلات التي _ لازمة على Windows فقط_:

على وجه التحديد ، هذه هي:

  • تمامًا كما هو الحال في نظام التشغيل Unix ، يتم استخدام ProcessStartInfo.ArgumentList افتراضيًا - إلا إذا تم استيفاء أحد الشروط التالية أو كليهما ، _ في هذه الحالة يجب إنشاء سطر أوامر العملية يدويًا_ (وتعيينه إلى ProcessStartInfo.Arguments كـ حاليا):

    • ملف دفعي ( .cmd ، .bat ) أو cmd.exe مباشرة يسمى:
    • في هذا الحدث ، يتم إفلات _embedded_ " كـ "" (بدلاً من \" ) ، وسيطات بدون مساحة تحتوي على أي من الأحرف الأولية التالية cmd.exe هي أيضًا ذات علامات اقتباس مزدوجة (عادةً ، فقط الوسيطات _ مع مسافات_ الحصول على علامة اقتباس مزدوجة): " & | < > ^ , ; - سيؤدي ذلك إلى إجراء استدعاءات لملفات الدُفعات تعمل بقوة ، وهو أمر مهم ، لأن العديد من CLIs عالية المستوى تستخدم الملفات الدفعية كـ _ إدخال نقاط_.
    • بشكل مستقل (وربما بالإضافة إلى ذلك) ، إذا كانت هناك وسيطة واحدة على الأقل تتطابق مع التعبير العادي
      '^([/-]\w+[=:]|\w+=)(.*? .*)$' موجود ، كل هذه الوسائط يجب أن تطبق _ جزئيًا_ الاقتباس المزدوج حول جزء القيمة فقط _ (ما يلي : أو = )

      • على سبيل المثال ، msiexec.exe / msdeploy.exe و cmdkey.exe وسيطات نمطية يتم عرضها بواسطة PowerShell حرفيًا على أنها
        FOO=bar baz و /foo:bar baz / -foo:bar baz في سطر أوامر العملية
        foo="bar baz" أو /foo:"bar baz" / -foo:"bar baz" صنع أي _ CLIs التي تتطلب هذا النمط من الاقتباس السعيد.
    • يجب معالجة الأحرف الحرفية \ في الوسيطات وفقًا لاتفاقيات MS C / C ++.

ما _لا _ تغطيه هذه التسهيلات:

  • msiexec.exe (ويفترض أن msdeploye.exe أيضًا) يدعم _ فقط_ "" إلغاء _ تضمين_ " حرف. ، والتي لن تغطيها القواعد المذكورة أعلاه - إلا إذا كنت تتصل عبر ملف دفعي أو 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 (ثم) تؤدي إلى "" -Escaping
    • Start-Process -Wait msiexec '/i example.msi PROPERTY="Nat ""King"" Cole"' - الاعتماد على المعلمة -ArgumentList ( -Args ) تمرير وسيطة سلسلة واحدة من خلال حرفيا كسطر أوامر العملية (على الرغم من أن هذه ليست الطريقة التي من المفترض أن تعمل بها هذه المعلمة - انظر # 5576).
  • أي CLIs أخرى غير تقليدية لم تكن التسهيلات المذكورة أعلاه كافية لها - لست على علم شخصيًا بأي منها.

في نهاية اليوم ، هناك دائمًا حل بديل: الاتصال عبر cmd /c ، أو ، للتطبيقات بخلاف وحدة التحكم ، عبر Start-Process ، أو استخدم --% ؛ إذا وعندما قدمنا ​​cmdlet ins ( Invoke-NativeShell ) ، فهذا خيار آخر ؛ a dbea ( Debug-ExecutableArguments cmdlet بقدرات شبيهة بقدرات echoArgs.exe ، ولكن عند الطلب أيضًا لملفات الدُفعات ، سيساعد أيضًا في تشخيص المشاكل.


بالنسبة إلى المسار المؤدي إلى تغيير مفاجئ مقابل الاشتراك:

  • هل يعني تنفيذ هذا كميزة تجريبية أنه إذا تم إظهار اهتمام كافٍ ، فإنه سيصبح السلوك _ الافتراضي وبالتالي سيصل إلى تغيير فاصل (غير بديهي)؟

  • هل يمكنك التأكد من نشر هذه الميزة التجريبية على نطاق واسع ، نظرًا لأهميتها؟

    • من دواعي القلق العام لدي بشأن الميزات التجريبية أن استخدامها يمكن أن يكون في كثير من الأحيان _ غير متزامن_ في إصدارات المعاينة ، نظرًا لأن _جميع _ الميزات التجريبية قيد التشغيل افتراضيًا. نريد بالتأكيد أن يعرف الناس هذه الميزة ويمارسونها عن عمد.
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات