Странное цитирование аргументов с пробелами в PowerShell

Я пытаюсь разработать довольно простой так называемый SendLet. Он состоит из сценария PowerShell, который запускается с помощью ярлыка «Отправить» из контекстного меню проводника Windows. Вызванные для произвольных файлов или папок, они передаются сценарию в качестве аргументов. Мой подход заключается в следующем:

SendLet.ps1

param (
    [Parameter(ValueFromRemainingArguments=$true)]
    $FilePath
)

Get-ChildItem -Recurse -Path $FilePath | ForEach-Object {
  Write-Host $_
}

Pause

Произвольные файлы

PowerShell 7.3.2
PS C:\Users\user\Desktop\Arbitrary Files> Get-ChildItem

    Directory: C:\Users\user\Desktop\Arbitrary Files

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          29.01.2023    16:44      276246528 Baz qux.AVI
-a---          29.01.2023    16:44        3787047 Foo bar.JPG

PS C:\Users\user\Desktop\Arbitrary Files>

Настраивать

  1. Сохраните скрипт SendLet как C:\Users\user\Documents\PowerShell\Scripts\SendLet.ps1
  2. Создайте ярлык в shell:sendto (например, как C:\Users\user\AppData\Roaming\Microsoft\Windows\SendTo\SendLet.lnk) с целью C:\Users\user\AppData\Local\Microsoft\WindowsApps\pwsh.exe C:\Users\user\Documents\PowerShell\Scripts\SendLet.ps1.
  3. Отправлять произвольные файлы или папки в скрипт через контекстное меню Отправить.

Бегать

Выполнение описанного выше подхода на моем компьютере с Windows 11 22H2 приводит к следующему выводу:

C:\Users\user\Desktop\Arbitrary Files\Baz qux.AVI
C:\Users\user\Desktop\Arbitrary Files\Foo bar.JPG
Press Enter to continue...:

Значение: найдены два файла с абсолютным путем для обработки...

Однако использование того же подхода на моем компьютере с Windows 10 Pro 22H2 приводит к следующей ошибке:

Get-ChildItem: D:\Users\user\Documents\PowerShell\Scripts\SendLet.ps1:6
Line |
   6 |  Get-ChildItem -Recurse -Path $FilePath | ForEach-Object {
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot find path 'D:\Users\user\Documents\PowerShell\Scripts\Files\' because it does not exist.
Get-ChildItem: D:\Users\user\Documents\PowerShell\Scripts\SendLet.ps1:6
Line |
   6 |  Get-ChildItem -Recurse -Path $FilePath | ForEach-Object {
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot find path 'D:\Users\user\Documents\PowerShell\Scripts\Files\' because it does not exist.
Press Enter to continue...:

Путь, на который жалуются в сообщении об ошибке, совершенно неверен и не существует. Вот почему его нет! Так что само сообщение об ошибке правильное. Но почему скрипт ищет неверный аргумент пути? Я предполагаю, что цитирование аргумента не работает должным образом.

/UPDATE: Единственное отличие, которое я вижу, заключается в том, что тестовые данные помещаются на диск C: моей рабочей машины. На моей неисправной машине тестовые данные помещаются на диск D: вместо этого. Но почему это должно иметь значение?

/UPDATE 2: Теперь я проверил размещение тестовых данных на диске C: моей неисправной машины. Странно все работает!? Итак, мой SendLet работает с файлами или папками на диске C:, но не с диском D: :-(

/ОБНОВЛЕНИЕ 3: я протестировал оба сценария, предложенные @zett42 в качестве решения. Результат немного странный.

Запустите папку «Произвольные файлы» (упомянутую выше) на моем рабочем компьютере с Windows 11, второй скрипт напечатает:

Processing file: C:\Users\user\Desktop\Arbitrary Files\Baz qux.AVI
Processing file: C:\Users\user\Desktop\Arbitrary Files\Foo bar.JPG
Press Enter to continue...:

Второй скрипт, запущенный на двух содержащихся файлах, печатает:

Processing file: C:\Users\user\Desktop\Arbitrary Files\Baz qux.AVI
Processing file: C:\Users\user\Desktop\Arbitrary Files\Foo bar.JPG
Press Enter to continue...:

Пока все хорошо... И теперь то же самое на моей проблемной машине с Windows 10 Pro.

Второй скрипт запускает печать папки:

Processing file: D:\Users\user\Desktop\Arbitrary
Processing file: Files
Press Enter to continue...:

Запустите непосредственно два содержащихся файла, которые он печатает:

Processing file: D:\Users\user\Desktop\Arbitrary
Processing file: Files\Baz
Processing file: qux.AVI
Processing file: D:\Users\user\Desktop\Arbitrary
Processing file: Files\Foo
Processing file: bar.JPG
Press Enter to continue...:

Что это? Что происходит с моей машиной с Windows 10 Pro?

Похоже, моя машина с Windows 11 запускает сценарий PowerShell для папки с аргументами как "C:\Users\user\Desktop\Arbitrary Files". См. один аргумент в кавычках, содержащий пробелы.

Однако мой компьютер с Windows 10 Pro, похоже, запускает скрипт в папке с аргументами как C:\Users\user\Desktop\Arbitrary Files, в результате чего два аргумента разделены пробелом.

Такое же поведение, по-видимому, применимо к сценарию, запускаемому непосредственно в двух тестовых файлах в папке в качестве аргументов в кавычках:

"C:\Users\user\Desktop\Arbitrary Files\Baz qux.AVI" "C:\Users\user\Desktop\Arbitrary Files\Foo bar.JPG" => Два аргумента в кавычках

... или аргументы без кавычек:

C:\Users\user\Desktop\Arbitrary Files\Baz qux.AVI C:\Users\user\Desktop\Arbitrary Files\Foo bar.JPG => 6 аргументов без кавычек

Это не может быть нормальным поведением. Или это?

Может быть, я что-то упускаю? Какие-либо предложения?

/ОБНОВЛЕНИЕ 4: Я обнаружил проблему -__-, которая оказалась очень и очень раздражающей... Я еще раз проверил ссылки SendTo на обеих своих машинах. Единственным отличием был абсолютный путь к интерпретатору pwsh.exe, потому что PowerShell был установлен по-другому.

На хорошей машине путь к pwsh.exe указывает на C:\Program Files\PowerShell\7\pwsh.exe. Все идет нормально.

На проблемной машине путь, указывающий на pwsh.exe, вместо этого был C:\Users\user\AppData\Local\Microsoft\WindowsApps\pwsh.exe. Ну и что, подумал я. Это просто другой путь. Но когда я открыл папку в проводнике, у меня возникли подозрения, потому что файл pwsh.exe имеет размер 0 байт :-O Затем я открыл ту же папку в терминале PowerShell и набрал ls, чтобы показать то, чего я не ожидал. Все EXE-файлы там просто ссылки на что-то еще, но я не знаю, куда? Что означают стрелки ->?

PowerShell 7.3.2
PS C:\Users\user\AppData\Local\Microsoft\WindowsApps> ls

    Directory: C:\Users\user\AppData\Local\Microsoft\WindowsApps

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          10.01.2023    18:44                Backup
d----          25.01.2023    10:57                Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
d----          20.12.2022    11:50                Microsoft.MicrosoftEdge_8wekyb3d8bbwe
d----          28.01.2023    23:08                Microsoft.PowerShell_8wekyb3d8bbwe
d----          26.01.2023    17:18                Microsoft.SkypeApp_kzf8qxf38zg5c
d----          28.01.2023    14:19                Microsoft.WindowsTerminal_8wekyb3d8bbwe
d----          10.02.2023    10:50                Microsoft.XboxGamingOverlay_8wekyb3d8bbwe
d----          03.02.2023    15:55                SpotifyAB.SpotifyMusic_zpdnekdrzrea0
la---          10.02.2023    10:50              0 GameBarElevatedFT_Alias.exe ->
la---          20.12.2022    11:50              0 MicrosoftEdge.exe ->
la---          28.01.2023    23:08              0 pwsh.exe ->
la---          25.01.2023    10:57              0 python.exe ->
la---          25.01.2023    10:57              0 python3.exe ->
la---          26.01.2023    17:18              0 Skype.exe ->
la---          03.02.2023    15:55              0 Spotify.exe ->
la---          25.01.2023    10:57              0 WindowsPackageManagerServer.exe ->
la---          25.01.2023    10:57              0 winget.exe ->
la---          28.01.2023    14:19              0 wt.exe ->

PS C:\Users\user\AppData\Local\Microsoft\WindowsApps>

В заключение я заключаю, что проблема здесь в том, что ВСЕ аргументы, переданные в интерпретатор-ссылку, не передаются правильно (кавычки теряются) интерпретатору PowerShell!

Настройка моего SendLet, указывающего на сам интерпретатор, а не на ссылку, решила мою проблему.

Спасибо всем

Я просто заново создал и запустил его. Теперь, пожалуйста, дайте мне знать, правильно ли я понял: Script находится на моем диске C, и я только что проверил его с каталогом на моем диске D. Это то, что вы тестировали, не так ли? На моей машине все работало без проблем. Однако я изменил расположение своего PowerShell в ссылке: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

Max 05.02.2023 18:12

Является ли общий диск D под той же учетной записью и с тем же уровнем привилегий?

js2010 05.02.2023 18:18

@Max Да, вы правильно проверили. Поскольку вы тестировали с помощью powershell.exe (PS v5.x), а я тестировал с помощью pwsh.exe (PS v7.x), я снова попробовал его с powershell.exe, но все результаты были одинаковыми на моей неисправной машине. Таким образом, поведение кажется последовательным.

mrkskwsnck 05.02.2023 18:33

@ js2010 Мой диск D: — это физический вращающийся жесткий диск.

mrkskwsnck 05.02.2023 18:35

Re update #4: Отличная находка! Возможно ли, что PowerShell установлен через Microsoft Store на проблемную машину?

zett42 11.02.2023 15:25

@mrkskwsnck Я думаю, проблема здесь в том, что режим la указывает на следующие битовые поля: l - Reparse point, symlink, ... и a - archive, поэтому la будет означать, что эти «файлы» на самом деле являются архивами символических ссылок или архивами точек повторной обработки, которые совершенно бесполезны для пользователя. Похоже, что обновление до Win11 не помогло некоторым файлам, и теперь они указывают на новые местоположения или что-то в этом роде.

Max 11.02.2023 15:43

Это действительно сложная вещь, которую вы нашли. Возможно, вы можете написать свое обновление 4 в качестве ответа, принять его и закрыть ветку? таким образом другие люди могут найти решение быстрее. И, возможно, измените заголовок, чтобы указать на реальную проблему со ссылками для отправки.

kwasmich 12.02.2023 09:56

@zett42 zett42 Я больше не могу сказать, был ли PowerShell установлен из Магазина Microsoft или через winget. Определенно не для загрузки старого доброго установщика EXE.

mrkskwsnck 13.02.2023 13:28
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
83
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не думаю, что вы должны передавать пути к файлам с помощью -recurse переключаться на Get-ChildItem. По крайней мере, я получаю странное поведение в том, что каталог, содержащий файл, перечисляется, даже если я передаю только один файл через меню SendTo. Кроме того, скрипт корректно получает все пути, независимо от пробелов и расположения на дисках.

Итак, чтобы убедиться, что Get-ChildItem ведет себя так, как ожидалось, сначала переберите пути, полученные в качестве аргументов, и проверьте, какой из них на самом деле является каталогом. Кроме того, не забудьте вызвать Get-ChildItem с параметром -File, если вы хотите только перебирать файлы.

param (
    [Parameter(ValueFromRemainingArguments=$true)]
    $FilePath
)

foreach( $path in $FilePath ) {

    if ( (Test-Path $path -PathType Container) ) {
        "Is directory: $path"
        Get-ChildItem -Recurse -File -Path $path | ForEach-Object {
            $_
        }
    }
    else {
        "Is file: $path"
    }
}

Pause

Обратите внимание, что я удалил Write-Host, потому что он не нужен. Неназначенные выражения рассматриваются PowerShell как неявные выходные данные.


Хотя для лучшей структуры я бы обернул код перечисления файлов в функцию.

param (
    [Parameter(ValueFromRemainingArguments=$true)]
    $FilePath
)

# A function that outputs only file paths, regardless if file
# paths or directory paths are passed. Directories are processed recursively.
Function EnumerateFilesRecursive( [string[]] $FileOrDirectoryPaths ) {

    foreach( $path in $FileOrDirectoryPaths ) {

        if ( (Test-Path $path -PathType Container) ) {
            Get-ChildItem -Recurse -File -Path $path | ForEach-Object Fullname
        }
        else {
            $path
        }
    }
}

# Call the function and process its output (file paths)
EnumerateFilesRecursive $FilePath | ForEach-Object {
    "Processing file: $_"
}

pause

Спасибо за ваши тестовые сценарии. Я написал обновление 3 в своем первоначальном вопросе со странным результатом.

mrkskwsnck 10.02.2023 17:28

@mrkskwsnck Это действительно очень странно. Создали ли вы таким же образом ярлык SendTo на проблемной машине? Для дальнейшей диагностики проблемы, можете ли вы скопировать командную строку, полученную pwsh.exe, с помощью диспетчера задач?

zett42 10.02.2023 17:35

Еще раз спасибо @zett42 Я решил свою проблему. Пожалуйста, прочитайте мое обновление номер 4 для деталей.

mrkskwsnck 11.02.2023 15:17
Ответ принят как подходящий

Здесь я отвечаю на свой вопрос, основанный на /UPDATE 4 (см. выше) подобного.

Обратите внимание на то, на какой EXE-файл интерпретатора PowerShell указывает ваш ярлык «Отправить» (находится внутри shell:SendTo). Является ли это подлинным EXE-файлом (например, pwsh.exe для версии 7) или символической ссылкой на него? Оказывается, отправка файловых аргументов в символическую ссылку вообще не обрабатывает пути, содержащие пробелы!

Символическую ссылку можно узнать по ее размеру 0 байт в проводнике Windows или по стрелке вправо ->, напечатанной командой ls в терминале PowerShell.

PowerShell 7.3.2
PS C:\Users\user\AppData\Local\Microsoft\WindowsApps> ls

    Directory: C:\Users\user\AppData\Local\Microsoft\WindowsApps

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          10.01.2023    18:44                Backup
d----          25.01.2023    10:57                Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
d----          20.12.2022    11:50                Microsoft.MicrosoftEdge_8wekyb3d8bbwe
d----          28.01.2023    23:08                Microsoft.PowerShell_8wekyb3d8bbwe
d----          26.01.2023    17:18                Microsoft.SkypeApp_kzf8qxf38zg5c
d----          28.01.2023    14:19                Microsoft.WindowsTerminal_8wekyb3d8bbwe
d----          10.02.2023    10:50                Microsoft.XboxGamingOverlay_8wekyb3d8bbwe
d----          03.02.2023    15:55                SpotifyAB.SpotifyMusic_zpdnekdrzrea0
la---          10.02.2023    10:50              0 GameBarElevatedFT_Alias.exe ->
la---          20.12.2022    11:50              0 MicrosoftEdge.exe ->
la---          28.01.2023    23:08              0 pwsh.exe ->
la---          25.01.2023    10:57              0 python.exe ->
la---          25.01.2023    10:57              0 python3.exe ->
la---          26.01.2023    17:18              0 Skype.exe ->
la---          03.02.2023    15:55              0 Spotify.exe ->
la---          25.01.2023    10:57              0 WindowsPackageManagerServer.exe ->
la---          25.01.2023    10:57              0 winget.exe ->
la---          28.01.2023    14:19              0 wt.exe ->

PS C:\Users\user\AppData\Local\Microsoft\WindowsApps>

Другие вопросы по теме