Renaming multiple files in archives - powershell

Here's the renaming problem I need to solve:
There are a few hundred .zip archive files which contain at least few files in them.
Files in archives are parts and are named like this:
Data_Customer1_20160101_part1.txt
Data_Customer1_20160101_part2.txt
Data_Customer1_20160101_partN.txt
I need to change Customer1 part to Customer2 using CMD, PowerShell or 7zip.
Approach extract - rename - archive is not acceptable as there are few hundred GBs of data so it would be very slow.
Is there any possible solution to rename files in archives without extrating them?

This can be done using PowerShell and the latest 7-Zip 15.14 exe. Download and install the 7-Zip, once installed 7z.exe should be in the following location C:\Program Files\7-Zip\7z.exe.
$ArchivesFolder = "T:\Your Archives\"
$7zipExe = "C:\Program Files\7-Zip\7z.exe"
$search = "Customer1"
$replace = "Customer2"
gci $ArchivesFolder -Filter "*.zip" | % {
$archive = $_.FullName
Write-Host $archive -ForegroundColor DarkYellow
& $7zipExe l $archive `
| select -Skip 11 `
| %{IF($_.Length -gt 52 ){$_.substring(53)}} `
| ?{$_ -notmatch "\d* files, \d* folders|------------------------" -and $_ -match $search } `
| %{
write-host "Renaming:[$_] To:[$($_ -replace $search,$replace)]" -ForegroundColor cyan
& $7zipExe rn $archive "$_" "$($_ -replace $search,$replace)" | Out-Null # Comment out
}
}
The above PowerShell will recurse through a give folder ($ArchivesFolder) of .zip files. It will look within each zip file and replace/rename any part of the files name that match the give search variable ($search) with the replacement valuve given ($replace).
If you want to see what the script does before running it, comment out the line that has (# Comment out) next to it with a #. This will stop any file from being renamed.
The script is using the rn parameter of 7z.exe The syntax to rename files inside archives is
7z rn <archive_name> <src_file_1> <dest_file_1> [ <src_file_2> <dest_file_2> ... ]
Note This will only work with 15.14 version of 7z.exe.

Related

How can I modify this PowerShell script to include changing directory names to lower?

I am trying to rename all files and directories to lower and I found a powershell script here: Rename files to lowercase in Powershell
My favorite answer is the following because it is the cleanest and most concise answer. However, it does not include directory names and I don't have enough rep yet to respond to the comment
Get-ChildItem -r | Where-Object { !$_.PSIsContainer } | Rename-Item -NewName { $_.FullName.ToLower() }
I don't know PowerShell and I don't intend to become proficient, please skip all the details I'm just looking for code to rename all my files and directories to lower and I don't need to know anything about how it works. I don't like the following solution because 1, it is too wordy and 2, it only does directory names and not file names.
Where-Object { $_.PSIsContainer -And $_.Name -CMatch "[A-Z]" } |
ForEach-Object {
$NName = $_.Name.ToLowerInvariant()
# Set temporary name to enable rename to the same name; Windows is not case sensitive
$TempItem = Rename-Item -Path $_.FullName -NewName "x$NName" -PassThru
Rename-Item -Path $TempItem.FullName -NewName $NName
}
I want one clean command to rename files and directories, similar to the first example, please
when i first wrote this, i just wanted to open powershell and paste a command. in hindsight, that is not most efficient way either. so i ended up saving each script (one for files, one for folders) into one .ps1 file that you put in whatever directory you want to lower, then right-click and "run with powershell" and it will rename all files and subdirectories
the script looks like this:
# files to lower
Get-ChildItem -r | Where-Object { !$_.PSIsContainer } |
Rename-Item -NewName { $_.FullName.ToLower() }
# folders to lower
$fso = New-Object -ComObject Scripting.FileSystemObject
Get-ChildItem . -rec -dir |
ForEach-Object { $fso.MoveFolder($_.fullname, $_.Fullname.ToLower()) }
as you mentioned on your provided code, Windows is not a case sensitive OS, so you need to rename the directories to a temp name (for example insert a character after lowering it) then rename it again (by removing the inserted character)
i modified your line as follow to be able to lower both directories and files, please give it a try
Get-ChildItem -r | Rename-Item -NewName { $_.Name.ToLower().Insert(0,'_') } -PassThru | Rename-Item -NewName { $_.Name.Substring(1) }

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")
}
}

Merge CSV Files in Powershell traverse subfolders - archiving & deleting old files use folder name for Target-CSV

I want to merge many CSV-files into one (a few hundred files) removing the header row of the added CSVs.
As the files sit in several subfolders I need to start from the root traversing all the subfolders and process all CSVs in there. Before merging I want to archive them with zip deleting old CSVs. The new merged CSV-file and the zip-archive should be named like their parent folder.
In case the Script is started again for the same folder none of already processed files should be damaged or removed accidentally.
I am not a Powershell guy so I have been copying pasting from several resources in the web and came up with the following solution (Sorry don't remember the resources feel free to put references in the comment if you know).
This patch-work code does the job but it doesn't feel very bulletproof. For now it is processing the CSV files in the subfolders only. Processing the files within the given $targDir as well would also be nice.
I am wondering if it could be more compact. Suggestions for improvement are appreciated.
$targDir = "\\Servername\folder\"; #path
Get-ChildItem "$targDir" -Recurse -Directory |
ForEach-Object { #walkinthrough all subfolder-paths
#
Set-Location -Path $_.FullName
#remove existing AllInOne.csv (targed name for a merged file) in case it has been left over from a previous execution.
$FileName = ".\AllInOne.csv"
if (Test-Path $FileName) {
Remove-Item $FileName
}
#remove existing AllInOne.csv (targed name for archived files) in case it has been left over from a previous execution.
$FileName = ".\AllInOne.zip"
if (Test-Path $FileName) {
Remove-Item $FileName
}
#compressing all csv files in the current path, temporarily named AllInOne.zip. Doing that for each file adding it to the archive (with -Update)
# I wonder if there is a more efficient way to do that.
dir $_.FullName | where { $_.Extension -eq ".csv"} | foreach { Compress-Archive $_.FullName -DestinationPath "AllInOne.zip" -Update}
##########################################################
# This code is basically merging all the CSV files
# skipping the header of added files
##########################################################
$getFirstLine = $true
get-childItem ".\*.csv" | foreach {
$filePath = $_
$lines = $lines = Get-Content $filePath
$linesToWrite = switch($getFirstLine) {
$true {$lines}
$false {$lines | Select -Skip 1}
}
$getFirstLine = $false
Add-Content ".\AllInOne.csv" $linesToWrite
# Output file is named AllInOne.csv temporarily - this is not a requirement
# It was simply easier for me to come up with this temp file in the first place (symptomatic for copy&paste).
}
#########################################################
#deleting old csv files
dir $_.FullName | where { $_.Extension -eq ".csv" -and $_ -notlike "AllInOne.csv"} | foreach { Remove-Item $_.FullName}
# Temporarily rename AllinOne files with parent folder name
Get-ChildItem -Path $_.FullName -Filter *.csv | Rename-Item -NewName {$_.Basename.Replace("AllInOne",$_.Directory.Name) + $_.extension}
Get-ChildItem -Path $_.FullName -Filter *.zip | Rename-Item -NewName {$_.Basename.Replace("AllInOne",$_.Directory.Name) + $_.extension}
}
I have been executing it in the Powershell ISE. The Script is for a house keeping only, executed casually and not on a regular base - so performance doesn't matter so much.
I prefer to stick with a script that doesn't depend on additional libraries if possible (e.g. for Zip).
It may not be bulletproof, but I have seen worse cobbled together scripts. It'll definitely do the job you want it to, but here are some small changes that will make it a bit shorter and harder to break.
Since all your files are CSVs and all would have the same headers, you can use Import-CSV to compile all of the files into an array. You won't have to worry about stripping the headers or accidentally removing a row.
Get-ChildItem "*.csv" | Foreach-Object {
$csvArray += Import-CSV $_
}
Then you can just use Export-CSV -Path $_.FullName -NoTypeInformation to output it all in to a new CSV file.
To have it check the root folder and all the subfolders, I would throw all of the lines in the main ForEach loop into a function and then call it once for the root folder and keep the existing loop for all the subfolders.
function CompileCompressCSV {
param (
[string] $Path
)
# Code from inside the ForEach Loop
}
# Main Script
CompileCompressCSV -Path $targetDir
Get-ChildItem -Path $targetDir -Recurse -Directory | ForEach-Object {
CompileCompressCSV -Path $_.FullName
}
This is more of a stylistic choice, but I would do the steps of this script in a slightly different order:
Get Parent Folder Name
Remove old compiled CSVs and ZIPs
Compile CSVs into an array and output with Parent Folder Name
ZIP together CSVs into a file with the Parent Folder Name
Remove all CSV files
Personally, I'd rather name the created files properly the first time instead of having to go back and rename them unless there is absolutely no way around it. That doesn't seem the case for your situation so you should be able to create them with the right name on the first go.

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) }

Powershell - modify items in ZIP archive

I read about extracting ZIP archives on Stackoverflow which results in creating following script:
$shell_app=New-Object -com shell.application
Get-ChildItem -name *.zip | ForEach-Object {
$zip_file=$shell_app.NameSpace((Get-Location).path + " \$_")
$destination=$shell_app.NameSpace((Get-Location).path)
$destination.copyhere($zip_file.items()) }
Now I'm interested in manipulating on items in this archive - for example before I unpack all those files, I want to add to their filenames, name of archive.
AS I check, I can get it (with .zip extenston which I want to remove) by simply entering
%zip_file.title
but I'm not aware how to modify file names. Could anybody help or give sufficient resources?
I don't think you can do that with the shell object alone. I think you'll have to extract the file files and then rename using PowerShell. Here's my test code that works. Quick and dirty but it does the trick.
$zip="c:\work\brain.zip"
$shell_app=New-Object -com shell.application
$zip_file=$shell_app.NameSpace($zip)
$destination=$shell_app.NameSpace("G:\test")
$zip_file.items() | foreach {
$newname="{0}_{1}" -f $zip_file.Title,$_.name
Write-Host "Extracting $newname" -ForegroundColor Green
$destination.copyhere($_)
$oldfile=Join-path $destination.Self.Path -ChildPath $_.name
Rename-Item -Path $oldfile -NewName $newname -passthru
}
Why not create a subfolder for each zipfile in the destination?