Reputation: 730
I am executing the following code attempting to execute the 7z.exe command to unzip files.
$dir contains the user input of the path to the zip file which can contain spaces of course! And $dir\temp2 below is a directory that I previously created.
Get-ChildItem -path $dir -Filter *.zip |
ForEach-Object {
$zip_path = """" + $dir + "\" + $_.name + """"
$output = " -o""$dir\temp2"""
&7z e $zip_path $output
}
When I execute it I get the following from 7z.exe:
7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
Processing archive: C:\test dir\test.zip
No files to process
Files: 0
Size: 0
Compressed: 50219965
If I then copy the value from $zip_path and $output to form my own cmd line it works!
For example:
7z e "c:\test dir\test.zip" -o"c:\test output"
Now, I can reproduce the same message "no files to process" I get when I execute within PowerShell by using the following cmd in cli.
7z e "c:\test dir\test.zip" o"c:\test output"
So, it seems that PowerShell is removing the dash char from my -o option. And yes, it needs to be -o"C:\test output" and not -o "c:\test output" with 7z.exe there is no space between the -o parameter and its value.
I am stumped. Am I doing something wrong or should I be doing this a different way?
Upvotes: 3
Views: 2597
Reputation: 24370
Using the excellent Process Explorer from the Windows Sysinternals suite I was able to observe some very interesting behavior. I simplified your command line a little as seen below:
dir -Path $dir -Filter *.zip |
select FullName |
% { & 7za.exe e $_ "-o$dir\tmp" }
This was actually invoking the following command line according to Process Explorer:
C:\temp\7za.exe @{FullName="C:\temp\test.zip"} -oC:\temp\test
Telling PowerShell to expand the FullName
property forces it out of the hashmap and treats it as a regular string which 7-Zip can deal with:
dir -Path $dir -Filter *.zip |
select -ExpandProperty FullName |
% { & 7za.exe e $_ "-o$dir\tmp" }
There may still be other issues like dealing with spaces in file names that I really didn't consider or account for, but I thought it was worth adding a note that PowerShell (v2 in this case) wasn't quite passing the parameters as you might expect.
Upvotes: 3
Reputation: 9854
I was able to duplicate the exact issue and tried numerous combinations escaping the -o
switch and escaping quotes "
and what not.
But as one answer mentioned Sysinternals, and I used Process Monitor to find out the format it was passing to 7z.exe. Things that work on a plain commandline doesn't work inside PowerShell the same way.
For example, if I tried to construct parameters inside PowerShell just like cmdline it would fail. I.e., -o"C:\scripts\so\new folder"
doesn't work. But if you include the -o
switch inside quotes then PowerShell passes the string "-oC:\scripts\so\new folder"
which 7z.exe
is happy to accept. So I learned that 7z.exe would accept both the formats such as
"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" -o"C:\scripts\so\new folder"
and
"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" "-oC:\scripts\so\new folder"
And both examples contain spaces in them.
[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
}
Another approach in PowerShell V3 is to escape the PowerShell parsing feature. You can use the --%
command to tell PowerShell to stop parsing any more commands like this.
$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"
Using the --%
syntax, you type commands just like you would type them on the command line. I tested this logic, and it extracts files to the destination folder.
To learn more about --%
, check PS> help about_parsing
.
The issue with this approach is after --%
it is not possible to include a variable. The solution to this issue is to just include the --%
as another string variable and pass it like this. And this approach is similar to the commandline approach which wasn't working originally.
[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]$formatted = [System.String]::Concat("e ", """$zipfile"""," -o""$output""");
[string]$stopparser = '--%';
& $pathtoexe $stopparser $formatted;
}
Upvotes: 3
Reputation: 7638
I can never get Invoke-Expression (alias = &) to work right either, so I learned how to use a process object
$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()
Upvotes: 3