Get-ChildItem -Recurse не работает, когда во входном пути есть квадратные скобки?

Итак, я чувствую, что это должно быть ошибкой в ​​​​PowerShell, но я хотел посмотреть, считаете ли вы, ребята, что это не работает. Это довольно легко воспроизвести, но я понимаю, почему это может быть не особенно распространенным вариантом использования. Шаги, которые я изложил ниже, на самом деле не являются тем, что делал мой скрипт, я на самом деле вычислял размеры подпапок - я просто сжал его до простейшего возможного сценария, который показывает мою проблему.

Я пробовал это только в PowerShell 5.0.10240.16384, но, возможно, вскоре у меня будет возможность протестировать его в более ранней версии [Редактировать: я проверил это на PowerShell 2.0, и ошибка не появляется в этой версии — она работает, как и ожидалось]. Небольшое примечание: я везде использовал gci как аббревиатуру Get-ChildItem. Если вы еще не знали, это работает и для ввода в PowerShell. Но проблема существует независимо от того, какой псевдоним вы используете.

Сначала создайте папку с именем Test [123] где-нибудь под рукой. В этой папке создайте пару файлов. Мои называются Test1.txt и Test2.txt. В них ничего не должно быть.

Затем откройте сеанс PowerShell и Set-Location в родительскую папку вашей новой папки Test [123].

Теперь запустите gci -Filter Test* и вы должны увидеть что-то вроде:

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       11/15/2015   3:22 PM                Test [123]

Все хорошо, верно? Затем попробуйте gci -Filter Test* | gci. Это делает вывод первого gci входом для следующего, т.е. показывает нам дочерние элементы каждого элемента, возвращаемого первым gci. Это дает нам следующее:

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       11/14/2015  10:21 PM              0 Test1.txt
-a----       11/15/2015   3:55 PM              0 Test2.txt

Опять же, все хорошо - именно так, как ожидалось. Это все файлы из нашей папки Test [123].

Теперь попробуйте это: gci -Filter Test* | gci -Recurse. Все, что мы изменили, это то, что второй вызов gci теперь рекурсивный, поэтому он должен показывать нам все файлы, включая подпапки. У нас нет подпапок, поэтому мы ожидаем таких же результатов, верно?

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

А вот и следующая странность. Теперь попробуйте gci -Filter Test* | gci -Recurse -Name. Это то же самое, что и раньше, за исключением того, что добавлен параметр -Name. Это просто говорит о том, что нам нужен тот же результат, за исключением того, что нам нужны только имена файлов, а не полная информация о каждом элементе. Так что, скорее всего, мы ничего не ждем взамен. Но это не то, что мы получаем, мы получаем это:

Test1.txt
Test2.txt

Это нужно сломать, верно? Во-первых, -Recurse никогда не должен уменьшать объем вывода, а во-вторых, запрос вывода в другом формате не должен изменять объем вывода, который мы получаем.

Это происходит только тогда, когда в имени папки есть квадратные скобки. Если вы создадите другую папку, только что названную Test, и снова запустите все приведенные выше команды, вы всегда будете видеть файлы из папки Test.

Мои исследования привели меня к параметру -LiteralPath; однако, перефразируя приведенную выше команду как gci -Filter Test* | foreach { gci -Recurse -LiteralPath $_ }, чтобы использовать этот параметр, по-прежнему не возвращаются выходные данные, и снова добавление параметра -Name снова запускает возврат файлов.

Мне удалось найти обходной путь для этого, используя параметр -Name, а затем объединив его с путем к папке, которую я ищу, а затем передав его в Get-Item, но это сделало код длиннее и уродливее, чем он должен быть.

Итак, мой вопрос: я сделал ошибку или что-то не так понял? Или это ошибка, о которой я должен сообщить?


person Richard Irons    schedule 15.11.2015    source источник
comment
Используйте -Include * в качестве обходного пути.   -  person user4003407    schedule 15.11.2015
comment
Вау, это работает! Спасибо!   -  person Richard Irons    schedule 15.11.2015
comment
@RichardIrons Кажется, это связано с тем, как реализация -Recurse обрабатывает подстановочные знаки ([ и ]) в имени. Если вы экранируете их вручную, это тоже работает   -  person Mathias R. Jessen    schedule 15.11.2015
comment
@MathiasR.Jessen Почему [Regex]::Replace вместо [System.Management.Automation.WildcardPattern]::Escape?   -  person user4003407    schedule 16.11.2015
comment
@PetSerAl По той простой причине, что я не знал о WildcardPattern.Escape() до 50 секунд назад :)   -  person Mathias R. Jessen    schedule 16.11.2015
comment
Просто хочу отметить, что теперь у меня была возможность протестировать это на PowerShell 2.0, и ошибка не появляется в этой версии. Это работает так, как вы ожидаете. Я обновил вопрос, чтобы отразить это.   -  person Richard Irons    schedule 16.11.2015
comment
Хотя я люблю PowerShell, командлет Get-ChildItem — это полный беспорядок. Одна вещь в PS, которая никогда не дает последовательного вывода.   -  person beatcracker    schedule 16.11.2015


Ответы (2)


TL;DR: используйте -LiteralPath для папки, когда в именах могут быть необычные символы (включая квадратные скобки).


Мне не удалось воспроизвести это в соответствии с OP в PowerShell v5.1.17134.590.

Однако мне удалось воспроизвести что-то подобное, используя следующую команду, чтобы попытаться вывести список файлов в папке, которая, как я подозревал, была пустой, чтобы удалить ее. На самом деле в этой папке было 12 .mp3 файлов:

[PS]> gci '.\Music\Artist - Name\Album Name [Disc 1]\'

[PS]>

Добавление переключателя -Recurse привело к тому, что командлет вернул ошибку, а не (вводящий в заблуждение) пустой ответ, показанный выше. Я протестировал ту же команду с параметром -Include *, и все равно не было никаких результатов, однако на этот раз не было ошибки в сочетании с переключателем -Recurse.

-литеральный путь

Лучше всего для меня сработало указание пути с параметром -LiteralPath:

[PS]> gci -LiteralPath '.\Music\Artist - Name\Album Name [Disc 1]\'

Побег

Чтобы охватить некоторые другие возможные случаи, вы можете попытаться избежать квадратных скобок. Используя предложение @PetSerAl в комментариях к OP, вы можете попробовать что-то вроде этого:

[PS]> [System.Management.Automation.WildcardPattern]::Escape('.\Music\Artist - Name\Album Name [Disc 1]\')

.\Music\Artist - Name\Album Name `[Disc 1`]\

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

[PS]> gci '.\Music\Artist - Name\Album Name ``[Disc 1``]\'

Дополнительные сведения о квадратных скобках и экранировании см. в следующих вопросах и ответах:

person Charlie Joynt    schedule 26.03.2019

Похоже, что об ошибке сообщалось здесь или очень похожей проблеме.

person Roman Kuzmin    schedule 15.11.2015
comment
Похоже, это та же ошибка, и я думаю, что она была исправлена ​​в большом ноябрьском обновлении для Win10. - person Jason Shirk; 18.11.2015
comment
К сожалению, он все еще существует в 2020 году на моем Win10 Enterprise 2016 LTS с PS 5.1. Я пытаюсь получить файлы gci с датами ...[20200701].log - person chingNotCHing; 09.07.2020