I am trying to create myself a powershell script that has mainly two tasks - one after the other.
Initial assumptions: it runs where it performs the following tasks.
Trace all the files, those that are folders - make a zip of them, then after making the archive, delete them. Objective accomplished.
Write to the file the names of all files in the folder (after completing point 1) but check if the given file has the extension of *.zip
a) if it has extension of *.zip then it should be saved in .txt file like "uresoruce = foo/file.zip"
b) if it doesn't have extension *.zip then it should be saved to a .txt file like this "resoruce = foo/file2.jar"
c) since the script is started from the place where all the files are, it will probably also be saved to a file, I would like to avoid this
Suppose we have some files in a folder after compiling, and we don't have any folder inside. The .txt file should look like the following:
uresource = plugins/Liula_1.0.0.0.zip
uresource = plugins/Liborts_3.7.1.0.zip
uresource = plugins/Liwer_1.2.0.0.zip
resource = plugins/o0.I20100512-1500.jar
resource = plugins/or.v20100505-1235.jar
The script I managed to write so far:
## set current path
$path = (Resolve-Path .\).Path
## dirs in a path
$source = Get-ChildItem -Path $path -Filter "" -Directory
$files = Get-ChildItem $path
Add-Type -assembly "system.io.compression.filesystem"
Foreach ($s in $source) {
$destination = Join-path -path $path -ChildPath "$($s.name).zip"
[io.compression.zipfile]::CreateFromDirectory($s.fullname, $destination)
Remove-Item $s -Recurse
}
# This does not working!!! :/
Foreach ($f in $files) {
$extn = [IO.Path]::GetExtension($f)
if ($extn -eq ".zip" ) {
$outfile = "uresource = plugins/" + $f.Name
}
else {
$outfile = "resource = plugins/" + $f.Name
}
}
As #Santiago Squarzon rightly pointed out I did not do anything with this variable. I got a little confused because before I did it only for out-file I didn't use -append and in fact I got the last value in .txt. Now I made my first script in ph, it works like a dream ;)
## set current path
$path = (Resolve-Path .\).Path
## dirs in a path
$source = Get-ChildItem -Path $path -Filter "" -Directory
$files = Get-ChildItem $path
Add-Type -assembly "system.io.compression.filesystem"
## create zip and delete other dirs
Foreach ($s in $source) {
$destination = Join-path -path $path -ChildPath "$($s.name).zip"
[io.compression.zipfile]::CreateFromDirectory($s.fullname, $destination)
Remove-Item $s -Recurse
}
## output filename in .txt
$logFile = "$pwd\logfile.txt"
Foreach ($f in $files) {
$extn = [IO.Path]::GetExtension($f)
if ($extn -eq ".zip" ) {$outfile = "uresource = plugins/$f"}
else {$outfile = "resource = plugins/$f"}
$outfile | Out-File -Append $logFile
}
Related
Currently, I am writing a script that moves PDF files that are wrongfully zipped to a certain folder. I have achieved that. The next thing I need to get to work, is that the zipped .pdf files get unzipped into a different folder.
This is my whole script. Everything except for the last 2 lines is dedicated to finding the PDF files that are zipped and moving them.
In the first parts, the script checks the first few bytes of every pdf file in the folder. If they start with "PK*", they are zip files and get moved to the zipped folder.
For every PDF/zip file, there is one associated HL7 file in the folder next to it.
These also need to get moved to the same folder. From there the zip files need to be unzipped and relocated to "unzipped"
The last 2 lines are for unzipping.
$pdfDirectory = 'Z:\Documents\16_Med._App\Auftraege\PDFPrzemek\struktur_id_1225\ext_dok'
$newLocation = 'Z:\Documents\16_Med._App\Auftraege\PDFPrzemek\Zip'
Get-ChildItem "$pdfDirectory" -Filter "*.pdf" | foreach {
if ((Get-Content $_.FullName | select -First 1 ) -like "PK*") {
$HL7 = $_.FullName.Replace("ext_dok","MDM")
$HL7 = $HL7.Replace(".pdf",".hl7")
move $_.FullName $newLocation;
move $HL7 $newLocation
}
}
Get-ChildItem 'Z:\Documents\16_Med._App\Auftraege\PDFPrzemek\Zip' |
Expand-Archive -DestinationPath 'Z:\Documents\16_Med._App\Auftraege\PDFPrzemek\Zip\unzipped' -Force
This, sadly, doesn't work.
I suspect that it's because these files dont have the .zip extension. The only Filter that works for Expand-Archive is .zip.
So I need to find a way to get this function to unzip the files, even though they dont have the fitting extension...
Like #Ansgar said this would be the way to go:
Param (
$SourcePath = 'C:\Users\xxx\Downloads\PDF',
$ZipFilesPath = 'C:\Users\xxx\Downloads\ZIP',
$UnzippedFilesPath = 'C:\Users\xxx\Downloads\Unzipped'
)
$VerbosePreference = 'Continue'
#region Test folders
#($SourcePath, $ZipFilesPath, $UnzippedFilesPath) | Where-Object {
-not (Test-Path -LiteralPath $_)
} | ForEach-Object {
throw "Path '$_' not found. Make sure that the folders exist before running the script."
}
#endregion
#region Get all files with extension .pdf
$Params = #{
Path = Join-Path -Path $SourcePath -ChildPath 'ext_dok'
Filter = '*.pdf'
}
$PDFfiles = Get-ChildItem #Params
Write-Verbose "Got $($PDFfiles.count) files with extension '.pdf' from '$($Params.Path)'"
#endregion
#region Move PDF and HL7 files
$MDMpath = Join-Path -Path $SourcePath -ChildPath 'MDM'
foreach ($PDFfile in ($PDFfiles | Where-Object {
(Get-Content $_.FullName | Select-Object -First 1) -like 'PK*'})
) {
$MoveParams = #{
Path = $PDFfile.FullName
Destination = Join-Path -Path $ZipFilesPath -ChildPath ($PDFfile.BaseName + '.zip')
}
Move-Item #MoveParams
Write-Verbose "Moved file '$($MoveParams.Path)' to '$($MoveParams.Destination)'"
$GetParams = #{
Path = Join-Path -Path $MDMpath -ChildPath ($PDFfile.BaseName + '.hl7')
ErrorAction = 'Ignore'
}
if ($HL7file = Get-Item #GetParams) {
$MoveParams = #{
Path = $HL7file
Destination = $ZipFilesPath
}
Move-Item #MoveParams
Write-Verbose "Moved file '$($MoveParams.Path)' to '$($MoveParams.Destination)$($HL7file.Name)'"
}
}
#endregion
#region Unzip files
$ZipFiles = Get-ChildItem -Path $ZipFilesPath -Filter '*.zip' -File
foreach ($ZipFile in $ZipFiles) {
$ZipFile | Expand-Archive -DestinationPath $UnzippedFilesPath -Force
Write-Verbose "Unzipped file '$($ZipFile.Name)' in folder '$UnzippedFilesPath'"
}
#endregion
Some tips:
Add a Param () clause at the beginning of the script to contain all your variables that can change.
Try to use the full parameter name to clearly indicate what is what. Use Get-ChildItem -Path xxx instead of Get-ChildItem xxx.
Use hash tables for long parameters. This makes the code more compact in width and more easily to read.
Use #region and #endregion to group your code.
I've finally have given up googling and come here out of desperation. Go easy on me I'm fairly new to Powershell.
So, the objective of the code below was to first look through the source folder, then read through each .zip file and move to the directory specified by the value in the hashtable. Unfortunately, this is not how they want it to work anymore.
Now I need to retain the parent folder from source: for example "DAL" and then create the proceeding folders based on the file names and finally move each .zip to its file specified folder. Also, it needs to go through each folder under source which will be at least 20 other folders with a unique 3 character names.
$srcRoot = "C:\Cloud\source\dal"
$dstRoot = "C:\Cloud\Destination"
##$map = #{}; dir -recurse | ? { !$_.psiscontainer} | % { ##$map.add($_.name,$_.PSChildName) }
# DAT and DEV will have to be excluded from folder creation
$map = {
#AEODDAT_201901 = "AEOD\2019\01"
#AEOMDEV_201902 = "AEOM\2019\01"
#AEOYDAT_201902 = "AEOY\2019\01"
}
$fileList = Get-ChildItem -Path $srcRoot -Filter "*.zip*" -File -Force -Recurse
foreach ($file in $fileList)
{
#Go through each file up to mapped string
$key = $file.BaseName.Substring(0,14)
if ($key -in $map.Keys)
{
$fileName = $file.Name
$dstDir = Join-Path -Path $dstRoot -ChildPath $map[$key]
#create direcotory if not in path
if (-not (Test-Path -Path $dstDir))
{
mkdir -Path $dstDir
}
Write-Verbose "Moving $($file.FullName)"
if (Test-Path -Path (Join-Path -Path $dstDir -ChildPath $fileName))
{
#Write error if name exists
Write-Error -Message "File $fileName already exists at $dstDir"
#move path
} else {
Move-Item -Path $($file.FullName) -Destination $dstDir
}
}
}
So C:\Cloud\source\DAL\AEODDAT20190101.zip should create folders in C:\Cloud\Destination\DAL\AEOD\2019\01\AEODDAT20190101.zip would be my desired output.
Welcome, Matt! (no pun intended) One of the habits I have in similar situations with destination folders is to Set-Location $dstRoot and create folders from the relative path. You can execute New-Item with the relative path and the syntax is simpler. For example, your If statement could look like this and it would work the same way (with a slightly different error message):
if ($key -in $map.Keys){
Set-Location $dstRoot
New-Item -ItemType Directory $map[$key] -ErrorAction Ignore #won't raise an error if it exists
Write-Verbose "Moving $($file.FullName)"
#this will raise an error if the file already exists, unless you specify -Force
Move-Item "$($file.FullName)" $map[$key]
}
EDIT: Found 2 issues.
$map is a Hashtable literal that should be preceded with #:
$map = #{
AEODDAT20190101 = "AEOD\2019\01"
You were missing the last character of the base file name by taking only the first 14 characters. AEODDAT2019010 didn't match AEODDAT20190101. This should fix it:
$key = $file.BaseName.Substring(0,15)
I try to automate a repetitive scenario by Powershell script:
I have a compressed (.zip) folder with multiple files (.XML) in it.
My scenario is to:
uncompressed this folder located in: C:\Users\*\Downloads
changed value inside a specific in each file
compress all the file into a folder with a specific name (format: AppName.Version.zip) that will be located in the same path: C:\Users\*\Downloads.
Questions:
How do I compress the folder again so that by clicking on the compress folder I'll see immediately the files (zipped folder>files) and not a folder then the files (zipped folder>files)
Compress and Expand commands are yelling about the folder name format of and therefore those commands are failed to their job. How do I get over it?
$languageString = Read-Host ...
$replaceString = '<value>' + $languageString
$file = Get-Childitem "c:\users\*\Downloads\app.version.zip" -Recurse
$originalFileName = $file.Name
$absoluteFilePath = $file.FullName
$rename-item -path $absoluteFilePath -newname translate.zip
$file = Get-Childitem "c:\users\*\Downloads\translate.zip" -Recurse
$absoluteFilePath = $file.FullName
$unzipFilePath = $absoluteFilePath.Substring(0, $absoluteFilePath.LastIndexOf('.'))
expand-archive -path $absoluteFilePath -destinationpath $unzipFilePath
$files = #(Get-Childitem "c:\users\*\Downloads\*.xml)
foreach ($file in $files)
{
(Get-Content -path $file.FullName -Raw) -replace '<value>' , $replaceString | Set-Content -Path $file.FullName
#this line makes problems...
Compress-Archive -path $unzipFilePath -Destinationpath $unzipFilePath -Force
I see it in three steps:
1. unzip in a temporary clean location.
2. change your files there
3. zip that location back.
the code would have the functions:
function Unzip_archive ($ArchiveFullPath, $Temp_Work_Fld) {
try {
#unzip
Add-Type -assembly 'system.io.compression.filesystem'
[io.compression.zipfile]::ExtractToDirectory($ArchiveFullPath, $Temp_Work_Fld)
}
catch {
throw "Failed to unzip the artifact"
}
Remove-Item $ArchiveFullPath -Force #only if you want to delete the archive
}
function CreateZipArchive($FolderToArchive, $ZipArchiveFullName){
$source = Get-ChildItem * -Directory | Where-Object {$_.FullName -match "$FolderToArchive"}
write-host "`nArchiving: $source ........."
Add-Type -Assembly system.IO.compression.filesystem
$compressionlevel = [System.IO.Compression.CompressionLevel]::Optimal
[io.compression.zipfile]::CreateFromDirectory($source, $ZipArchiveFullName, $compressionlevel, $true)
write-host "nDone !"
}
then just call the functions before and after doing your changes in the temporary folder
I'd like to use Powershell to recursively copy a specific set of files from a series of sub directories into a single folder (without the source folder structure). The files all follow the filename format "Writing (*).xlsm". Also, some of the directories have their files stored in a ZIP file and I'd like to search these ZIP files for this same file name.
I've seen plenty of solutions that can copy the files but the result recreates the folder structure that the files were found in but I need to capture them all into a single directory, and I have not seen anything that seems to include the ability to add the opening of ZIP files to the search.
Try this:
$Path = "C:\"
$Destination = "C:\Destination"
$Files = #()
$Files = #(Get-ChildItem -Path "$Path" -Filter "Writing*.xml" -Recurse).Fullname
#Zip
$ZipFiles = #(Get-ChildItem -Path "$Path" -Filter "*.Zip" -Recurse).Fullname
[Void][Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
Add-Type -AssemblyName System.IO.Compression.FileSystem
$ToRemove = #()
Foreach($Zip in $ZipFiles){
$RawFiles = [IO.Compression.ZipFile]::OpenRead($Zip).Entries
foreach($RawFile in $RawFiles) {
If($RawFile.FullName -like "Writing*.xml"){
$outpath = $Zip.Remove($Zip.Length - 4)
[System.IO.Compression.ZipFile]::ExtractToDirectory($Zip, $outpath)
$Files += "$outpath\" + $RawFiles.Fullname
$ToRemove += $outpath
}
}
}
Foreach($File in $Files){
Copy-Item -Path $Files -Destination $Destination
}
Foreach($Folder in $ToRemove){
Remove-Item -Path $Folder -Force
}
I need to unzip a specific directory from a zipfile.
Like for example extract the directory 'test\etc\script' from zipfile 'c:\tmp\test.zip' and place it in c:\tmp\output\test\etc\script.
The code below works but has two quirks:
I need to recursively find the directory ('script') in the zip file (function finditem) although I already know the path ('c:\tmp\test.zip\test\etc\script')
With CopyHere I need to determine the targetdirectory, specifically the 'test\etc' part manually
Any better solutions? Thanks.
The code:
function finditem($items, $itemname)
{
foreach($item In $items)
{
if ($item.GetFolder -ne $Null)
{
finditem $item.GetFolder.items() $itemname
}
if ($item.name -Like $itemname)
{
return $item
}
}
}
$source = 'c:\tmp\test.zip'
$target = 'c:\tmp\output'
$shell = new-object -com shell.application
# find script folder e.g. c:\tmp\test.zip\test\etc\script
$item = finditem $shell.NameSpace($source).Items() "script"
# output folder is c:\tmp\output\test\etc
$targetfolder = Join-Path $target ((split-path $item.path -Parent) -replace '^.*zip')
New-Item $targetfolder -ItemType directory -ErrorAction Ignore
# unzip c:\tmp\test.zip\test\etc\script to c:\tmp\output\test\etc
$shell.NameSpace($targetfolder).CopyHere($item)
I don't know about most elegant, but with .Net 4.5 installed you could use the ZipFile class from the System.IO.Compression namespace:
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null
$zipfile = 'C:\path\to\your.zip'
$folder = 'folder\inside\zipfile'
$dst = 'C:\output\folder'
[IO.Compression.ZipFile]::OpenRead($zipfile).Entries | ? {
$_.FullName -like "$($folder -replace '\\','/')/*"
} | % {
$file = Join-Path $dst $_.FullName
$parent = Split-Path -Parent $file
if (-not (Test-Path -LiteralPath $parent)) {
New-Item -Path $parent -Type Directory | Out-Null
}
[IO.Compression.ZipFileExtensions]::ExtractToFile($_, $file, $true)
}
The 3rd parameter of ExtractToFile() can be omitted. If present it defines whether existing files will be overwritten or not.
As far as the folder location in a zip is known, the original code can be simplified:
$source = 'c:\tmp\test.zip' # zip file
$target = 'c:\tmp\output' # target root
$folder = 'test\etc\script' # path in the zip
$shell = New-Object -ComObject Shell.Application
# find script folder e.g. c:\tmp\test.zip\test\etc\script
$item = $shell.NameSpace("$source\$folder")
# actual destination directory
$path = Split-Path (Join-Path $target $folder)
if (!(Test-Path $path)) {$null = mkdir $path}
# unzip c:\tmp\test.zip\test\etc\script to c:\tmp\output\test\etc\script
$shell.NameSpace($path).CopyHere($item)
Windows PowerShell 5.0 (included in Windows 10) natively supports extracting ZIP files using Expand-Archive cmdlet:
Expand-Archive -Path Draft.Zip -DestinationPath C:\Reference