Почему разные способы фильтрации Get-ChildItem дают одинаковые объекты, которые на самом деле разные? - PullRequest
1 голос
/ 12 октября 2019

По нескольким несвязанным причинам я играю несколько способов фильтрации выходных данных конвейера при использовании команды Get-ChildItem.

Я создал короткий код, чтобы проиллюстрировать, что я имею в виду. Когда я использую разные способы, чтобы получить один и тот же элемент, элемент «зашнуруется» по-разному, в зависимости от того, как он был найден, даже если каждый раз это один и тот же элемент.

Допустим, у нас есть эта папка C:\Folder1\Folder1-aс File1.7z и File2.txt внутри. Внутри Folder1 находится больше папок File1-X, каждая из которых содержит файл .7z и .txt, и их имена могут содержать некоторые специальные символы, например квадратные скобки. Это не относится к этому вопросу, но это причина того, почему я предпочел бы использовать какой-то определенный способ фильтрации по другому (объяснено в комментариях к прилагаемому коду).

Вот код, который иллюстрирует мою точку зрения:

#Initialize 7zip
if (-not (test-path "$env:ProgramFiles\7-Zip\7z.exe")) {throw "$env:ProgramFiles\7-Zip\7z.exe needed"}
set-alias 7zip "$env:ProgramFiles\7-Zip\7z.exe"

#this is a placeholder, $TXTFile would be the result of an iteration over a previous Item array
$TXTFile = Get-Item -Path "C:\Folder1\Folder1-a\File2.txt"

#Now there comes 3 different ways to select the 7z file in the same directory as File2.txt

# I use this to modify the path name so square brackets are properly escaped
$directory1 =$TXTFile.DirectoryName -replace "\[","`````[" -replace "\]","`````]"
[array]$7ZFile1 = Get-ChildItem -File -Path "$directory1\*.7z"

# This option uses LiteralPath so no modification is needed.
# More convenient since it supports any kind of special character in the name.
$directory2=$TXTFile.DirectoryName
[array]$7ZFile2= Get-ChildItem -File -LiteralPath $directory2 -Filter *.7z

# This option uses LiteralPath so no modification is needed.
# More convenient since it supports any kind of special character in the name.
$directory3=$TXTFile.DirectoryName
[array]$7ZFile3 = Get-ChildItem -File -LiteralPath $directory3 | Where-Object {$_.Extension -eq ".7z"}

#Lets see each item. They all seem equal
$7ZFile1
$7ZFile2
$7ZFile3
Write-Host "`n"

#Lets see how they have he same FullName
Write-Host $7ZFile1.FullName
Write-Host $7ZFile2.FullName
Write-Host $7ZFile3.FullName
Write-Host "`n"

#Lets compare them using -eq. Damn, they are not equal
if($7ZFile1 -eq $7ZFile2){"7ZFile1=7ZFile2"}Else{"7ZFile1!=7ZFile2"}
if($7ZFile2 -eq $7ZFile3){"7ZFile2=7ZFile3"}Else{"7ZFile2!=7ZFile3"}
if($7ZFile3 -eq $7ZFile1){"7ZFile3=7ZFile1"}Else{"7ZFile3!=7ZFile1"}
Write-Host "`n"

#This is relevant if we "stringify" each object. First one returns FullName, the two others return Name
Write-Host $7ZFile1
Write-Host $7ZFile2
Write-Host $7ZFile3
Write-Host "`n"

#Example of this being relevant. Inside File1.7z is a txt file. If you use 7zip por example like this:
7zip t $7ZFile1 *.txt -scrc     #Success
7zip t $7ZFile2 *.txt -scrc     #Fail, can't find 7ZFile2
7zip t $7ZFile3 *.txt -scrc     #Fail, can't find 7ZFile3

Я использую $7ZFile.FullName, чтобы всегда всегда получать желаемую строку, однако я хотел бы знать, почему это происходит? почему разница в первую очередь?

Ответы [ 2 ]

1 голос
/ 13 октября 2019

Здесь есть две не связанные проблемы:

  • In Windows PowerShell - но, к счастью, больше не в PowerShell Core - экземпляры System.IO.DirectoryInfo и System.IO.FileInfo, которые Get-ChildItem выводят ситуативно по-разному - простое имя файла или полный путь - в зависимости от спецификиGet-ChildItem вызов.

    • Подробнее см. в этом ответе .
    • Основной причиной является несоответствие типов .NET, которое было исправлено. в .NET Core (на котором построен PowerShell Core ), как объяснено в этом комментарии к GitHub .
    • На всякий случай всегдаиспользовать свойство .FullName при передаче экземпляров в качестве аргументов другим командам или при намерении для перевода в полное имя .

    • Простая демонстрация проблемы:

# Full-name stringification, due to targeting a *file* (pattern).
PS> (Get-ChildItem $PSHOME\powershell.exe).ToString()
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

# Name-only stringification, due to targeting a *directory* by *literal* name 
# (even though a filter to get a file is applied) and 
# not also using -Include / -Exclude
PS> (Get-ChildItem $PSHOME -Filter powershell.exe).ToString()
powershell.exe
  • System.IO.DirectoryInfo и System.IO.FileInfo являются ссылочными типами , что означает, что их экземпляры сравниваются с использованием ссылочного равенства : то есть две переменные, содержащие экземпляры , сравнивают только одно, если они указывают на тот же самый объект в памяти .

    • Следовательно, $7ZFile1 -eq $7ZFile2 равно никогда $true, если были получены два экземпляра разными Get-ChildItem звонками; лучший подход заключается в сравнении экземпляров по их .FullName свойству .

    • См. этот ответ для получения дополнительной информации о равенстве ссылок и значенииравенство.

    • Простая демонстрация поведения эталонного равенства:

PS> (Get-ChildItem $PSHOME\powershell.exe) -eq (Get-ChildItem $PSHOME\powershell.exe)
False  # Distinct FileInfo objects, which aren't reference-equal.
0 голосов
/ 12 октября 2019

Это обычное раздражение в PS 5, где строковая версия того, что возвращает get-childitem, не имеет полного пути. Это изменилось в более поздних версиях PS. Получить полный путь к файлам в PowerShell

get-childitem .   | foreach tostring  # not full path
get-childitem .\* | foreach tostring  # full path
...