I currently have a script that will loop through a group of files in a folder - open each one up and let me enter a new file name and then rename it. I am then trying to create a CSV file with the old name in one column and the new name of the file in the second column.
#[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
Add-Type -AssemblyName Microsoft.VisualBasic
$folderpath = 'C:\Scans\docs\' '#
$items = Get-ChildItem -Recurse $folderpath *.pdf
$newFileName = ""
$counterID = 0
$amountOfScans = $items.Length
foreach( $i in $items) {
Start-Process ((Resolve-Path ("$folderpath$i")).Path)
Start-Sleep -Seconds 1
$counterID++
$newFileName = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the new file name $counterID / $amountOfScans :", $i)
Stop-Process -Name "Acro*"
Add-Content -Path 'C:\Scans\fileNames.csv' -Value "$i","$newFileName "
Rename-Item $i.FullName ("$newName.pdf")
}
This does work but it outputs into the csv only in the first column like:
old file name 1
new file name 1
old file name 2
new file name 2
How do make this output:
old file name 1 | new file name 1
old file name 2 | new file name 2
You're passing an array. A CSV is nothing more than a text file, so you want to pass the entire line as a single string.
Add-Content -Path 'C:\Scans\fileNames.csv' -Value "`"$i`",`"$newFileName`""
Related
I am new to PowerShell and have an issue. I need a PowerShell script to modify the first line of a .dat file within a zip folder. Then the zip folder will be need to be renamed but the file within has to keep the same name.
Any help will be appreciated!
I have the following code. It is reading the latest zip file from one directory and copying it into a new worker2 directory. This is working fine. I am trying to open the file and modify the first line. However the file is blank so the code is not copying into the file.
$today = get-date -Format yyyyMMdd
robocopy "C:\Tcc_Touchpoints\Tcc_Touchpoints\data\fusion\Worker\"
"C:\Tcc_Touchpoints\Tcc_Touchpoints\data\fusion\Worker2\" /s
/maxage:$today
$file = gci C:\Tcc_Touchpoints\Tcc_Touchpoints\data\fusion\Worker2\ | sort
LastWriteTime | select -last 1
$file2 = "C:\Tcc_Touchpoints\Tcc_Touchpoints\data\fusion\Worker2\" + $file
$zipfileName = $file2
$fileToEdit = "Worker.dat"
$path = $zipfileName + '\' + $fileToEdit
$contents = Get-Content $fileToEdit #-path $path
$contents
Add-Type -assembly System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::Open($zipfileName,"Update")
#$zip = [System.IO.Compression.ZipFile]::Open($path,"Update")
$robotsFile = $zip.Entries.Where({$_.name -eq $fileToEdit})
$desiredFile = [System.IO.StreamWriter]($robotsFile).Open()
$desiredFile.BaseStream.SetLength(0)
$desiredFile -replace 'SET PURGE_FUTURE_CHANGES Y','SET
PURGE_FUTURE_CHANGES N'
$desiredFile.Write($contents)
$desiredFile.Flush()
$desiredFile.Close()
# Write the changes and close the zip file
$zip.Dispose()
Write-Host "zip file updated"
Your code is not far off. The following edits a single plain text entry in a ZIP file in-place.
Be sure to use the correct text encoding (i.e. the encoding your Worker.dat actually is in). Not specifying the encoding when working with text files will lead to mangled data at some point, always play it safe there.
using namespace System.Text
using namespace System.IO
using namespace System.IO.Compression
Add-Type -Assembly System.IO.Compression.FileSystem
$fileToEdit = "Worker.dat"
$fileEncoding = [Encoding]::UTF8
$zipFileName = "C:\path\to\your\file.zip"
$zip = [ZipFile]::Open($zipfileName, "Update")
$entry = $zip.Entries.Where({$_.name -eq $fileToEdit}) | Select-Object -First 1
$reader = [StreamReader]::new($entry.Open(), $fileEncoding)
$currentText = $reader.ReadToEnd()
$reader.Dispose()
$newText = $currentText -replace "\}$"," }"
$writer = [StreamWriter]::new($entry.Open(), $fileEncoding)
$writer.Write($newText)
$writer.Dispose()
$zip.Dispose()
This does not set the last modified date of the ZIP entry. You can (propably) do this by setting a new value to the $entry.LastWriteTime property.
For over 800 files I need information that's in the file name to be included in the contents of the text file (actually .md files).
The file names are always of the same structure, something like 0000-title-text-1-23.md; only the 1-23 part changes (and that is the information I need).
I am a novice as it comes to scripting, but I figured out that this should be an easy task for PowerShell — yet I don't get it working the way I want. What did come closest:
Get-Childitem "C:\PATH\*.md" | ForEach-Object{
$fileName = $_.BaseName
Add-Content -Path .\*.md -Value $fileName
}
But that adds all file names in the directory, not just the one from the file itself.
What am I doing wrong?
Use this code to do what you exactly want,
it will get the last 2 parts of your filename and
put it in the beginning of your file content.
Get-Childitem "C:\PATH\*.md" | ForEach-Object{
$fileNameParts = ($_.BaseName).split('-')
$info = $fileNameParts[-2] + '-' + $fileNameParts[-1]
$info + (Get-Content $_ -Raw) | Set-Content $_
}
Something like this would work although it does add the content to the end of the file:
#Get all the .txt or .md files in your location
Get-ChildItem -Filter "*.txt" | Foreach-Object{
#Get the base name of the file
$baseName = $_.BaseName
#Split the base name
$array = $baseName -Split '-'
#Put the third and fourth element in the array into a separate variable
#This will be added to the file
$addToFile = $array[3] + '-' + $array[4]
#Add the $addToFile variable to the file
Add-Content $_.FullName -Value $addToFile
}
I need help with PowerShell.
I will have to start renaming files in a weekly basis which I will be renaming more than 100 a week or more each with a dynamic name.
The files I want to rename are in a folder name Scans located in the "C: Documents\Scans". And they would be in order, to say time scanned.
I have an excel file located in "C: Documents\Mapping\ New File Name.xlsx.
The workbook has only one sheet and the new names would be in column A with x rows. Like mention above each cell will have different variables.
P Lease make comments on your suggestions so that I may understand what is going on since I'm a new to coding.
Thank you all for your time and help.
Although I agree with Ad Kasenally that it would be easier to use CSV files, here's something that may work for you.
$excelFile = 'C:\Documents\Mapping\New File Name.xlsx'
$scansFolder = 'C:\Documents\Scans'
########################################################
# step 1: get the new filenames from the first column in
# the Excel spreadsheet into an array '$newNames'
########################################################
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$workbook = $excel.Workbooks.Open($excelFile)
$worksheet = $workbook.Worksheets.Item(1)
$newNames = #()
$i = 1
while ($worksheet.Cells.Item($i, 1).Value() -ne $null) {
$newNames += $worksheet.Cells.Item($i, 1).Value()
$i++
}
$excel.Quit
# IMPORTANT: clean-up used Com objects
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
########################################################
# step 2: rename the 'scan' files
########################################################
$maxItems = $newNames.Count
if ($maxItems) {
$i = 0
Get-ChildItem -Path $scansFolder -File -Filter 'scan*' | # get a list of FileInfo objects in the folder
Sort-Object { [int]($_.BaseName -replace '\D+', '') } | # sort by the numeric part of the filename
Select-Object -First ($maxItems) | # select no more that there are items in the $newNames array
ForEach-Object {
try {
Rename-Item -Path $_.FullName -NewName $newNames[$i] -ErrorAction Stop
Write-Host "File '$($_.Name)' renamed to '$($newNames[$i])'"
$i++
}
catch {
throw
}
}
}
else {
Write-Warning "Could not get any new filenames from the $excelFile file.."
}
You may want to have 2 columns in the excel file:
original file name
target file name
From there you can save the file as a csv.
Use Import-Csv to pull the data into Powershell and a ForEach loop to cycle through each row with a command like move $item.original $item.target.
There are abundant threads describing using import-csv with forEach.
Good luck.
I'm trying to rename files that match values in column one of a csv adding the value in column 3 to the beginning of the file name leaving the rest of the file name intact. Here is what I have so far. I cant seem to figure out the Rename-Item.
# Common Paths
$PathRoot = "C:\Temp\somefiles" #Where the files are to rename
# Get csv file
$ClientAccounts = Import-CSV -path "\\server\some\path\to\csv\file.csv"
# Get each file and rename it
ForEach($row in $ClientAccounts)
{
$CurrentClientTaxId = $row[-1].TaxId
$CurrentClientName = $row[-1].ClientName
#loop through files
$FileExists = Test-Path -Path "$PathTotal\*$CurrentClientLB_Number*" #See if there is a file.
If ($FileExists -eq $true) #The file does exist.
{
#ReName File
Rename-Item -Path $PathRoot -NewName {$CurrentClientName + " " + $_.name}
}
}
Lets suppose your CSV file looks similar to this:
"LB_Number","TaxId","ClientName"
"987654","12345","Microsoft"
"321456","91234","Apple"
"741852","81234","HP"
Column 1 has the portion of the existing file name to match
Column 3 has the client name you want to prepend to the file name
Then your function could be something like this:
# Common Paths
$PathRoot = "C:\Temp\somefiles" # Where the files are to rename
# Get csv file
$ClientAccounts = Import-CSV -path "\\server\some\path\to\csv\file.csv"
# Loop through all clients in the CSV
foreach($client in $ClientAccounts) {
$CurrentClientLB_Number = $client.LB_Number
$CurrentClientTaxId = $client.TaxId # unused...??
$CurrentClientName = $client.ClientName
# get the file(s) using wildcards (there can be more than one)
# and rename them
Get-ChildItem -Path "$PathRoot\*$CurrentClientLB_Number*" -File | ForEach-Object {
$_ | Rename-Item -NewName ($CurrentClientName + " " + $_.Name)
}
# Curly braces work also, although this is not very common practice:
# Get-ChildItem -Path "$PathRoot\*$CurrentClientLB_Number*" -File |
# Rename-Item -NewName { ($CurrentClientName + " " + $_.Name) }
}
I use the -File parameter with Get-ChildItem so the function will only return files; not directories. If you are using PowerShell version 2.0, you need to replace that with | Where-Object { !$_.PSIsContainer }.
I have multiple comma delimited text files (each has over 1 million rows). Also I have one file called "delete.log" .
Structure of delete.log file is like this:
STRING1
STRING2
STRING3
.
.
STRING N
Structure one of many txt files is like this:
name1.txt :
text1,text2,text3,text4,STRING1 "entire row will be deleted"
text1,text2,text3,text4,STRING1 "entire row will be deleted"
text1,text2,text3,text4,text5
text1,text2,text3,text4,STRING2 "entire row will be deleted"
text1,text2,text3,text4,STRING1 "entire row will be deleted"
text1,text2,text3,text4,text5
I'm looking for a effective way how to read each text string from file "delete.log" and if there is a match in row 5 with text string from delete.log file,
entire row will be deleted.
Also if there is a text file in a folder with a file name from delete.log
like STRING1.txt, file will be deleted.
This code below just delete entire row from multiple text files if text string in column 5 is STRING1
$paths = Get-ChildItem '.\' -Filter '*.txt'
ForEach ($path in $paths) {
$pathtmp = "$path.tmp"
$sr = New-Object -TypeName System.IO.StreamReader -ArgumentList $path
$sw = New-Object -TypeName System.IO.StreamWriter -ArgumentList $pathtmp
Do {
$line = $sr.ReadLine()
$Column = $line.split(",")
If ($Column[4] -ne "STRING1") {
$sw.WriteLine($line)
}
} Until ( $sr.EndOfStream )
$sr.close()
$sw.close()
Remove-Item $path
Rename-Item $pathtmp $path
}
Just read the files using the Get-Content cmldet, use the Select-String cmdlet to filter the entries and finally write the entries back using Set-Content:
$deleteLog = Get-Content 'delete.log'
$name1 = Get-Content 'name1.txt'
$name1 | Select-String -NotMatch $deleteLog | Set-Content 'name1.txt'
Content of name1.txt now:
text1,text2,text3,text4,text5
text1,text2,text3,text4,text5