In windows, how can I batch convert base64 file names in a folder to their original names assuming every file name in the folder is encoded with base64
You can do this by iterating the path of the files and try to decode the base64 basenames of those files. If that succeeds, rename the file.
Get-ChildItem -Path 'TheFolderWhereTheFilesAre>' -File | ForEach-Object {
# store the file name for when we hit the catch block
$file = $_.FullName
try {
$newBase = [System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String($_.BaseName))
$_ | Rename-Item -NewName ('{0}{1}' -f $newBase, $_.Extension) -ErrorAction Stop
}
catch {
Write-Warning "Error renaming file '$file':`r`n$_.Exception.Message"
}
}
Related
I want to add a text (sample) to the end of specific file Before the Extension (in powershell or anything else)
for example:
file1.mp4 => file1(sample).mp4 ///
file2.mkv => file2(sample).mkv ///
and don't do anything on other formats
tried this
Get-ChildItem *.mp4 | ForEach-Object {
Rename-Item -Path $_.Name -NewName "$($_.Name) (sample)$($_.extension)"
}
but it adds an addition file format to the name
Because .Name includes the extension. Try .BaseName which does not, e.g.:
-NewName "$($_.BaseName) (sample)$($_.extension)"
I have Several zip files that Contain multiple filetypes. The ONLY ones I am interested in are the .txt files. I need to extract the .txt files only and place them in a folder of their own, ignoring all other file types in the zips files.
All the zip files are in the same folder.
Example
-foo.zip
--1.aaa
--2.bbb
--3.ccc
--4.txt
-foo2.zip
--5.aaa
--6.bbb
--7.ccc
--8.txt
I want to have 4.txt and 8.txt extracted to another folder. I can't for the life of my figure it out and spent ages looking, googling and trying. Even managing to delete to zips once in a while :-)
Thanks in advance
Use the ZipArchive type to programmatically inspect the archive before extracting:
Add-Type -AssemblyName System.IO.Compression
$destination = "C:\destination\folder"
# Locate zip file
$zipFile = Get-Item C:\path\to\file.zip
# Open a read-only file stream
$zipFileStream = $zipFile.OpenRead()
# Instantiate ZipArchive
$zipArchive = [System.IO.Compression.ZipArchive]::new($zipFileStream)
# Iterate over all entries and pick the ones you like
foreach($entry in $zipArchive.Entries){
if($entry.Name -like '*.txt'){
# Create new file on disk, open writable stream
$targetFileStream = $(
New-Item -Path $destination -Name $entry.Name -ItemType File
).OpenWrite()
# Open stream to compressed file, copy to new file stream
$entryStream = $entry.Open()
$entryStream.BaseStream.CopyTo($targetFileStream)
# Clean up
$targetFileStream,$entryStream |ForEach-Object Dispose
}
}
# Clean up
$zipArchive,$zipFileStream |ForEach-Object Dispose
Repeat for each zip file.
Note that the code above has very minimal error-handling, and is to be read as an example
Try this:
Set-Location "Extraction path"
#("full path of foo.zip","full path of foo2.zip") | ForEach {
& "Full path of 7z.exe" x '-i!*.txt' $_.FullName
}
Sets location to the path where files will be extracted.
Passes a array of zip files to for loop.
Exexute 7z command to extract only zip files.
Here is one approach:
Go through each .zip file in a folder.
Extract archive into separate folder.
Extract .txt file from folder.
Copy files into destination folder containing all .txt files. This will overwrite files if they already exist in the destination folder.
Cleanup extracted folders once finished.
Demo:
function Copy-ZipArchiveFiles {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[ValidateScript({
if (-not(Test-Path $_ -PathType Container))
{
throw "The source path $_ does not exist. Please enter a valid source path."
}
else
{
$true
}
})]
[string]$Path,
[Parameter(Mandatory=$true)]
[ValidateScript({
if ([string]::IsNullOrEmpty($_.Trim()))
{
throw "The Destination path is null or empty. Please enter a valid destination path."
}
else
{
$true
}
})]
[string]$Destination,
[Parameter(Mandatory=$false)]
[AllowNull()]
[AllowEmptyString()]
[AllowEmptyCollection()]
[string[]]$Include
)
# Create destination folder if it doesn't already exist
if (-not(Test-Path -Path $Destination -PathType Container))
{
try
{
New-Item -Path $Destination -ItemType Directory -ErrorAction Stop
}
catch
{
throw "The destination path $Destination is invalid. Please enter a valid destination path."
}
}
# Go through each .zip file
foreach ($zipFile in Get-ChildItem -Path $Path -Filter *.zip)
{
# Get folder name from zip file w/o .zip at the end
$zipFolder = Split-Path $zipFile -LeafBase
# Get full folder path
$folderPath = Join-Path -Path $Path -ChildPath $zipFolder
# Expand .zip file into folder if it doesn't exist
if (-not(Test-Path -Path $folderPath -PathType Container))
{
Expand-Archive -Path $zipFile.FullName -DestinationPath $folderPath
}
# Copy files into destination folder
foreach ($file in Get-ChildItem $folderPath -Include $Include -Recurse)
{
Copy-Item -Path $file.FullName -Destination $Destination
}
# Delete extracted folders
Remove-Item -Path $folderPath -Recurse -Force
}
}
Usage:
Copy-ZipArchiveFiles `
-Path "C:\path\to\zip\files" `
-Destination "C:\path\to\text\files" `
-Include "*.txt"
Note: Could also use this for multiple extension types as well by passing -Include *.txt, *.pdf. I also went a bit overboard in the parameter error checking, but I believe in writing robust code. Good habit to get into when writing your own cmdlets anyways :)
I currently have a CSV which contains 1 column that lists many file FullNames. (ie. "\\server\sub\folder\file.ext").
I am attempting to import this CSV, move the file to a separate location and append a GUID to the beginning of the filename in the new location (ie GUID_File.ext). I've been able to move the files, generate the GUID_ but haven't been able to store and reuse the existing filename.ext, it just gets cut off and the file ends up just being a GUID_. I just am not sure how to store the existing filename for reuse.
$Doc = Import-CSV C:\Temp\scripttest.csv
ForEach ($line in $Doc)
{
$FileBase = $Line.basename
$FileExt = $Line.extension
Copy-Item -path $line.File -Destination "\\Server\Folder\$((new-guid).guid.replace('-',''))_$($Filebase)$($FileExt)"
}
If possible, I'm going to also need to store and place all the new GUID_File.ext back into a CSV and store any errors to another file.
I currently have a CSV which contains 1 column that lists many file FullNames. (ie. "\server\sub\folder\file.ext").
This isn't a CSV. It's just a plaintext file with a list.
Here's how you can accomplish your goal, however:
foreach ($path in (Get-Content -Path C:\Temp\scripttest.csv))
{
$file = [System.IO.FileInfo]$path
$prefix = (New-Guid).Guid -replace '-'
Copy-Item -Path $file.FullName -Destination "\\Server\Folder\${prefix}_$file"
}
This will take your list, convert the item into a FileInfo type it can work with, and do the rest of your logic.
Based on:
$FileBase = $line.basename
$FileExt = $line.extension
it sounds like you mistakenly think that the $line instances representing the objects returned from Import-Csv C:\Temp\scripttest.csv are [System.IO.FileInfo] instances, but they're not:
What Import-Csv outputs are [pscustomobject] instances whose properties reflect the column values of the input CSV, and the values of these properties are invariably strings.
You must therefore use $line.<column1Name> to refer to the column containing the full filenames, where <column1Name> is the name defined for the column of interest in the header line (the 1st line) of the input CSV file.
If the CSV file has no header line, you can specify the column names by passing an array of column names to Import-Csv's -Header parameter, e.g.,
Import-Csv -Header Path, OtherCol1, OtherCol2, ... C:\Temp\scripttest.csv
I'll assume that the column of interest is named Path in the following solution:
$Doc = Import-Csv C:\Temp\scripttest.csv
ForEach ($rowObject in $Doc)
{
$fileName = Split-Path -Leaf $rowObject.Path
Copy-Item -Path $rowObject.Path `
-Destination "\\Server\Folder\$((new-guid).guid.replace('-',''))_$fileName"
}
Note how Split-Path -Leaf is used to extract the filename, including extension, from the full input path.
If I read your question carefully, you want to:
copy the files listed in the CSV file in the 'File' column.
the new files should have a GUID prepended to the filename
you need a new CSV file where the new filenames are stored for later reference
you want to track any errors and write those to a (log) file
Assuming you have an input CSV file looking something like this:
File,Author,MoreStuff
\\server\sub\folder\file.ext,Someone,Blah
\\server\sub\folder\file2.ext,Someone Else,Blah2
\\server\sub\folder\file3.ext,Same Someone,Blah3
Then below script does hopefully what you want.
It creates new filenames by prepending them with a GUID and copies the files in the CSV listed in column File to some destination path.
It outputs a new CSV file in the destination folder like this:
OriginalFile,NewFile
\\server\sub\folder\file.ext,\\anotherserver\sub\folder\38f7bec9e4c0443081b385277a9d253d_file.ext
\\server\sub\folder\file2.ext,\\anotherserver\sub\folder\d19546f7a3284ccb995e5ea27db2c034_file2.ext
\\server\sub\folder\file3.ext,\\anotherserver\sub\folder\edd6d35006ac46e294aaa25526ec5033_file3.ext
Any errors are listed in a log file (also in the destination folder).
$Destination = '\\Server\Folder'
$ResultsFile = Join-Path $Destination 'Copy_Results.csv'
$Logfile = Join-Path $Destination 'Copy_Errors.log'
$Doc = Import-CSV C:\Temp\scripttest.csv
# create an array to store the copy results in
$result = #()
# loop through the csv data using only the column called 'File'
ForEach ($fileName in $Doc.File) {
# check if the given file exists; if not then write to the errors log file
if (Test-Path -Path $fileName -PathType Leaf) {
$oldBaseName = Split-Path -Path $fileName.Path -Leaf
# or do $oldBaseName = [System.IO.Path]::GetFileName($fileName)
$newBaseName = "{0}_{1}" -f $((New-Guid).toString("N")), $oldBaseName
# (New-Guid).toString("N") returns the Guid without hyphens, same as (New-Guid).Guid.Replace('-','')
$destinationFile = Join-Path $Destination $newBaseName
try {
Copy-Item -Path $fileName -Destination $destinationFile -Force -ErrorAction Stop
# add an object to the results array to store the original filename and the full filename of the copy
$result += New-Object -TypeName PSObject -Property #{
'OriginalFile' = $fileName
'NewFile' = $destinationFile
}
}
catch {
Write-Error "Could not copy file to '$destinationFile'"
# write the error to the log file
Add-content $Logfile -Value "$((Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) - ERROR: Could not copy file to '$destinationFile'"
}
}
else {
Write-Warning "File '$fileName' does not exist"
# write the error to the log file
Add-content $Logfile -Value "$((Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) - WARNING: File '$fileName' does not exist"
}
}
# finally create a CSV with the results of this copy.
# the CSV will have two headers 'OriginalFile' and 'NewFile'
$result | Export-Csv -Path $ResultsFile -NoTypeInformation -Force
Thank you to everyone for the solutions. All of them worked and worked well. I chose Theo as the answer for the fact that his solution solved the error logging and stored all the new renamed files with GUID_File.ext new to the existing CSV info.
Thank you all.
i am trying to loop through all files no matter the type, in a folder, and change a string with one that is input by the user..
i can do this now, with the code below, but only with one type of file extension..
This is my code:
$NewString = Read-Host -Prompt 'Input New Name Please'
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$InputFiles = Get-Item "$scriptPath\*.md"
$OldString = 'SolutionName'
$InputFiles | ForEach {
(Get-Content -Path $_.FullName).Replace($OldString,$NewString) | Set-Content -Path $_.FullName
}
echo 'Complete'
How do i loop through the files, no matter the extension ?
so no matter if it is a md, txt or cshtml or some other, it will replace the string as instructed.
To get all the files in a folder you can get use Get-ChildItem. Add the -Recurse switch to also include files inside of sub-folders.
E.g. you could rewrite your script like this
$path = 'c:\tmp\test'
$NewString = Read-Host -Prompt 'Input New Name Please'
$OldString = 'SolutionName'
Get-ChildItem -Path $path | where {!$_.PsIsContainer} | foreach { (Get-Content $_).Replace($OldString,$NewString) | Set-Content -Path $_.FullName }
this will first get all the files from inside the folder defined in $path, then replace the value given in $OldString with what the user entered in when prompted and finally save the files.
Note: the scripts doesn't make any difference regarding if the content of the files changed or not. This will cause all files modified date to get updated. If this information is important to you then you need to add a check to see if the files contains the $OldString before changing them and saving.
Well as the title states i made a PowerShell script that had such a horrible performance that it overextended the server ressources and crashed it.
The script reads an entire.xml file and appends a text at the beginning and at the end of the file. Also it changes the name of the file accroding to what is located in my filename.txt.
The .xml files are around 500 MB big and have over 4.7 million rows. Is there a way, that i don't have to read the entire file but not loose information?
function start-jobhere([scriptblock]$block){
start-job -argumentlist (get-location),$block { set-location $args[0]; invoke-expression $args[1] }
}
$handler_button1_Click= {
Try{
$job3 = start-jobhere {
#Text that should be at filebeginning
#('<?xml version="1.0" encoding="UTF-8"?>
<ids:ControlInfo>
<ids:ObjectFormat>CSV</ids:ObjectFormat>
<ids:SeparatorForCSV>;</ids:SeparatorForCSV>
</ids:ControlInfo>
<ids:BatchDeltaUntil></ids:BatchDeltaUntil>
</ids:BatchInfo>
</ids:Header>
<ids:Body>'
) + (get-content ZUB_Lokalisation.xml) | set-content ZUB_Lokalisation.xml
#Text that should be at file end
Add-Content ZUB_Lokalisation.xml -Value "</ids:Body>`n</ids:SimpleOperation>"
#Information that goes into the header of the file but has to be extracted from the filename inside a .txt
$filename = Select-String filename.txt -Pattern "Lokalisation"
$nameoffile = [System.IO.Path]::GetFileName($filename)
$split = $nameoffile.split('_')
$finalid = $split[5]
$content = Get-Content ZUB_Lokalisation.xml
$content[8] = ' <ids:BatchInfo ids:BatchID="{0}">' -f $finalid
$content | Set-Content ZUB_Lokalisation.xml
#Rename the file
Rename-Item ZUB_Lokalisation.xml -NewName $filename}
}catch [System.Exception]{zeigen
[System.Windows.Forms.MessageBox]::Show("ZUB_LOK_ERROR", "ERROR")}
}
Get-Job | Wait-Job | Where State -eq "Running"
}
Create files containing the start and end fragments that you want.
Then run this in a dos window or batch file:
COPY StartFile.TXT + YourXMLFile.TXT + EndFile.TXT OutputFile.TXT
This sticks the three files together and saves them as OutputFile.TXT