Как объединить команды в специальную командную строку PowerShell 4?

Обычно команды PowerShell могут быть скованы точкой с запятой. После этого открывается 2 блокнота:

PS> notepad; notepad

Вы также можете связать более сложное утверждение:

PS> Add-Type -AssemblyName System.IO.Compression; `
> $src = "C:\aFolder"; $zip="C:\my.zip"; `
> [io.compression.zipfile]::CreateFromDirectory($src, $zip)

Команды Chained PowerShell также могут быть вызваны из командной строки CMD:

C:\> powershell notepad; notepad

В этом сообщении описывается метод создания приглашения PowerShell.Net 4.0, даже если .Net 2.0 является активной структурой вашей ОС. Вы создаете .cmd script и запускаете его. Теперь вы находитесь в среде .Net 4.0.

В этом приглашении 4.0 работает цепочка:

C:\> ps4.cmd
PS> notepad; notepad

А также работает со стандартной подсказки CMD:

C:\> ps4 notepad; notepad

В этом сообщении описывается способ сделать Add-Type явному пути (необходимо ссылаться на сборки 4.0 из командной строки ps4):

Add-Type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.IO.Compression.FileSystem\v4.0_4.0.0.0__b77a5c561934e089\System.IO.Compression.FileSystem.dll"

Это работает даже при приковании в командной строке ps4:

C:\> ps4
PS> Add-Type -Path "C:\x\System.IO.Compression.FileSystem.dll"; `
> $src = "C:\x\xl"; $zip="C:\x\xl.zip"; `
> [io.compression.zipfile]::CreateFromDirectory($src, $zip)

Проблема: цепочка вышеприведенного утверждения терпит неудачу, когда ps4 запущен в стандартной командной строке (полная ошибка внизу сообщения):

C:\> ps4 Add-Type -Path "C:\x\System.IO.Compression.FileSystem.dll"; $src = "C:\x\xl"; $zip="C:\x\xl.zip"; [io.compression.zipfile]::CreateFromDirectory($src, $zip)

Однако все эти методы работают. Зачем? Как я могу сделать эту работу?

The term 'C:\x\xl' is not recognized as the name of a cmdlet, function, script
file, or operable program. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again.
At line:1 char:73
+ Add-Type -Path C:\x\System.IO.Compression.FileSystem.dll; $src = C:\x\xl <<<< ; $zip=C:\x\xl.zip;
[io.compression.zipfile]::CreateFromDirectory($src, $zip)
    + CategoryInfo          : ObjectNotFound: (C:\x\xl:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

The term 'C:\x\xl.zip' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path
was included, verify that the path is correct and try again.
At line:1 char:91
+ Add-Type -Path C:\x\System.IO.Compression.FileSystem.dll; $src = C:\x\xl; $zip=C:\x\xl.zip <<<< ;
[io.compression.zipfile]::CreateFromDirectory($src, $zip)
    + CategoryInfo          : ObjectNotFound: (C:\x\xl.zip:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Exception calling "CreateFromDirectory" with "2" argument(s): "The path is not
of a legal form."
At line:1 char:138
+ Add-Type -Path C:\x\System.IO.Compression.FileSystem.dll; $src = C:\x\xl; $zip=C:\x\xl.zip;
[io.compression.zipfile]::CreateFromDirectory <<<< ($src, $zip)
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

Ответ 1

Двойные кавычки вокруг строк удаляются, когда команды с последовательным соединением передаются на powershell.exe. Add-Type не влияет на это, поскольку путь не содержит пробелов, поэтому он не требует кавычек:

Add-Type -Path C:\x\System.IO.Compression.FileSystem.dll   # <- this works

Однако в операции присваивания PowerShell требует, чтобы строки были в кавычках, иначе он будет интерпретировать строку как команду и попытаться ее выполнить:

$src = C:\x\xl  # <- this would try to run a (non-existent) command 'C:\x\xl'
                #    and assign its output to the variable instead of assigning
                #    the string "C:\x\xl" to the variable

Вот что вызывает первые две ошибки, которые вы наблюдали. Третья ошибка - это последующая ошибка, поскольку две переменные пути не были правильно инициализированы.

Вы должны избегать этого поведения, избегая двойных кавычек:

ps4 Add-Type -Path \"C:\x\System.IO.Compression.FileSystem.dll\"; $src = \"C:\x\xl\"; $zip=\"C:\x\xl.zip\"; [io.compression.zipfile]::CreateFromDirectory($src, $zip)

или заменяя их одинарными кавычками (потому что последние не распознаются CMD как цитирующие символы и поэтому передаются как есть в PowerShell, который распознает их как символы цитирования):

ps4 Add-Type -Path 'C:\x\System.IO.Compression.FileSystem.dll'; $src = 'C:\x\xl'; $zip='C:\x\xl.zip'; [io.compression.zipfile]::CreateFromDirectory($src, $zip)

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

#requires -version 4
[CmdletBinding()]
Param(
  [Parameter(Mandatory=$true)]
  [string]$Path,
  [Parameter(Mandatory=$true)]
  [string]$Zipfile,
)

Add-Type -Assembly 'System.IO.Compression.FileSystem' | Out-Null
[IO.Compression.ZipFile]::CreateFromDirectory($Path, $Zipfile)

script будет запускаться следующим образом:

powershell.exe -File zip.ps1 -Path "C:\x\xl" -Zipfile "C:\x\xl.zip"

Если вы измените политику выполнения на RemoteSigned или Unrestricted, а также измените обработчик по умолчанию для файлов .ps1, вы можете даже запустить script следующим образом:

zip.ps1 -Path "C:\x\xl" -Zipfile "C:\x\xl.zip"