Compare-Object возвращает неверные результаты

Первый пост в stackoverflow, поэтому заранее извиняюсь, если я пропущу социальную норму.

У меня есть массив в PowerShell, который содержит тысячи объектов. Стандартные вещи с каждым объектом, имеющим такие свойства, как имя пользователя, время входа в систему, компьютер, на который они вошли, и т. д. Затем я отделил небольшое количество объектов от первого массива с пользователями, чье имя начинается с A:

$Array2 = $Array1 | where-object Username -like '*\a*'

Это работает, как и ожидалось.

Я хочу построить третий массив, содержащий только объекты из массива $Array1, которых нет в массиве $Array2. Или, другими словами, массив только с пользователями, имена которых не начинаются с буквы A. Похоже, это путь:

$Array3 = compare-object $Array1 $Array2 | select-object -expandproperty InputObject

Ожидание:

$Array3 | where-object Username -like '*\a*'

ничего не возвращается

То, что я получаю, хотя много результатов. Как ни странно, если я посмотрю на .count каждого массива, математика получится. Массив1 минус Массив2 равно Массив3. Таким образом, он удаляет правильное количество объектов, а не ожидаемые объекты. Что я здесь делаю неправильно? Поскольку я делаю выборку из точного массива, с которым я сравниваю, я не могу придумать никаких других критериев, по которым он будет искать, которые вернут неожиданное совпадение. Я также пытался отсортировать каждый массив по одному и тому же свойству перед сравнением, потому что я схватывал, но, как и ожидалось, это не помогло.

Заранее спасибо!


person Troy    schedule 23.04.2020    source источник
comment
Вам не хватает знака доллара перед массивом1: $Array2 = Array1 | where-object Username -like '*\a*'. Я попробовал это с помощью get-aduser, но со свойством name, и у меня это работает. У меня нет свойства имени пользователя или имен с обратной косой чертой. Если вы не назначите $array3, правильно ли будет выглядеть вывод объекта сравнения?   -  person js2010    schedule 24.04.2020
comment
В имени пользователя только одна обратная косая черта? dom/joe/abel Compare-object просматривает строковую версию объекта aduser или DistinguishedName. Он не смотрит на свойство имени пользователя.   -  person js2010    schedule 24.04.2020


Ответы (5)


Самый простой способ — выполнить такое же присваивание -like, но с противоположным оператором -notlike, например:

$Array2 = $Array1 | where-object Username -like 'a*'
$Array3 = $Array1 | where-object Username -notlike 'a*'

Примечание:

(не уверен, что это была опечатка в вопросе о совпадении: '*\a*'). Соответствие должно быть a* только с одной звездочкой после символа «a». Вы хотите сопоставить все, начинающееся с "a" и "*" для всего, что после (например, только "Алекс", а не "Брэд").

person HAL9256    schedule 23.04.2020
comment
Извините, я должен был объяснить это, так как это действительно похоже на опечатку. *\ потому что я имею дело с несколькими доменами в своем свойстве имени пользователя. Итак, я захватываю пользователей domain1\a* и domain2\a*. - person Troy; 23.04.2020

Вы можете решить эту проблему, отрицая операнд Where-Object (-notlike вместо -like):

$Array2 = $Array1 |Where-Object Username -like *\a*
$Array3 = $Array1 |Where-Object Username -notlike *\a*

... или вы можете воспользоваться режимом "Разделение" метода расширения .Where() и сделать это в одном назначении:

$Array2,$Array3 = $Array1.Where({$_.Username -like '*\a*'}, 'Split')
person Mathias R. Jessen    schedule 23.04.2020
comment
Этот первый пример работает для меня. Вам не хватает кавычек: {$_ -like 'a*'} - person js2010; 23.04.2020
comment
Я чувствую, что это близко к объяснению этого, но я думаю, что ошибка на моей стороне. Если я правильно вас понимаю, математическая часть не соответствует тому, что я вижу. В вашем примере $Array1.count будет шесть, $Array2.count будет два, и сравнение двух разных объектов (ваша средняя строка) приведет к пяти несовпадениям. Но я вижу, что $Array1.count минус $Array2.count дает четыре, а не пять. Мне все еще чего-то не хватает в том, как это работает. - person Troy; 23.04.2020
comment
Итак, после некоторых проб и ошибок это сработало: «сравнить объект $Array1 $Array2 -Property Username -PassThru», хотя я до сих пор не знаю, почему, что беспокоит. - person Troy; 23.04.2020
comment
Нет, мое объяснение было совершенно неверным, логично, что оно сработает с -Property Username - person Mathias R. Jessen; 23.04.2020
comment
Желаю, чтобы это было для меня! В любом случае спасибо за попытку объяснения. Я не буду отмечать ни один из них в качестве ответа, хотя мой вопрос о том, почему объект сравнения не работает так, как я думаю, а не о том, как создать другое решение, чтобы получить то, что мне нужно. - person Troy; 24.04.2020

Сравните предметные работы в забавной форме. Для объектов обычно необходимо указывать свойства. Я удивлен, что в документах нет примера этого. К сожалению, -property не принимает подстановочные знаки. Я думаю, что без "-property", если нет специального метода compareto() или equals(), он будет сравнивать строковые версии объектов. Я думаю, что с выводом get-aduser он будет сравнивать строки (отличающиеся имена).

$a = @([pscustomobject]@{name='joe'})              
$b = @([pscustomobject]@{name='joey'})

compare-object $a $b  # no output, they're equal!


compare-object $a $b -property name

name SideIndicator
---- -------------
joey =>
joe  <=

Пробуем дату и время. Он использует что-то помимо строк для сравнения.

$a = get-date; sleep -Milli 500; $b = get-date
$a.tostring(); $b.tostring()
4/23/2020 6:33:56 PM
4/23/2020 6:33:56 PM

$a -eq $b
False

compare-object $a $b

InputObject          SideIndicator
-----------          -------------
4/23/2020 6:33:56 PM =>
4/23/2020 6:33:56 PM <=
person js2010    schedule 23.04.2020
comment
Но если я вытаскиваю объект непосредственно из первого массива, все свойства должны быть одинаковыми. Очевидно, что он сравнивает два массива и успешно определяет, сколько между ними различий. Разве это не доказывает, что можно сравнивать объекты, а не только свойства? - person Troy; 23.04.2020
comment
Я не вижу этого, если строковые версии объектов не отличаются. Можете ли вы привести пример? - person js2010; 23.04.2020
comment
Я не уверен, как создать пример, который другие могли бы попробовать без доступа к моим данным. Если я смогу воссоздать его с помощью get-aduser, сможете ли вы воссоздать его на своей стороне? - person Troy; 24.04.2020
comment
Даже с get-aduser все, что делает объект сравнения, — это запускает $obj.tostring() для каждого объекта, а затем сравнивает строки. - person js2010; 24.04.2020
comment
Кто-то сказал мне, что объект сравнения может сначала попробовать метод .compareto() или .equals(), прежде чем прибегать к .tostring(). Но объекты aduser не имеют сравнения. Дата и время делать. - person js2010; 24.04.2020
comment
Виноват. Объекты aduser имеют метод equals(). Хм, я не уверен, что это конкретно для этого типа. Я могу установить для одного из них enable значение false, и объект сравнения все равно скажет, что они равны. - person js2010; 24.04.2020
comment
Использование Get-ADUser также сработало для меня. Это должно быть связано с тем, как командлет Citrix get-brokersession создает объекты массива. Я собираюсь оставить это, сделав вывод, что некоторые командлеты строят массивы несовместимым образом с тем, как работает compare-object, и продолжаем жить дальше. Спасибо, что остаетесь со мной в моем замешательстве! - person Troy; 25.04.2020
comment
В порядке. Если бы вы сказали, какие команды вы использовали, это помогло бы. - person js2010; 25.04.2020

Я думаю, что Compare-Object внутренне использует метод ToString() для сравнения объектов.

class foo {
    [string]$str

    foo($str) {
        $this.str = $str
    }
}

class bar {
    [string]$str

    bar($str) {
        $this.str = $str
    }

    [string] ToString() { return $this.str }
}

"== Compare foo object =="
$fooA = [foo]::new("a")
$fooB = [foo]::new("b")
compare $fooA $fooB -IncludeEqual | ft

"== Compare bar object =="
$barA = [bar]::new("a")
$barB = [bar]::new("b")
compare $barA $barB -IncludeEqual | ft

выход:

== Compare foo object ==

InputObject SideIndicator
----------- -------------
foo         ==           


== Compare bar object ==

InputObject SideIndicator
----------- -------------
b           =>           
a           <=  
person rokumaru    schedule 23.04.2020
comment
Ага. Это то, что делает пример объекта процесса в документации. - person js2010; 23.04.2020
comment
Это может ответить на мой вопрос, но я недостаточно силен в PowerShell, чтобы перейти от того, кто объясняет, как работает двигатель, к знанию, почему моя машина не заводится. ЭЛИ 5 это? - person Troy; 24.04.2020

Мой вывод состоит в том, что некоторые командлеты строят массивы таким образом, что это ломает объект сравнения. Я до сих пор не уверен, что это за загадочный «путь», но проблема проявляется при использовании Citrix Get-BrokerSession, а не при использовании Get-AdUser.

Длинный ответ. Для тех, кто действительно хочет использовать объект сравнения, вот как я заставил его работать:

compare-object $Array1 $Array2 -Property Username -PassThru

Когда -PassThru стал для меня моментом озарения.

Мой вопрос был конкретно о том, как/почему объект сравнения не работает так, как я ожидал. Все ответы, в моей интерпретации, только предлагали разные способы решения проблемы и на самом деле не отвечали на вопрос. Особая благодарность @js2010 за то, что он продолжает работать со мной.

person Troy    schedule 24.04.2020