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) не совсем передает параметры, как вы можно ожидать.

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