windows - بوويرشيل: ملف فارغ ولدت دائما(إخراج كائن المقارنة)




powershell file (2)

تتضمن الإجابة الأكثر شيوعًا لهذا السؤال رمز powerhell Windows التالي (تم تحريره لإصلاح الخلل):

$file1 = Get-Content C:\temp\file1.txt  
$file2 = Get-Content C:\temp\file2.txt   
$Diff = Compare-Object $File1 $File2  
$LeftSide = ($Diff | Where-Object {$_.SideIndicator -eq '<='}).InputObject  
$LeftSide | Set-Content C:\temp\file3.txt

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

لماذا يكون ملف الإخراج فارغًا دائمًا ، وكيف يمكن إصلاحه؟


ربما بدلا من

$LeftSide = ($Diff | Where-Object {$_.SideIndicator -eq '<='}).InputObject  

قد يعمل PowerShell 2 بشكل أفضل مع:

$LeftSide = $Diff | Where-Object {$_.SideIndicator -eq '<='} | 
            Foreach-object { $_.InputObject } 

PetSerAl ، كما يفعل بشكل روتيني ، بتوفير المؤشر الحاسم في تعليق على السؤال (مع عدم وجود نية PetSerAl إلى إجابة):

تعداد الأعضاء - تم تقديم القدرة على الوصول إلى عضو (خاصية أو طريقة) على مجموعة وتطبيقها ضمنيًا على كل عنصر من عناصرها ، مع تجميع النتائج في صفيف ، في PSv3 .

تعداد الأعضاء ليس معبّرًا ومريحًا فحسب ، بل إنه أسرع أيضًا من المناهج البديلة .

مثال مبسط:

PS> ((Get-Item /), (Get-Item $HOME)).Mode
d--hs-   # The value of (Get-Item /).Mode
d-----   # The value of (Get-Item $HOME).Mode

تطبيق .Mode على المجموعة التي تؤدي مخرجات الأمر (...) المغلقة إلى الوصول إلى خاصية .Mode على كل عنصر في المجموعة ، مع إرجاع القيم الناتجة كصفيف (صفيف PowerShell منتظم ، من النوع [System.Object[]] ).

تحذير : يتعامل تعداد الأعضاء مع الصفيف الناتج كما يفعل خط الأنابيب ، مما يعني:

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

    PS> @([pscustomobject] @{foo=1}).foo.GetType().Name
    Int32  # 1 was returned as a scalar, not as a single-element array.
  • إذا كانت قيم الخصائص التي يتم تجميعها هي نفسها صفائف ، فسيتم إرجاع مجموعة مسطحة من القيم:

    PS> @([pscustomobject] @{foo=1,2}, [pscustomobject] @{foo=3,4}).foo.Count
    4 # a single, flat array was returned: 1, 2, 3, 4

أيضًا ، يعمل تعداد الأعضاء فقط للحصول على قيم خاصية (القراءة) ، وليس لإعدادها (كتابتها). هذا التباين حسب التصميم ، لتجنب التعديل بالجملة غير المرغوب فيه ؛ في PSv4 + ، استخدم .ForEach('<property-name', <new-value>) كحل أسرع (انظر أدناه).

هذه الميزة المريحة غير متوفرة ، ولكن:

  • إذا كنت تعمل على PSv2 (بشكل قاطع)
  • إذا كان لدى المجموعة نفسها عضو بالاسم المحدد ، في هذه الحالة يتم تطبيق عضو مستوى المجموعة.

على سبيل المثال ، حتى في نظام PSv3 + ما يلي لا يقوم بتعداد الأعضاء:

    PS> ('abc', 'cdefg').Length  # Try to report the string lengths
    2 # !! The *array's* .Length property value (item count) is reported, not the items'

في مثل هذه الحالات - وفي PSv2 بشكل عام - هناك حاجة إلى نهج مختلف:

  • أسرع بديل ، باستخدام عبارة foreach ، بافتراض أن المجموعة بأكملها تناسب الذاكرة ككل (وهذا يعني ضمنيًا عند استخدام تعداد الأعضاء).
PS> foreach ($s in 'abc', 'cdefg') { $s.Length }
3
5
  • بديل PSv4 + ، باستخدام طريقة الجمع .ForEach() ، التي تعمل أيضًا على المجموعة ككل:
PS> ('abc', 'cdefg').ForEach('Length')
3
5

ملاحظة: إذا كان ذلك قابلاً للتطبيق على مجموعة الإدخال ، يمكنك أيضًا تعيين قيم الخصائص باستخدام .ForEach('<prop-name>', <new-value>) ، والذي يعد أسرع الحلول لعدم القدرة على الاستخدام .<prop-name> = <new-value> ، أي عدم القدرة على تعيين قيم الخصائص مع تعداد الأعضاء.

  • الطرق الأبطأ ولكن الفعالة في الذاكرة ، باستخدام خط الأنابيب :

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

باستخدام أمر ForEach-Object cmdlet ، كما في إجابة Burt Harris المفيدة :

PS> 'abc', 'cdefg' | ForEach-Object { $_.Length }
3
5

بالنسبة للخصائص فقط (على عكس الأساليب) ، يعد Select-Object -ExpandProperty خيارًا ؛ إنه واضح وبسيط من الناحية النظرية ، ForEach-Object نهج ForEach-Object من حيث الأداء (من أجل مقارنة الأداء ، انظر القسم الأخير من هذه الإجابة ).

PS> 'abc', 'cdefg' | Select-Object -ExpandProperty Length
3
5




member-enumeration