Creating Zip files using PowerShell - powershell

I have these below files at a location C:\Desktop\Mobile.
Apple_iphone6.dat
Apple_iphone7.dat
Samsung_edge7.dat
Samsung_galaxy.dat
Sony_experia.dat
Sony_M2.dat
I need to create a script that writes the similar files into a single zip. So files Apple_iphone6.dat and Apple_iphone7.dat must be into single zip.
So the final zip files created would be:
Apple_Files_Timestamp.zip
Samsung_Files_Timestamp.zip
Sony_Files_Timestamp.zip
I tried this
Get-ChildItem C:\Desktop\Mobile -Recurse -File -Include *.dat | Where-Object { $_.LastWriteTime -lt $date } | Compress-Archive -DestinationPath C:\Desktop\Mobile
But it gives me error 'Compress-Archive' is not recognized as the name of a cmdlet.
How can I get this code work?

You have two problems, I will try to summarize both of them.
1. Compress files
In order to use Compress-Archive command you need to have PowerShell 5 as already commented by #LotPings. You can:
run your script on Windows 10 machine, or Server 2016 which are coming with v5
download and install PoSh 5, see details on MSDN
If you cannot do either of those, you can
install some module from PowerShell gallery that provides similar functionality via 7-zip tool. Search resultes are here. Download and check those modules before use!
use .NET 4.5 class, check answer here on Stack Overflow
2. Group files
Once you group files, you can easily pipe them to compressing command, similar as you already tried. Proper grouping would be achieved with something like this:
$Files = Get-ChildItem 'C:\Desktop\Mobile'
$Groups = $Files | ForEach-Object {($_.Name).split('_')[0]} | Select-Object -Unique
foreach ($Group in $Groups) {
$Files | where Name -Match "^$Group" | Compress-Archive "C:\Desktop\Mobile\$Group.7z"
}

Pre Powershell v5 you can use this. No additional downloads needed.
$FullName = "Path\FileName"
$Name = CompressedFileName
$ZipFile = "Path\ZipFileName"
$Zip = [System.IO.Compression.ZipFile]::Open($ZipFile,'Update')
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($Zip,$FullName,$Name,"optimal")
$Zip.Dispose()

With Powershell 2.0 you can't use Compress-Archive, you need download the original terminal executables to zip and unzip files from here.
You can use:
zip <path> <zip_name> -i <pattern_files>
In your example:
zip "C:\Desktop\Mobile" Apple_Files_Timestamp.zip -i Apple*.dat
zip "C:\Desktop\Mobile" Samsung_Files_Timestamp.zip -i Samsung*.dat
zip "C:\Desktop\Mobile" Sony_Files_Timestamp.zip -i Sony*.dat
If you need use adittional zip options, visit zip manual.

The following script does the grouping,
the zipping command depends on your chosen zipper.
$TimeStamp = Get-Date -Format "yyyyMMddhhmmss"
Get-ChildItem *.dat|
Group-Object {($_.Name).split('_')[0]}|
ForEach-Object {
$Make = $_.Name
Foreach($File in $_.Group){
"{0,20} --> {1}_Files_{2}.zip" -f $File.Name,$Make,$TimeStamp
}
}
Sample output:
> .\SO_44030884.ps1
Samsung_edge7.dat --> Samsung_Files_20170517081753.zip
Samsung_galaxy.dat --> Samsung_Files_20170517081753.zip
Apple_iphone6.dat --> Apple_Files_20170517081753.zip
Apple_iphone7.dat --> Apple_Files_20170517081753.zip
Sony_M2.dat --> Sony_Files_20170517081753.zip
Sony_experia.dat --> Sony_Files_20170517081753.zip
This link might help Module to Synchronously Zip and Unzip using PowerShell 2.0

Related

PowerShell partial zip file name

Currently I have a code that extracts a zip file that is uploaded nightly and it has the name CallRecording_1-000-XXXXXXXX the X's represent the date and time that the zip file was generated. What I would like to do is have a powershell script that looks for the partial name. So for example it would look for just CallRecording_1-000 or CallRecording.
At the moment I have the following script:
#expand archive into folder
expand-archive ("Y:\CallRecording_1-000.zip") -destinationPath $folder
#rename zip file with yesterdays date
$yesDateName = $yesDate + ".zip"
Rename-Item "Y:\CallRecording_1-000.zip" -NewName $yesDateName
The scripts that I have found previously that use partial names seems to focus mostly on the extension rather than the name itself.
Any help would be appreciated!
It sounds like you only expect 1 zip file however I tailored this answer around the possibility of having more than 1
We are going to use Get-ChildItem to get any zip files from y:\ that match 'CallRecordings*.zip'
We then pipe these files one at a time to the ForEach-Object cmdlet where we
assign the extraction folder
unzip the file
and then rename the file.
$i is used to allow us different names for our renamed zip file in case there are more than 1 being processed.
$i = 0
Get-ChildItem -Path 'Y:\' -Filter 'CallRecording*.zip' | ForEach-Object -Process {
$extractFolder = "C:\temp\$($_.BaseName)"
$_ | Expand-Archive -DestinationPath $extractFolder
# ($? tells us if the last command completed successfully)
if ($?) {
# only rename file if Expand-Archive was successful
$_ | Rename-Item -NewName ((Get-Date).AddDays(-1).ToString('yyyyMMdd') + "_$((++$i)).zip")
}
}

Filter files with a extesion and run a command for all found files in Windows PowerShell

I want to find all the files having .zip extension in a folder (MyFiles) recursively and run the following command for each file in Windows PowerShell.
PS C:\solr-7.3.0> java -Dc=myCore1 -Dauto=yes -Ddata=files
-Drecursive=yes -jar example/exampledocs/post.jar "File fullpath goes here"
Could you help me to achieve this?
Use the Get-ChildItem cmdlet to find the relevant Zip files and then pipe the results to the ForEach-Object cmdlet to loop over the files. The $_ or $psitem variable is the current object passed through the pipeline. Then the FullName property on that object will contain the full path to each Zip file.
Get-ChildItem -Path C:\Example\Path -Filter '*.zip' -Recurse |
ForEach-Object {
& java -Dc=myCore1 -Dauto=yes -Ddata=files -Drecursive=yes -jar example/exampledocs/post.jar $_.Fullname
}

Automating MS Updates with Powershell

I am trying to Automate my Microsoft update process based on the KB/MSU packages I download. I have a WMIC csv file that I am able to install based on simple If else statement. I would like to use the list extract just the "KBXXXXX" from the downloaded files and compare it to the list of installed KBs and discover what is missing.
KB install List (WMIC output):
KB123456
KB234567
KB345678
Downloaded KB file list format $KBUpdateList:
Name
WINDOWS6.1-KB2533552-X64.MSU
WINDOWS6.1-KB2533552-X86.MSU
WINDOWS6.1-KB2539635-X64.MSU
Windows6.1-KB958488-v6001-x64.MSU
Here is what I have that is not working to pull KB number:
PowerShell script to list the MSU files under the selected folder:
$Dir = get-childitem $folder -recurse
$KBUpdateList = $Dir | where {$_.extension -eq ".msu"}
$KBUpdatenames = $KBUpdateList | format-table name
$KBNumberonly = $KBUpdateList.split("-")[1]
The split fails and I can't find a fix. I just want to return the KBXXXX number so I can run my foreach statement that follows. Thank You
This should suffice for what you are looking for.
$KBUpdatenames = get-childitem $folder -recurse -Filter "*.msu" | Select-Object -Expand Name
$KBNumberonly = $KBUpdatenames | ForEach-Object{$_.split("-")[1]}
Use Get-ChildItem to get the files of type ".msu". Using -Filter is more efficient than Where-Object in most case where you are just looking for extensions. Then we expand just the names of the files with Select-Object
As for the Format-Table in your code I will refer you to an answer about Format-Taco that I enjoy.

Rename first 20 characters of every filename in a file

I am trying to write a script in powershell to remove the first 20 characters of every MP3 filename in a folder, I have created a file 'test.ps' and inserted the powershell code below into it,
gci *.mp3 | rename-item -newname { [string]($_.name).substring(20) }
When I run this file in powershell.exe nothing happens,
Can anyone help? Thanks.
This may get you started. (There are probably much more concise ways, but this works and is readable when you need to maintain it later. :-) )
I created a folder C:\TempFiles, and created the following files in that folder:
TestFile1.txt
TestFile2.txt
TestFile3.txt
TestFile4.txt
(I created them the old-fashioned way, I'm afraid. <g>. I used
for /l %i in (1,1,4) do echo "Testing" > TestFile%i.txt
from an actual command prompt.)
I then opened PowerShell ISE from the start menu, and ran this script. It creates an array ($files), containing only the names of the files, and processes each of them:
cd \TempFiles
$files = gci -name *.txt
foreach ($file in $files) {
$thename = $file.substring(4);
rename-item -path c:\TempFiles\$file -newname $thename
}
This left the folder containing:
File1.Txt
File2.Txt
File3.Txt
File4.Txt
File5.Txt
In order to run a script from the command line, you need to change some default Windows security settings. You can find out about them by using PowerShell ISE's help file (from the menu) and searching for about_scripts or by executing help about_scripts from the ISE prompt. See the sub-section How To Run A Script in the help file (it's much easier to read).
Your code actually works. Two things...
Rename the file to test.ps1.
Run it in the folder you have your MP3 files in. Since you didn't provided a path to Get-ChildItem it will run in the current directory.
I tested your line by making a bunch of mp3 files like this -
1..30 | % { new-item -itemtype file -Name (
$_.ToString().PadLeft(30, 'A') + ".mp3" )}
I would use a more "safer" way (you'll get an error if the file name is shorter than the length in question, you are also targeting the file extension as a part of the total characters). Check if the base name of each file is greater than 21 characters (if you remove the first 20 it can be still have a name with one character long). It can fail if the directory contains a file with same name after you removed the first 20, you can develop it further on your own):
gci *.mp3 | foreach{
if($_.BaseName.Length -ge 21)
{
$ext = $_.Extension
$BaseName = $_.BaseName.Substring(20)
Rename-Item $_ -NewName "$BaseName$ext"
}
}
// delete (replace with empty char) first 20 charters in all filename witch is started with "dbo."
// powershell
Get-ChildItem C:\my_dir\dbo -Recurse -Force -Filter dbo.* | Where-Object {!$_.PSIsContainer} | Rename-Item -NewName { ($_.name).Substring(20) }

Find out whether a file is a symbolic link in PowerShell

I am having a PowerShell script which is walking a directory tree, and sometimes I have auxiliary files hardlinked there which should not be processed. Is there an easy way of finding out whether a file (that is, System.IO.FileInfo) is a hard link or not?
If not, would it be easier with symbolic links (symlinks)?
Try this:
function Test-ReparsePoint([string]$path) {
$file = Get-Item $path -Force -ea SilentlyContinue
return [bool]($file.Attributes -band [IO.FileAttributes]::ReparsePoint)
}
It is a pretty minimal implementation, but it should do the trick. Note that this doesn't distinguish between a hard link and a symbolic link. Underneath, they both just take advantage of NTFS reparse points, IIRC.
If you have Powershell 5+ the following one-liner recursively lists all file hardlinks, directory junctions and symbolic links and their targets starting from d:\Temp\:
dir 'd:\Temp' -recurse -force | ?{$_.LinkType} | select FullName,LinkType,Target
Output:
FullName LinkType Target
-------- -------- ------
D:\Temp\MyJunctionDir Junction {D:\exp\junction_target_dir}
D:\Temp\MySymLinkDir SymbolicLink {D:\exp\symlink_target_dir}
D:\Temp\MyHardLinkFile.txt HardLink {D:\temp\MyHardLinkFile2.txt, D:\exp\hlink_target.xml}
D:\Temp\MyHardLinkFile2.txt HardLink {D:\temp\MyHardLinkFile.txt, D:\exp\hlink_target.xml}
D:\Temp\MySymLinkFile.txt SymbolicLink {D:\exp\symlink_target.xml}
D:\Temp\MySymLinkDir\MySymLinkFile2.txt SymbolicLink {D:\temp\normal file.txt}
If you care about multiple targets for hardlinks use this variation which lists targets tab-separated:
dir 'd:\Temp' -recurse -force | ?{$_.LinkType} | select FullName,LinkType,#{ Name = "Targets"; Expression={$_.Target -join "`t"} }
You may need administrator privileges to run this script on say C:\.
Utilize Where-Object to search for the ReparsePoint file attribute.
Get-ChildItem | Where-Object { $_.Attributes -match "ReparsePoint" }
For those that want to check if a resource is a hardlink or symlink:
(Get-Item ".\some_resource").LinkType -eq "HardLink"
(Get-Item ".\some_resource").LinkType -eq "SymbolicLink"
My results on Vista, using Keith Hill's powershell script to test symlinks and hardlinks:
c:\markus\other>mklink symlink.doc \temp\2006rsltns.doc
symbolic link created for symlink.doc <<===>> \temp\2006rsltns.doc
c:\markus\other>fsutil hardlink create HARDLINK.doc \temp\2006rsltns.doc
Hardlink created for c:\markus\other\HARDLINK.doc <<===>> c:\temp\2006rsltns.doc
c:\markus\other>dir
Volume in drive C has no label.
Volume Serial Number is C8BC-2EBD
Directory of c:\markus\other
02/12/2010 05:21 PM <DIR> .
02/12/2010 05:21 PM <DIR> ..
01/10/2006 06:12 PM 25,088 HARDLINK.doc
02/12/2010 05:21 PM <SYMLINK> symlink.doc [\temp\2006rsltns.doc]
2 File(s) 25,088 bytes
2 Dir(s) 6,805,803,008 bytes free
c:\markus\other>powershell \script\IsSymLink.ps1 HARDLINK.doc
False
c:\\markus\other>powershell \script\IsSymLink.ps1 symlink.doc
True
It shows that symlinks are reparse points, and have the ReparsePoint FileAttribute bit set, while hardlinks do not.
here is a one-liner that checks one file $FilePath and returns if it is a symlink or not, works for files and directories
if((Get-ItemProperty $FilePath).LinkType){"symboliclink"}else{"normal path"}
Just want to add my own two cents, this is a oneliner function which works perfectly fine for me:
Function Test-Symlink($Path){
((Get-Item $Path).Attributes.ToString() -match "ReparsePoint")
}
The following PowerShell script will list all the files in a directory or directories with the -recurse switch. It will list the name of the file, whether it is a regular file or a hardlinked file, and the size, separated by colons.
It must be run from the PowerShell command line. It doesn't matter which directory you run it from as that is set in the script.
It uses the fslink utility shipped with Windows and runs that against each file using the hardlink and list switches and counts the lines of output. If two or greater it is a hardlinked file.
You can of course change the directory the search starts from by changing the c:\windows\system in the command. Also, the script simply writes the results to a file, c:\hardlinks.txt. You can change the name or simply delete everything from the > character on and it will output to the screen.
Get-ChildItem -path C:\Windows\system -file -recurse -force |
foreach-object {
if ((fsutil hardlink list $_.fullname).count -ge 2) {
$_.PSChildname + ":Hardlinked:" + $_.Length
} else {
$_.PSChildname + ":RegularFile:" + $_.Length
}
} > c:\hardlinks.txt