Powershell: Multipart RAR extraction - powershell

I have a folder with n number of rar files with some having .part01,.part02 as they are divided. the code i am currently using is given below.
$Rars = Get-ChildItem -path 'c:\demopath' -filter "*.rar"
$Destination = 'c:\demopath'
$WinRar = "C:\Program Files\WinRAR\WinRAR.exe"
foreach ($rar in $Rars)
{
&$Winrar x -y $rar.FullName $Destination
Get-Process winrar | Wait-Process
}
This code is running many times to extract same file again and again by the number of parts it has. example if the file is having 3 parts it will get extract same file 3 times (overwriting the previously extracted file). for single rar file there is no issue. if i only give "x" then it is giving popups for file already exists. need a solution which will not give any popups and extract only if same name file is not there in the directory.
can someone help me to fix this issue?

solved the issue with using unrar freeware in winrar directory.
all i needed was to use the "-o-" parameter to stop overwriting the existing extracted file. same command will work with winrar.exe as well
&$UnRAR x -o- $rar.FullName $Destination

Related

Powershell: How to copy files from one Directory to another

I am looking to copy a series of files from one directory to another. Essentially the files are a series of zip folders that are simply changed versions of programs. The files will be named something like: test_1_092.zip in the source directory and test_1_091.zip in the target directory. I don't want the script to look at the numeric portion of the folder, simply the name.
Please forgive my lack of knowledge as this is my first foray into powershell scripting. Any thoughts or need more info?
Something to start with
(Get-ChildItem -Filter "*.zip").Name | where {$_ -like 'test_1_*'} | Move-Item -Destination .\1 -Force -WhatIf
Please confirm the output from –whatif , then remove it to perform the action.
For the relation between the new and old name, please provide some more information.

Problems with zip content extraction

I need help figuring out how to extract the contents of several zip folders within a directory. I am having issues with the following script:
get-childitem *zip | expand-archive | foreach {-destinationpath C:\\...genericpathdestination }
The command works, as it successfully creates unzipped versions in the destination path, but the issue is that it creates each new zip folder within each subsequent folder. To clarify, when I run the command to unzip:
Folder 1
Folder 2
Folder 3
The command saves Folder 3 within Folder 2 and its contents, then Folder 2 (which includes Folder 3) within Folder 1.
I have about 40+ folders that I need to work with so you can see how this solutions becomes counter intuitive rather fast.
All relevant input/help is greatly appreciated.
Sincerely,
RM
The foreach isn't needed in this case. The -force is to update the destination folder.
Get-ChildItem -Path "C:\Users\Administrator\Documents\*.zip" | Expand-Archive -DestinationPath "C:\Users\Administrator\Documents\Here is the unzipped stuff" -Force

How do I zip up files that are too long?

I am using a PowerShell script to zip up files that are older than 60 days. Some of these files have really long filenames so I get the filename or extension is too long error.
I would rather not go into each file and change the names so I need a way to be able to apply something to all the files at the same time or have my script bypass the error somehow. This script is also going to be run on several computers so I would prefer not to download something on to each one.
This is the script:
#Set source and target
$Source = "D:\Testing\"
$Target = "$ENV:USERPROFILE\Desktop\TEST.zip"
#Set time parameters
$Days = 60
$LastWrite = (Get-Date).Date.AddDays(-$Days)
#Invoke 7-zip
if (-not (test-path "$env:ProgramFiles\7-Zip\7z.exe")) {throw
"$env:ProgramFiles\7-Zip\7z.exe needed"}
set-alias zip "$env:ProgramFiles\7-Zip\7z.exe"
$Target = Get-Childitem $Source -Recurse | Where-Object -FilterScript
{($_.LastWriteTime -ge $LastWrite)}
zip a -mx=9 $Target $Source
I am using 7-zip to zip up the files and I have PS version 5.1.
As mentioned in the comments, one way around long file names is to store relative paths. 7Zip will store relative paths if you specify an input file with the relative paths and they resolve to the files you want to archive, as described in this answer.
Intermediate files can be messy, so I've written a script that uses the ZipFileExtensions' CreateEntryFromFile method to store a relative path in a zip file.
You can specify -ParentFolder on the command line to store paths relative to the parent, including a UNC path if you want to archive files on another computer. If -ParentFolder is not specified it will choose the script's folder as the parent and store paths relative to the script.
Copy the code to a new script named ArchiveOldLogs.ps1 and run it with this command line:
.\ArchiveOldLogs.ps1 -ParentFolder "D:\Testing\" -FileSpecs #("*.*") -Filter { $_.LastWriteTime -lt (Get-Date).AddDays(-60)} -DeleteAfterArchiving:$false
That will get you 11 more characters at the end of the path to store, which should be enough to get around the 10 character difference between Windows and Zip path length limits. Try a deeper folder if you still get errors. The files that can't be archived, or are already archived will be skipped.
Remove -DeleteAfterArchiving:$false from the command line when you're comfortable that it's archiving only what you want.

Renaming files from a text list using powershell

I have seen many different postings here on how to approach this task. I have tried them all with minor variations of them to no avail. Every time I run the powershell code, I still get the same error message: "cannot rename because the file does not exist..."
I just simply want to rename a bunch of files in one folder from the list of filenames I have in a text file.
Here is one version of code:
$inputFile1=#()
$filesTochange=#()
$inputFile1 = get-content H:\dev\outputfile.txt
$filesToChange = Get-ChildItem H:\dev\extractedFiles1 | Foreach -Begin
{$i=0}
-process {Rename-Item $filesToChange[$i] -newName ($inputFile1[$i] -f $i++)
}
Here is another version:
$inputFile1=#()
$filesTochange=#()
$inputFile1 = get-content H:\dev\outputfile.txt
$filesToChange = Get-ChildItem H:\dev\extractedFiles1
$i=0
foreach ($fil in $filesToChange) {Rename-Item $fil -NewName
$inputFile1[$i++]}
Not entirely sure what's your setup or desired output is but give this a whirl bud. Not the most elegant looking solutions but hopefully this is what you are looking for? Do be mindful with the sorting of the filenames in your outputfile.txt and how the folders are listed when you get the childitem.
$BasePath = 'C:\Test'
$FilesToChangeFolderName = 'extractedFiles1'
$filestochange = Get-ChildItem -Path "$BasePath\$FilesToChangeFolderName"
$FileNames = Get-Content "$BasePath\outputfile.txt"
if($filestochange.FullName.Count -eq $FileNames.Count)
{
for($i=0; $i -lt $FileNames.Count; $i++)
{
write-host "Renaming file $($filestochange.Name[$i]) to $($FileNames[$i]+".txt")"
Rename-Item -Path $filestochange.FullName[$i] -NewName ($FileNames[$i]+".txt")
}
}
Setup -
outputfile.txt contains:
renametoA
renametoB
renametoC
renametoD
renametoE
renametoF
Folder structure:
Results:
Renaming file renameto1.txt to renametoA.txt
Renaming file renameto2.txt to renametoB.txt
Renaming file renameto3.txt to renametoC.txt
Renaming file renameto4.txt to renametoD.txt
Renaming file renameto5.txt to renametoE.txt
Renaming file renameto6.txt to renametoF.txt
Explanation [TLDR]:
The script below takes a text file input which contains the name that should be used to rename each text file you have.
Example:
outputfile.txt file contains the names below:
renametoA
renametoB
renametoC
renametoD
renametoE
renametoF
There are 6 text files inside the "extractedFiles1" folder which you can see in the image.
The script actually renames the files in the "extractedFiles1" folder according to the names from the output.txt file.
Thus it follows this logic:
Renaming file renameto1.txt to renametoA.txt
Renaming file renameto2.txt to renametoB.txt
Renaming file renameto3.txt to renametoC.txt
Renaming file renameto4.txt to renametoD.txt
Renaming file renameto5.txt to renametoE.txt
Renaming file renameto6.txt to renametoF.txt
So after all the script runs your "extractedFiles1" folder's gonna look something like this:
Despite this being an older thread, I wanted to offer another "brute force" option.
CodeNagi's reply works well in PowerShell, although it took me a bit to get working.
If you already have a list with file names (output.txt) consider using excel (or OpenOffice) and the command prompt cmd.
This video is a step by step guide for some of this solution:
https://youtu.be/znhqGrF4gVQ
Open cmd with administrator privileges.
Create a list of your current (old) file names:
cd H:\dev\extractedFiles1
dir > input.txt
Open the input.txt (e.g. in Notepad). It should look like this:
Volume in drive H is Windows
Volume Serial Number is C123-4567
Directory of H:\dev\extractedFiles1
05/12/2022 11.24 .
05/12/2022 11.24 ..
05/12/2022 09.34 5,255,248 Filename1.txt
05/12/2022 09.34 5,255,952 Filename2.txt
...
Copy the lines with filenames and timestamps into an Excel sheet
Use Data > Text to columns to split the filenames from the time stamps
Copy or import your target/new filenames from output.txt next to the old filenames
Make a new column with the formula
= "ren"&" "&A1&" "&B1
resulting in something like
ren Filename1.txt FilenameA.txt
Copy all formulas and paste them in cmd. Make sure you are in the right directory.
Notes: If you have spaces in the file names, you will need to wrap each file name first in apostrophes ". Since the concatenation formula in excel doesn't accept """ (triple apostropes), make yourself a column with only " (here C) and then refer to it in the concatenation: = "ren "&C1&A1&C1&" "&C1&B1&C1&.
Further, if you have duplicate files or want to make sure they are copied correclty, you can use the MOVE function instead of rename (ren).
Instead of point 6. above do the following:
Make a new column with the formula
= "MOVE"&" "&A1&" "&"H:\dev\extractedFiles1\Renamed"&B1
Copy the created command into cmd
It will move and rename the files according to the names in B1.
This example make a copy and rename file to a list csv
Import-CSV LISTA.csv -Header newFileName | % { Copy-Item -Path archivo_convert.pdf -Destination "$($_.newfilename).pdf" }

How to do a copy /b in a powershell script to insert a BOM marker, but as a batch for files that match a filter and changes the ext on output?

REASONS WHY THIS IS NOT A DUPLICATE
Since 3 people have already voted to close, I guess I should explain why this question is not a duplicate:
I cannot use cat or >> as these mess up the encoding of the files, which are UTF8 on input and need to be UTF8-BOM on output.
The linked question does not show how to loop through all files that match a given pattern in a directory, and concatenate a single file to each of the matching files on output, plus give the new file a different extension.
Using Set-Content is not Powershell 6 future-proof, since Set-Content will NOT add a BOM marker. In Powershell 5 and below, it sometimes adds a BOM marker and sometimes not, depending on the configuration settings of the executing user. See 'quick note on encoding' at the end of this article.
So in conclusion I am looking for a solution that uses copy (hence the question title) and does NOT use Cat or Set-Content.
I need to loop through certain files in a given directory and run the following on each file:
copy /b BOMMarker.txt+InputFile.dat OutputFile.txt
This inserts the contents of the BOMMarker.txt file at the start of the InputFile.dat and writes the output to OutputFile.txt
I found this question which explains how I can loop through the folder to load each file into Powershell, but how do I apply the "copy /b" command so that I can get the BOM marker at the start of each file?
EDIT
The comment from Jeroen indicates I can just do Set-Content on the output file, as Powershell will automatically add the BOM at the start.
But I also need to change the extension. So the output filename needs to be the same as the input filename, just with a changed extension (from .dat to .txt) and including the BOM.
I am guessing I can use Path.ChangeExtension somehow to do this, but not sure how to combine that with also adding the BOM.
EDIT - for Bounty
The example answer I posted does not work in all environments I tested it, and I do not know why (possibly different default Powershell setttings) but also, it is not future proof since Powershell 6 will not output BOM by default.
From the given directory, I need to process all files that match the filter (DIL_BG_TXN*.dat).
For each of those files, I need to copy it with a BOM at the start but the resultant new file needs to be the same name but with the extension .txt instead of .dat.
This solutions uses streams, that reliably read and write as-is:
$bomStream = [IO.File]::OpenRead('BOMMarker.txt')
$location = "" # set this to the folder location
$items = Get-ChildItem -Path $location -Filter DIL_BG_TXN*.dat
foreach ($item in $items) {
$sourceStream = [IO.File]::OpenRead($item.FullName)
$targetStream = [IO.File]::OpenWrite([IO.Path]::ChangeExtension($item.FullName, '.txt'))
$bomStream.CopyTo($targetStream)
$sourceStream.CopyTo($targetStream)
$targetStream.Flush()
$targetStream.Close()
$sourceStream.Close()
$bomStream.Position = 0
}
$bomStream.Close()
Of course please write the absolute path of BOMMarker.txt (1st line) according to its location.
This finally worked:
$Location = "C:\Code\Bulgaria_Test"
$items = Get-ChildItem -Path $Location -Filter DIL_BG_TXN*.dat
ForEach ($item in $items) {
Write-Host "Processing file - " $item
cmd /c copy /b BOMMarker.txt+$item ($item.BaseName + '.txt')
}
Description:
Set the directory location where all the .dat files are.
Load only those files that match the filter into the array $items.
Loop through each $item in the array.
With each $item, call cmd shell with the copy /b command and concatenate the bom marker file with the $item file and write the result to the basename of $item plus the new extension.