PowerShell выполняет внешнюю команду, не принимая параметр
Я выполняю следующий код, пытаясь выполнить команду 7z.exe, чтобы разархивировать файлы.
$ dir содержит пользовательский ввод пути к zip-файлу, который, конечно, МОЖЕТ содержать пробелы! и $dir\temp2 ниже - это каталог, который я ранее создал.
Get-ChildItem -path $dir -Filter *.zip |
ForEach-Object {
$zip_path = """" + $dir + "\" + $_.name + """"
$output = " -o""$dir\temp2"""
&7z e $zip_path $output
}
когда я выполняю его, я получаю следующее от 7z.exe
7-Zip [64] 9.20 Copyright (c) 1999-2010 Игорь Павлов 2010-11-18
Обработка архива: C:\test dir\test.zip
Нет файлов для обработки
Файлы: 0 Размер: 0 Сжатый: 50219965
если я затем скопирую значение из $zip_path и $output, чтобы сформировать мою собственную строку cmd, это РАБОТАЕТ!
например: 7z e "c: \ test dir \ test.zip" -o "c: \ test output"
ТЕПЕРЬ, я могу воспроизвести то же самое сообщение "нет файлов для обработки", которое я получаю, когда выполняю в powershell, используя следующий cmd в cli.
7z e "c: \ test dir \ test.zip" o "c: \ test output"
Итак, похоже, что powershell удаляет тире-символ из моего параметра -o. и ДА, это должно быть -o "C: \ test output", а не -o "c: \ test output" с 7z.exe, между параметром -o и его значением нет пробела.
я в тупике, я делаю что-то не так или я должен делать это по-другому? любые предложения или помощь спасибо
3 ответа
Я никогда не смогу заставить Invoke-Expression (alias = &) работать правильно, поэтому я научился использовать объект процесса
$7ZExe = (Get-Command -CommandType Application -Name 7z )
$7ZArgs = @(
('-o"{0}\{1}"' -f $dir, $_.Name),
('"{0}\{1}"' -f $dir, 'temp2')
)
[Diagnostics.ProcessStartInfo]$7Zpsi = New-Object -TypeName:System.Diagnostics.ProcessStartInfo -Property:@{
CreateNoWindow = $false;
UseShellExecute = $false;
Filename = $7ZExe.Path;
Arguments = $7ZArgs;
WindowStyle = 'Hidden';
RedirectStandardOutput = $true
RedirectStandardError = $true
WorkingDirectory = $(Get-Location).Path
}
$proc = [System.Diagnostics.Process]::Start($7zpsi)
$7ZOut = $proc.StandardOutput
$7ZErr = $proc.StandardError
$proc.WaitForExit()
Я был в состоянии продублировать точную проблему и пробовал многочисленные комбинации, избегая -o
переключаться и выходить из кавычек "
и что "нет. Но в качестве одного из ответов упоминается SysInternals, и я использовал ProcessMonitor, чтобы выяснить формат, который он передает 7z.exe. Вещи, которые работают в простой командной строке, не работают внутри powershell таким же образом. Например, если бы я попытался сконструировать параметры внутри PowerShell точно так же, как cmdline, он потерпел бы неудачу. т.е. -o"C:\scripts\so\new folder"
не работает Но если вы включите -o
переключаться внутри кавычек, затем PowerShell передает строку "-oC:\scripts\so\new folder"
который 7z.exe
рад принять. Итак, я узнал, что 7z.exe будет принимать оба формата, такие как
"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" -o"C:\scripts\so\new folder"
а также
"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" "-oC:\scripts\so\new folder"
И оба эти примера содержат пробелы в них.
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
$dir = "C:\scripts\so"
$output = "$dir\new folder"
Get-ChildItem -path $dir -Filter *.zip | % {
[array]$marguments = "e",$_.FullName,"-o$output";
& $pathtoexe $marguments
}
Другой подход в PS V3 состоит в том, чтобы избежать функции синтаксического анализа powershell. Ты можешь использовать --%
Команда, чтобы сказать PowerShell, чтобы прекратить синтаксический анализ больше команд, как это.
$zipfile = "C:\scripts\so\newfolder.zip"
$destinationfolder = "C:\scripts\so\New Folder"
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
& $pathtoexe --% e "C:\scripts\so\newfolder.zip" -o"C:\scripts\so\new folder"
С помощью --%
Синтаксис вы вводите команды так же, как вы вводите их в командной строке. Я проверил эту логику, и она извлекает файлы в папку назначения.
Чтобы узнать больше о --%
проверять PS> help about_parsing
Проблема с этим подходом после --%
невозможно включить переменную. Решение этой проблемы состоит в том, чтобы просто включить --%
в качестве другой строковой переменной и передайте ее следующим образом. И этот подход похож на подход командной строки, который изначально не работал.
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
$dir = "C:\scripts\so"
$output = "$dir\new folder"
Get-ChildItem -path $dir -Filter *.zip | % {
$zipfile = $_.FullName;
[string]$formated = [System.String]::Concat("e ", """$zipfile"""," -o""$output""");
[string]$stopparser = '--%';
& $pathtoexe $stopparser $formated;
}
Используя отличный Process Explorer из пакета Windows Sysinternals, я смог наблюдать очень интересное поведение. Я немного упростил вашу командную строку, как показано ниже:
dir -Path $dir -Filter *.zip |
select FullName |
% { & 7za.exe e $_ "-o$dir\tmp" }
На самом деле это вызывало следующую командную строку в соответствии с Process Explorer:
C:\temp\7za.exe @{FullName="C:\temp\test.zip"} -oC:\temp\test
Говоря PowerShell о расширении свойства FullName, он вытесняет его из хэш-карты и обрабатывает как обычную строку, с которой может справиться 7-zip:
dir -Path $dir -Filter *.zip |
select -ExpandProperty FullName |
% { & 7za.exe e $_ "-o$dir\tmp" }
Могут быть и другие проблемы, такие как работа с пробелами в именах файлов, которые я действительно не рассматривал или не учитывал, но я подумал, что стоит добавить примечание, что PowerShell (в данном случае v2) не совсем передает параметры, как вы можно ожидать.