How to do looping to rename and read a file using PowerShell? - 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

Related

replacing files names with split output

I am trying to use PowerShell to read filenames from a dir;
then within a for loop:
split names using a delimiter; store desired output in a new variable. Now I want to replace the original filenames in the directory with this new variable. So far I have gathered the following with the expected outputs shown:
$files = Get-ChildItem -Path C:\Test
write-output $files
Directory: C:\Test
1_N04532L_LEFT.JPG
2_N04532R_RIGHT.JPG
code continues
foreach ($file in $files)
{
$nameArray = $file -split "_"
$newName = $nameArray[1]
write-output $newName
}
N04532L
N04532R
Any Ideas on how to accomplish this. I am not a programmer and there is lots of data on this, but it's not working for me.
As both commenters already explained, there is the Rename-Item cmdlet for renaming files.
Since this cmdlet can take a scriptblock in its NewName parameter, you can use that to create a new filename.
# adding switch -File makes sure you do not also try to rename subfolders
$files = Get-ChildItem -Path 'C:\Test' -File
foreach ($file in $files) {
$file | Rename-Item -NewName { '{0}{1}' -f ($file.BaseName -split '_')[1], $file.Extension }
}
You can shorten this by piping the results from Get-ChildItem trhough one-by-one to the Rename-Item cmdlet.
Because we're piping the FileInfo objects here, we can make use of the $_ automatic variable
# enclose the Get-ChildItem cmd in brackets so this will enumerate the files to completion
# before passing them on to te Rename-Item cmdlet.
# if you don't, files you already have renamed could be picked up and processed again..
(Get-ChildItem -Path 'C:\Test' -File) |
Rename-Item -NewName { '{0}{1}' -f ($_.BaseName -split '_')[1], $_.Extension }
Note: when renaming files, you can always run into naming collisions, upon which you will receive an exception

Powershell - Gather file content and names recursively and output CSV

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

Trying to create an array of filenames

I am trying to use the PSWritePDF module to merge pdfs. I have about 64 folders and each of them have about 20+ files that need to be merged. In the end, I would have 64 pdfs, each containing the merged files from each of the 64 folders. I have already written some code but I am struggling to create an array of file names that I can pass to the Merge-PDF function. I know the first part of this code is redundant, just haven't fixed it yet.
#https://github.com/EvotecIT/PSWritePDF/blob/master/Example/Example03.Merging/Example03.ps1
#This gives me the 64 folder names
$folder_NM = Get-ChildItem -Path \\main_directory\CURRENT |
Where-Object {$_.PSIsContainer} |
Foreach-Object {$_.Name}
#This iterates through the 64 folders
foreach ($X IN $folder_NM)
{
#this grabs each of the 64 directories
$main_path = join-path -path \\main_directory\CURRENT -ChildPath $X
#This grabs the names of the pdfs in each folder
$file_names = Get-ChildItem $main_path |
ForEach-Object {$_.Name}
#This is grabbing each file in the folder and giving me the formatted string I need to pass to Merge-PDF. i.e. C:\\User\Current\pdf.1
foreach($Y in $file_names){
$idv_files = join-path -path $main_path -ChildPath $Y
#This is where I am stuck. I am trying to create an array with each filename comma separated. This currently just overwrites itself each time it goes through the loop.
$arr = $idv_files-join','
#This is needed for mergePDF
$OutputFile = "$maindirectory\TESTING\$X.pdf"
#This only puts the most recent file in the output file. Thus the need for an array of file names.
Merge-PDF -InputFile $arr -OutputFile $OutputFile
#Debugging
#Write-Host $arr
}
}
Specifically, this is where I am struggling. I am getting the correct files in $idv_files and if I use those in Merge-PDF then I just get a PDF with the one file that was processed last. I think I just need them comma separated and all put into the same array so that Merge-PDF will merge them all together.
foreach($Y in $file_names){
$idv_files = join-path -path $main_path -ChildPath $Y
#This is where I am stuck. I am trying to create an array with each filename comma separated. This currently just overwrites itself each time it goes through the loop.
$arr = $idv_files-join','
Anything helps. Very new to powershell!
Untested but, if the function takes [string[]] as input as in my comment, this should get you a MERGED PDF.pdf on each folder.
I would recommend you to test this with a few folders containing pdf files on your local host before trying with your FS.
# Get the Directories
$folder_NM = Get-ChildItem -Path \\main_directory\CURRENT -Directory
#This iterates through the 64 folders
foreach ($dir IN $folder_NM)
{
# This gets you the array of PDF Files
$file_names = Get-ChildItem $dir.FullName -Filter *.pdf -File |
Sort-Object Name
# Define the output file for Merged PDF
$OutputFile = Join-Path $dir.FullName -ChildPath 'MERGED PDF.pdf'
# If Merge-PDF takes [string[]] as input, this should work
Merge-PDF -InputFile $file_names.FullName -OutputFile $OutputFile
}
It appeared that you wanted the merged .pdf file to be the subdirectory name + '.pdf'. Perhaps I misunderstood. This is also UNTESTED, but might do what you want. Using the current Windows PowerShell 5.1 or any PowerShell Core, testing for .PSIsContainer is not necessary. Get-ChildItem supports -File and -Directory switches.
[CmdletBinding()]
param ()
$RootDir = '\\main_directory\CURRENT'
# Get the subdirectory list.
Get-ChildItem -Directory -Path $RootDir |
# Process each subdirectory.
ForEach-Item {
# Create an array of the .pdf files to be merged.
$file_names = (Get-ChildItem -File -Path $_.FullName -Filter '*.pdf').FullName
#This is needed for mergePDF
$OutputFile = Join-Path -Path $RootDir -ChildPath $($_.Name + '.pdf')
Write-Verbose "OutputFile is $OutputFile"
Merge-PDF -InputFile $file_names -OutputFile $OutputFile
}

How to backup these files into specific folders using powershell

I've finally have given up googling and come here out of desperation. Go easy on me I'm fairly new to Powershell.
So, the objective of the code below was to first look through the source folder, then read through each .zip file and move to the directory specified by the value in the hashtable. Unfortunately, this is not how they want it to work anymore.
Now I need to retain the parent folder from source: for example "DAL" and then create the proceeding folders based on the file names and finally move each .zip to its file specified folder. Also, it needs to go through each folder under source which will be at least 20 other folders with a unique 3 character names.
$srcRoot = "C:\Cloud\source\dal"
$dstRoot = "C:\Cloud\Destination"
##$map = #{}; dir -recurse | ? { !$_.psiscontainer} | % { ##$map.add($_.name,$_.PSChildName) }
# DAT and DEV will have to be excluded from folder creation
$map = {
#AEODDAT_201901 = "AEOD\2019\01"
#AEOMDEV_201902 = "AEOM\2019\01"
#AEOYDAT_201902 = "AEOY\2019\01"
}
$fileList = Get-ChildItem -Path $srcRoot -Filter "*.zip*" -File -Force -Recurse
foreach ($file in $fileList)
{
#Go through each file up to mapped string
$key = $file.BaseName.Substring(0,14)
if ($key -in $map.Keys)
{
$fileName = $file.Name
$dstDir = Join-Path -Path $dstRoot -ChildPath $map[$key]
#create direcotory if not in path
if (-not (Test-Path -Path $dstDir))
{
mkdir -Path $dstDir
}
Write-Verbose "Moving $($file.FullName)"
if (Test-Path -Path (Join-Path -Path $dstDir -ChildPath $fileName))
{
#Write error if name exists
Write-Error -Message "File $fileName already exists at $dstDir"
#move path
} else {
Move-Item -Path $($file.FullName) -Destination $dstDir
}
}
}
So C:\Cloud\source\DAL\AEODDAT20190101.zip should create folders in C:\Cloud\Destination\DAL\AEOD\2019\01\AEODDAT20190101.zip would be my desired output.
Welcome, Matt! (no pun intended) One of the habits I have in similar situations with destination folders is to Set-Location $dstRoot and create folders from the relative path. You can execute New-Item with the relative path and the syntax is simpler. For example, your If statement could look like this and it would work the same way (with a slightly different error message):
if ($key -in $map.Keys){
Set-Location $dstRoot
New-Item -ItemType Directory $map[$key] -ErrorAction Ignore #won't raise an error if it exists
Write-Verbose "Moving $($file.FullName)"
#this will raise an error if the file already exists, unless you specify -Force
Move-Item "$($file.FullName)" $map[$key]
}
EDIT: Found 2 issues.
$map is a Hashtable literal that should be preceded with #:
$map = #{
AEODDAT20190101 = "AEOD\2019\01"
You were missing the last character of the base file name by taking only the first 14 characters. AEODDAT2019010 didn't match AEODDAT20190101. This should fix it:
$key = $file.BaseName.Substring(0,15)

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.