Powershell - Gather file content and names recursively and output CSV - powershell

I have multiple XML files in different subfolders that I am looking to use Powershell to convert them to CSV and output them using the original file name into a different folder. Example of my code:
$xmlfilepaths = Get-ChildItem -Path "\\path\to\files\" -Recurse | where {$_.Name -like '*.xml'}
foreach ($xmlpath in $xmlfilepaths.FullName)
{
$xmlcontent = Get-Content -Path $xmlpath
}
This part works, but what I would like to do is output these CSV's to another folder, and keep the filename of the original XML file as the file name. Example:
\path\to\files\alpha\testing123.xml -> \path\to\output\files\testing123.csv
\path\to\files\bravo\production789.xml -> \path\to\output\files\production789.csv
My problem is once I am in the 'foreach' how do I "pull" the file name from either the $xmlpath or $xmlfilepaths variable and pass it along so that my file name input is the same as my filename output?

The original file name is contained in the Name property of each object stored in $xmlfilepaths.
Renaming your variables to something more accurate might help make it more obvious what to do:
$xmlFiles = Get-ChildItem -Path "\\path\to\files\" -Recurse -File -Filter *.xml
foreach ($xmlFile in $xmlFiles)
{
$xmlContent = Get-Content -Path $xmlFile.FullName
# !!!
# do what you need to do to convert the XML to CSV
# !!!
# construct new file name and write output file
$newFileName = $xmlFile.Name -replace '.xml$','.csv'
$outputPath = Join-Path '\path\to\output\files' $newFileName
$xmlContent |Set-Content $outputPath
}

To get new filename use following :
$filename = "\path\to\files\alpha\testing123.xml"
$basename = $filename.Substring($Filename.LastIndexOf("\") + 1)
$basename = $basename.SubString(0, $basename.LastIndexOf("."))
Write-Host $basename
$newName = "\path\to\output\files\" + $basename + ".csv"
Write-Host $newname

Related

powershell wildcard character in a control file list

I have a powershell script that uses a control file to archive files:
# Takes the information from $controlfile and moves the files to the archive folder**
foreach ($line in get-content $controlfile)
{
$file = $controlfile
$split = $line.split("|")
$archive_path = $split[0]
$source_path = $split[1]
$basename = $split[2]
$extention = $split[3]
$daystoleave = $split[4]
# move the files to the archive folder
Get-ChildItem $source_path -Filter "$basename*" -Force -Recurse | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays($daystoleave)} | Move-Item -Destination $archive_path
Get-ChildItem $archive_path -Filter "$basename*" -Force -Recurse | % { rename-item –path $_.Fullname –Newname (("$basename") + ("_") + ($_.LastWriteTime.toString("MM.dd.yy_HH.mm.ss")) + (".$extension")) }
}
Control file example:
\server\Apps\Archive-Requests\|\server\Apps\requests\|?||0
\server\Apps\Archive-Requests\|\server\Apps\requests\|*||0
Above examples don't work because of the ? or * character in the file name split of the control file.
\server\Apps\Archives\Archive_Daily_Reports\|\apps\FICS\Daily Reports\|audit|fics|0
Above example works because "audit" is the beginning of the file name.
This works fine when I want to capture and use the first part of the original file name (ex. SAD123.txt - capture SAD|add LastWriteTime date to name|add extension|leave some file(s) in original folder ($daystoleave).
But if I'd like to capture either the entire original name or a middle portion of the name using wildcard characters, I receive "Illegal characters in path." and nothing changes to the end result filename. Is there a wildcard I can use other than * or ? in the control file.
Is it possible to use a wildcard in the control file?

How do I create a new files automatically depend on an existing variable in PowerShell?

I have many file in a folder, I would like to check the existing and matching of the file with variable that I initialize. Then, if the file exit and match, I want to get some information from the files (many file), then create a new file depend on how many file exist and match.
I tried this code, I can check the matching and existing file. I can create a new file and get the information from the file, but I only can create 1 file.
The information that I get from the file, each file is different.
$ID = "123"
$Pre = "ABC"
$Path = "C:\Folder"
$PO = Get-ChildItem -Path $Path
foreach ($File in $PO) {
if (($File.Name -match $ID) -and ($File.Name -match $Pre)) {
Write-Host ">>POfile Found: $File"
} else {
Write-Host ">>Check Again!"
}
}
# CREATE FILE
$Jb_Path = "C:\Folder\Jb"
## GET INFORMATION
$count = 1
$Get_PO = Get-ChildItem -Path $Path\$File -File -Recurse
$POfile = Get-Random -InputObject $Get_PO -Count $count
Write-Host ">>Selected POfile= $POfile"
$FilteredContents = Get-Content $POfile | Where-Object {$_ -like "*;INFO*"}
$Get_INFO = $FilteredContents.Substring(5,2)
## NEW FILE
New-Item -Path $Jb_Path\NEW_$Pre$ID-$Get_INFO.txt -Force
In the section # CREATE FILE you are referencing the variable $File which has the last value iterated in the previous foreach (even if it didn't match the if condition).
Asuming the $Pre is for prefix and comes first in a file name simply do a
Get-ChildItem "$Path\*$Pre*$ID*"
to only get file names for your criteria.
As $File contains only one file name a Get-Random doesn't make sense, especially as it might not contain a line with ;INFO
Assuming the two characters to extract are in front of ;INFO this untested script might do:
$Pre = "ABC"
$ID = "123"
$Path = "C:\Folder"
$Jb_Path= "C:\Folder\Jb"
Get-ChildItem "$Path\*$Pre*$ID*" | Get-Content |
Select-String -Pattern '^.....(..).*;INFO' |
Get-Random | ForEach-Object {
$NewFile = Join-Path $Jb_Path ('NEW_{0}{1}-{2}.txt' -f $Pre,
$ID,$_.Matches.Groups[1].Value)
New-Item -Path $NewFile -ItemType File -Force -WhatIf
}
It will only output what it would do without the -WhatIf parameter.
If no file matching the criteria and RegEx pattern is found it will silently continue.
If my assumptions led me wrong, enhance your question be editing it with more details.

How to batch rename folders and files inside them based on a CSV file with PowerShell?

I need to batch rename 2,000+ folders and then rename the pictures inside these folders with the folder new name + the product name + a sequential number + the ".jpg" extension, all of this based on a CSV file that I've created that look like this:
folder_old_name,folder_new_name,folder_path,product_name
102597,WK240,C:\Users\Elvis\Desktop\Products\WK240,CASIO_DIGITAL_PIANO
Here is an example of a current folder and its content:
102597
CASIODIGITALPIANOFRONT.jpg
CASIODIGITALPIANOSIDE.jpg
CASIODIGITALPIANOWITHBOX.jpg
After the process it must look like this:
WK240
WK240_CASIO_DIGITAL_PIANO_1.jpg
WK240_CASIO_DIGITAL_PIANO_2.jpg
WK240_CASIO_DIGITAL_PIANO_3.jpg
I've managed to rename all folders with the help of this below code but I have no idea on how to include an instruction to rename the files in the way I've described.
$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
Import-Csv "C:\Users\Elvis\Desktop\batch_rename.csv" | ForEach-Object {
$old = $_.folder_old_name
if (Test-Path ($old)) {
$newPath = $_.folder_new_name
ren $old $newPath
}
}
I would appreciate it if someone could help me to do this all at once.
basic steps are:
1. import the csv that contains rename instructions
2. loops through the csv
1. rename the folder to its new name
2. get all files in the folder that was just renamed
3. loop through all the files in that folder
1. construct the new name with data from the csv
2. rename the file
I havn't test this code, but this is basically what it could look like.
$csv = import-csv -path "path\to\csv"
# loop through rows in csv
foreach($row in $csv){
# this assumes folder_old_name is in current working directory
# if its not you can use the Join-Path cmdlet to construct the path.
Rename-Item -Path $row.folder_old_name -NewName $row.folder_new_name
# get files and start filename construction
$files = Get-ChildItem -Path $row.folder_new_name
$fileIncrement = 1
$FileBaseName = $row.folder_new_name + '_' + $row.product_name
# loop through files
foreach($file in $files){
# increment filename
$NewFileName = $FileBaseName + '_' + $fileIncrement + $file.Extension
# rename file
Rename-Item -Path $file.FullName -NewName $NewFileName
$fileIncrement++
}
}
This should take you part of the way. It's probably better to do things in two stages anyway. Renaming the files depends on the folders having the new names.
$csv = import-csv input.csv
foreach ($line in $csv) {
$product = $line.product_name
$dir = $line.folder_new_name
$path = $line.folder_path
get-childitem $path\*.jpg |
foreach {$i=1} {
Rename-Item $_ -NewName ($dir + '_' + $product + '_' + $i++) -whatif
}
}

How to do looping to rename and read a file using PowerShell?

I have many files in a folder with the same extension file. I want to rename the file one by one then do the other process, which is Proc_After_Rename. In this process, I will read some information of the file. In this process, I want to read the information of the file one by one based on the previous process to rename the extension file name. After I finish do the process, then I pick again the file to rename and do the process.
For now, I can rename the file, but it rename it all the files directly before I do the other process. ANf when I go to this process Proc_After_Rename, I read the information for all the file, because all the file already rename the extension. Anyone can help, please
UPDATED
Function Proc_After_Rename
{
$Path = "C:\Users\SS\PowerShell\"
Write-Host "Do some process with .pro file"
$Job_Info = Get-ChildItem -Path "$store\*.ini" -File -Force
& $Path\UIni.exe $Job_Info AGM CRM AGM_CUR_CRM AGM_CUR_CRM.CMD #this how I read the .ini file
start-sleep -s 1
$Read_AGM_CUR_CRM = Get-Content .\AGM_CUR_CRM.CMD
$a_AGM_CUR_CRM,$b_AGM_CUR_CRM = $Read_AGM_CUR_CRM -split "="
$b_AGM_CUR_CRM
Pick_file
}
Function Pick_file
{
$WKFD= "C:\Users\SS\PowerShell\"
$store = "$WKFD\GM"
$files = #(Get-ChildItem -Path "$store\*.txt")
Foreach ($file in $files)
{
# Check file existence
if (Test-Path -Path $file -PathType Leaf)
{
# Get file name from object path file $file
$file_name = #(Get-ChildItem -Path "$file" -Name)
# Replace the .cue with .pro
$new_name = $file_name -replace ".txt", ".ini"
# Rename the file
Rename-Item -Path $file -NewName "$new_name"
}
Proc_After_Rename
}
}
$A = Pick_file
With the Get-ChildItem cmdlet, you can iterate the results easily by directly piping them through to a Foreach-Object. Inside that loop, every file found is a FileInfo object, represented by the automatic variable $_.
Using the -Filter parameter the below code gets only files with a *.txt extension and by adding the -File switch you only recieve FileInfo objects, not Directory objects.
If I understand the question correctly, you want to first rename each *.txt file to *.ini and then do some more stuff with the renamed file. This should do it:
$store = "C:\Users\HH"
Get-ChildItem -Path $store -Filter '*.txt' -File | ForEach-Object {
# the automatic variable '$_' here represents a single FileInfo object in the list.
# you don't need to test if the file exists, if it doesn't, Get-ChildItem would not return it.
# create the new name for the file. Simply change the extension to '.ini'
$newName = '{0}.ini' -f $_.BaseName
# rename the file and get a reference to it using the -PassThru parameter
$renamedFile = $_ | Rename-Item -NewName $newName -PassThru
# for testing/proof:
# remember that the '$_' variable now has old file name info.
Write-Host ("File '{0}' is now renamed to '{1}'" -f $_.FullName, $renamedFile.FullName)
# now do the rest of your processing, using the $renamedFile FileInfo object.
# you can see what properties and methods a FileInfo object has here:
# https://learn.microsoft.com/en-us/dotnet/api/system.io.fileinfo?view=netframework-4.8#properties
# to get the full path and filename for instance, use $renamedFile.FullName
# ........ #
}
Hope that helps
# Rename the file
Rename-Item -Path $file -NewName "$new_name"
# path of the renamed file
$new_path_file = "$store\$new_name"
# This is the process after rename the file
# ........ #
#Put your process here and make sure you reference the new file, as long as its in
#the foreach you are good.
}
}
One problem with your code is the Get-ChildItem inside Proc_After_Rename. This presents UIni with a list of files instead of a file. I have tried to fix this problem by reworking your code, and sliding part of Proc_After_Rename into Pick_File. I haven't tested any of this, but I hope it gives you a better idea of how to organize your code.
If I were writing this from scratch, I would use pipelines.
Function Pick_file
{
$WKFD= "C:\Users\SS\PowerShell\"
$store = "$WKFD\GM"
$files = #(Get-ChildItem -Path "$store\*.txt")
Foreach ($file in $files)
{
# Check file existence
if (Test-Path -Path $file -PathType Leaf)
{
# Get file name from object path file $file
$file_name = #(Get-ChildItem -Path "$file" -Name)
# Replace the .cue with .pro
$new_name = $file_name -replace ".txt", ".ini"
# Rename the file
Rename-Item -Path $file -NewName "$new_name"
$new_file_name = $file.fullname
& $Path\UIni.exe $new_file_name AGM CRM AGM_CUR_CRM AGM_CUR_CRM.CMD
#this how I read the .ini file
start-sleep -s 1
$Read_AGM_CUR_CRM = Get-Content .\AGM_CUR_CRM.CMD
$a_AGM_CUR_CRM,$b_AGM_CUR_CRM = $Read_AGM_CUR_CRM -split "="
$b_AGM_CUR_CRM
}
}
}
$A = Pick_file

Renaming a file, while adding +01 at the end

I am currently taking a CSV File and renaming it to a .txt file. I Currently save the name as yyyymmdd.txt, I am trying to check the folder for the txt file and append "-01" to the file. (yyyy-mm-dd-01.txt,..-02,..-03,..) each time the script checks it should add 01 to the file. My current problem is the file gets renamed to yyyymmdd.txt01 and if it checks it just puts yyyymmdd.txt11
I've tried a few ways, but have failed to succeed.
$filevalue = 'C:\test\test_notepad.csv'
$path = 'C:\test\'
$file = 'test.txt'
$file2 = 'test_notepade.csv'
$date = Get-Date -Format "yyyy-mm-dd-"
$filename = "$date.txt"
Rename-Item $filevalue $filename
$originalFiles = Get-ChildItem "C:\test\text" -Filter *.txt
$x = 1
ForEach ($originalFile in $originalFiles) {
$x++
Rename-Item -Path $originalFile.FullName -NewName ($originalFile.Name -replace "^", "1")
}
here's a way to do what you want. the stuff before the ForEach-Object is to make a testing file - and to ensure there is only the one since i didn't include any error checking. [grin]
what it does ...
iterates thru 17 times to test the code
gets the file info
splits the .BaseName on the unique _ character
grabs the last item in the resulting array
converts that number string to an [int]
increments it
pads it to two digits
replaces the old seq number in the file name with the new one
renames the file
here's the code ...
$Today = Get-Date -Format 'yyyy-MM-dd'
# delete any existing test file
$Null = Remove-Item -Path "$env:TEMP\$($Today)_*.txt" -ErrorAction SilentlyContinue
# create a file to work with
$Null = New-Item -Path $env:TEMP -Name "$($Today)_07.txt" -ItemType File -ErrorAction SilentlyContinue
# pretend to access the listing 17 times
1..17 |
ForEach-Object {
$FileInfo = Get-ChildItem -LiteralPath $env:TEMP -Filter "$Today*.txt"
$OldSeqNumber = $FileInfo.BaseName.Split('_')[-1]
$NewSeqNumber = '{0:D2}' -f ([int]$OldSeqNumber + 1)
$NewFileName = $FileInfo.Name.Replace("_$OldSeqNumber", "_$NewSeqNumber")
Rename-Item -LiteralPath $FileInfo.FullName -NewName $NewFileName
}
original file name = 2019-05-07_07.txt
last new file name = 2019-05-07_24.txt