Link to home
Start Free TrialLog in
Avatar of synetron
synetronFlag for United States of America

asked on

Tweak an unzip script

When i run:
function Extract-Zip
{
	param([string]$zipfilename, [string] $destination)

	if(test-path($zipfilename))
	{	
		$shellApplication = new-object -com shell.application
		$zipPackage = $shellApplication.NameSpace($zipfilename)
		$destinationFolder = $shellApplication.NameSpace($destination)
		$destinationFolder.CopyHere($zipPackage.Items())
	}
}


#find all MAC files in the immediate directory
dir "C:\script\test\ftp\MAC*.zip" | foreach-object{

	extract-zip $_ c:\script\test\Master\MAC
	extract-zip $_ c:\script\test\Working\MAC

}

Move-Item "C:\script\test\ftp\MAC*" "C:\script\test\ftp\archive"

#find all MAH files in the immediate directory
dir "C:\script\test\ftp\MAH*.zip" | foreach-object{

	extract-zip $_ c:\script\test\Master\MAH\
	extract-zip $_ c:\script\test\Working\MAH\

}

Move-Item "C:\script\test\ftp\MAH*" "C:\script\test\ftp\archive"

#find all MAM files in the immediate directory
dir "C:\script\test\ftp\MAM*.zip" | foreach-object{

	extract-zip $_ c:\script\test\Master\MAM
	extract-zip $_ c:\script\test\Working\MAM

}

Move-Item "C:\script\test\ftp\MAM*" "C:\script\test\ftp\archive"

Open in new window


This works flawlessly.

I need however to arrange the destination directory to receive the extracted files in a unique sub-directory.

Ideally i would arranged them based on characters 5 through 12 of the original zip file name, if however i can only use the original zip file name without the .zip extention as the name of my directory i would consider it a success.

For any verterans out there looking to help me out i obviously get a
You cannot call a method on a null-valued expression.
At C:\script\test\unzip_archive1.ps1:10 char:30
+         $destinationFolder.CopyHere <<<< ($zipPackage.Items())
    + CategoryInfo          : InvalidOperation: (CopyHere:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
when i add "\$_" to the end of my extraction path.

as for any rookies like myself, this isn't the solution... :-(

also, should I
Move-Item $_ "C:\script\test\ftp\archive" within my foreach-object{
} to make it cleaner or just wait until all have completed before moving all such items to the archive? and can I Move-Item $_ within the foreach statement?

Thanks
Avatar of Meir Rivkin
Meir Rivkin
Flag of Israel image

try add $_.BaseName to the end of my extraction path.
also on lines 18-19 you call the extract-zip with the same file but different destination.
i think u should simply copy the files to the other destination once the first call to extract-zip is done.
Avatar of synetron

ASKER

Thank you for the reply. I added $_.BaseName to the end of the extraction paths and it returned:

You cannot call a method on a null-valued expression.
At C:\script\test\test.ps1:10 char:30
+         $destinationFolder.CopyHere <<<< ($zipPackage.Items())
    + CategoryInfo          : InvalidOperation: (CopyHere:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

As for lines 18-19 and moving to the other destination after the first extraction is complete, this doesn't fit the current need to archive for backup and have two working extracted copies ready for the following morning.
try create the destination folder before doing the actual unzipping inside the function.
function Extract-Zip
{
	param([string]$zipfilename, [string] $destination)

	if(test-path($zipfilename))
	{	md $destination #creates destination folder
		$shellApplication = new-object -com shell.application
		$zipPackage = $shellApplication.NameSpace($zipfilename)
		$destinationFolder = $shellApplication.NameSpace($destination)
		$destinationFolder.CopyHere($zipPackage.Items())
	}
}

Open in new window

Well that was interesting...

New-Item : The given path's format is not supported.
At line:38 char:24
+         $scriptCmd = {& <<<<  $wrappedCmd -Type Directory @PSBoundParameters }
    + CategoryInfo          : InvalidOperation: (C:\script\test\...py.zip.BaseName:String) [New-Item], NotSupportedExc
   eption
    + FullyQualifiedErrorId : ItemExistsNotSupportedError,Microsoft.PowerShell.Commands.NewItemCommand

You cannot call a method on a null-valued expression.
At C:\script\test\mema.ps1:11 char:30
+         $destinationFolder.CopyHere <<<< ($zipPackage.Items())
    + CategoryInfo          : InvalidOperation: (CopyHere:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
unless I'm mistaken, the inability to designate the destination lies in trying to use a variable (pardon the ignorance in accurate terms) like $_ or $_.BaseName in the extraction section (lines 18, 19, for example) since the extract-zip is a call to the function and thus the $_ which typically would reference the file being currently processed (by the for each section) has a null value as it's an argument being processed within the function...

If that is correct, then i should create the directory before i extract the data to it within the ForEach with something like "MD <PATH TO DIRECTORY>\$_.BaseName" but still have no way to call upon the name of the subdirectory i just created within my extract-zip function as this will be unique to each file being extracted.  

Could this possibly be resolved by listing the currently processed file name as a string
$a = $_.BaseName
who's value can be accessed both in my for each statements and passed to the function? could a variable be set to represent this value... if so, how? could this type of statement be made before the function and stored (thinking object oriented here... ) outside of the function and outside of the ForEach sections thus being available to both or would the $_ only work when it's given life by the ForEach section of the script?

I can't help but feel I'm so close, but my limitation in experience (this is my first ever PS script) is killing me.
i probably missing something here cause if the initial script is working, it should work if only thing is to change the destination folder based on the file name.

can u put write-host $destination inside the function?
also use test-path before creating the folder:
function Extract-Zip
{
	param([string]$zipfilename, [string] $destination)
write-host $destination
	if(test-path($zipfilename))
	{	
if((Test-Path $destination) -eq $False){
md $destination #creates destination folder
}
		$shellApplication = new-object -com shell.application
		$zipPackage = $shellApplication.NameSpace($zipfilename)
		$destinationFolder = $shellApplication.NameSpace($destination)
		$destinationFolder.CopyHere($zipPackage.Items())
	}
}
                                            

Open in new window

Thank you sedgwick for your continued thoughts on the matter. I attempted this in various methods with no luck.
1. the destination directory does not create
2. the files still extract to the destination path without a unique sub-directory for each

mostly, this is what I'm seeing now:
c:\script\test\Master\MAH\C:\script\test\ftp\MAH.20120213-02.20.01 - Copy.zip
Test-Path : The given path's format is not supported.
At C:\script\test\mema.ps1:7 char:14
+ if((Test-Path <<<<  $destination) -eq $False){
    + CategoryInfo          : InvalidOperation: (C:\script\test\...0.01 - Copy.zip:String) [Test-Path], NotSupportedEx
   ception
    + FullyQualifiedErrorId : ItemExistsNotSupportedError,Microsoft.PowerShell.Commands.TestPathCommand

New-Item : The given path's format is not supported.
At line:38 char:24
+         $scriptCmd = {& <<<<  $wrappedCmd -Type Directory @PSBoundParameters }
    + CategoryInfo          : InvalidOperation: (C:\script\test\...0.01 - Copy.zip:String) [New-Item], NotSupportedExc
   eption
    + FullyQualifiedErrorId : ItemExistsNotSupportedError,Microsoft.PowerShell.Commands.NewItemCommand

You cannot call a method on a null-valued expression.
At C:\script\test\mema.ps1:13 char:30
+         $destinationFolder.CopyHere <<<< ($zipPackage.Items())
    + CategoryInfo          : InvalidOperation: (CopyHere:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
the concatenation of the $destination folder is wrong.
that's why u get:
c:\script\test\Master\MAH\C:\script\test\ftp\MAH.20120213-02.20.01 - Copy.zip

instead of:
c:\script\test\Master\MAH\MAH.20120213-02.20.01 - Copy.zip

please post the whole script again, i'll test it on my machine and make it work.
sedgwick, I can't thank you enough for your help, i believe I've had a breakthrough. I'll repost the script that I now have working...

function Extract-Zip
{
	param([string]$zipfilename, [string] $destination)

	if(test-path($zipfilename))
	{	
		$shellApplication = new-object -com shell.application
		$zipPackage = $shellApplication.NameSpace($zipfilename)
		$destinationFolder = $shellApplication.NameSpace($destination)
		$destinationFolder.CopyHere($zipPackage.Items())
	}
}


#find all MAC files in the immediate directory
dir "C:\script\test\ftp\MAC*.zip" | foreach-object{

#create directory	
	$id = $_.Name.SubString(4,8); 
    if(-not (Test-Path c:\script\test\Master\MAC\$id)) {mkdir c:\script\test\Master\MAC\$id}; 

#extract	
	extract-zip $_ c:\script\test\Master\MAC\$id
	
#create directory
    if(-not (Test-Path c:\script\test\Working\MAC\$id)) {mkdir c:\script\test\Working\MAC\$id}; 

#extract
	extract-zip $_ c:\script\test\Working\MAC\$id

}

#clean up to avoid duplicate work at next run
	Move-Item "C:\script\test\ftp\MAC*" "C:\script\test\ftp\archive"



#find all MAH files in the immediate directory
dir "C:\script\test\ftp\MAH*.zip" | foreach-object{

	$id = $_.Name.SubString(4,8); 
    if(-not (Test-Path c:\script\test\Master\MAH\$id)) {mkdir c:\script\test\Master\MAH\$id}; 

	extract-zip $_ c:\script\test\Master\MAH\

    if(-not (Test-Path c:\script\test\Working\MAH\$id)) {mkdir c:\script\test\Working\MAH\$id}; 

	extract-zip $_ c:\script\test\Working\MAH\

}

Move-Item "C:\script\test\ftp\MAH*" "C:\script\test\ftp\archive"




#find all MAM files in the immediate directory
dir "C:\script\test\ftp\MAM*.zip" | foreach-object{

	$id = $_.Name.SubString(4,8); 
    if(-not (Test-Path c:\script\test\Master\MAM\$id)) {mkdir c:\script\test\Master\MAM\$id}; 

	extract-zip $_ c:\script\test\Master\MAM\$id

    if(-not (Test-Path c:\script\test\Working\MAM\$id)) {mkdir c:\script\test\Working\MAM\$id}; 

	extract-zip $_ c:\script\test\Working\MAM\$id

}

# Move-Item "C:\script\test\ftp\MAM*" "C:\script\test\ftp\archive"

Open in new window


you got me to thinking about the destination etc. so i started looking at some of the attempts early on in this project when i was trying to create the file structure then populate with 7zip command line interface... an attempt that failed miserably, and the light bulb went off.
Do you know of a way to ensure a silent copy? ie overwrite existing file if copying a newer one?

this will be run through scheduler, so I must avoid a need for interaction.
ASKER CERTIFIED SOLUTION
Avatar of Meir Rivkin
Meir Rivkin
Flag of Israel image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thank you so much, and as I'm not using anything but running in powershell to "debug" i will certainly use your suggestion moving forward.

This is so much cleaner!! VERY much appreciated!
here's the script as optimal as possible:

function Extract-Zip
{
	param([string]$zipfilename, [string] $destination)

	if(test-path($zipfilename))
	{	
		$shellApplication = new-object -com shell.application
		$zipPackage = $shellApplication.NameSpace($zipfilename)
		$destinationFolder = $shellApplication.NameSpace($destination)
		$destinationFolder.CopyHere($zipPackage.Items())
	}
}

cls

$archivefolder = "C:\script\test\ftp\archive"

if(-not (Test-Path $archivefolder)){
	md $archivefolder 
}
		
$postfixes = "MAC", "MAH", "MAM"

$postfixes | foreach {
	$postfix = $_
	$masterfolder = Join-Path "c:\script\test\Master\" $postfix
	$workingfolder = Join-Path "c:\script\test\Working\" $postfix
	$dirfolder = "C:\script\test\ftp\$postfix*.zip"
	dir $dirfolder | foreach-object{
		$filename = $_.BaseName
		$srcfile = $_
		
		$newdest = Join-Path $masterfolder $filename
		if(-not (Test-Path ($newdest))){
			#create directory
			md $newdest
		}
		
		extract-zip $srcfile $newdest
		
		$newdest = Join-Path $workingfolder $filename
		if(-not (Test-Path ($newdest))){
			#create directory
			md $newdest
		}
		
		extract-zip $srcfile $newdest 
		
		Move-Item $srcfile $archivefolder
	}
}

Open in new window

excellent,
and I assume that adding .SubString (4,8) to the end of
$newdest = Join-Path $masterfolder $filename
as in
$newdest = Join-Path $masterfolder $filename.SubString( 4,8)
works as well as
$newdest = Join-Path $_ $filename.SubString (4,8)
from your previous post?

and by the way, I wish I had been using PowerGUI the whole time, instead of notepad. Great tip.
u don't need .SubString (4,8) cause $_.BaseName gives u only the file name without the extension.

so if the file is: c:\temp\jonny.zip
then  $_.BaseName equals jonny.
and that is what u wish to add to the $destination folder, and i use join-path to do so.

good luck.
In doing so i actually get a cleaner folder name.  For instance my zip file is actually quite long in name...
MAC.20120213-02.20.01
so i was using SubString (4,8) to extract only the yyyymmdd portion of the name so that the result would be <parent>\MAC\20120213 instead of <parent>\MAC\MAC.20120213-02.20.01

although both results are good enough
and thanks again!