I have a base folder as:
D:\St\Retail\AMS\AMS\FTP-FromClient\AMS
It contains various folders of dates:
2022-04-01
2022-04-02
...
...
2022-02-02
2021-05-05
2019-04-12
And each of these folders contains own files inside the folder. So, I want to retrieve all the filename inside the folder if it has 2022-04. So if the folder has '2022-04' as the base name ,I need to retreive all the file inside the folder like '2022-04-01','2022-04-02','2022-04-03'. The way I tried is:
cls
$folerPath = 'D:\St\Retail\AMS\AMS\FTP-FromClient\AMS'
$files = Get-ChildItem $folerPath
[System.Collections.ArrayList]$data = #()
foreach ($f in $files) {
$a = Get-ChildItem $f.FullName
foreach ($inner in $a) {
echo $inner.FullName
$outfile = $inner.FullName -match '*2022-04*'
$datepart = $inner.FullName.split('\')[-1]
if ($outfile) {
$data.add($datepart + '\' + $inner.Name.Trim())
}
}
}
My final $data may contains like this:
2022-04-01/abc.txt
2022-04-02/cde.pdf
2022-04-03/e.xls
You can do this by first collecting the directories you want to explore and then loop over these to get the files inside.
Using a calculated property you can output in whatever format you like:
$folderPath = 'D:\St\Retail\AMS\AMS\FTP-FromClient\AMS'
$data = Get-ChildItem -Path $folderPath -Filter '2022-04*' -Directory | ForEach-Object {
$dir = $_.Name
(Get-ChildItem -Path $_.FullName -File |
Select-Object #{Name = 'FolderFile'; Expression = {'{0}\{1}' -f $dir, $_.Name}}).FolderFile
}
After this, $data would be a string array with this content:
2022-04-01\abc.txt
2022-04-02\cde.pdf
2022-04-03\e.xls
By using wildcards for both directory and file name, you only need a single Get-ChildItem call:
$folderPath = 'D:\St\Retail\AMS\AMS\FTP-FromClient\AMS'
$folderDate = '2022-04'
[array] $data = Get-ChildItem "$folderPath/$folderDate*/*" -File | ForEach-Object{
# Join-Path's implicit output will be captured as an array in $data.
Join-Path $_.Directory.Name $_.Name
}
$data will be an array of file paths like this:
2022-04-01\abc.txt
2022-04-02\cde.pdf
2022-04-03\e.xls
Notes:
[array] $data makes sure that the variable always contains an array. Otherwise PowerShell would output a single string value when only a single file is found. This could cause problems, e. g. when you want to iterate over $data by index, you would iterate over the characters of the single string instead.
To make this answer platform-independent I'm using forward slashes in the Get-ChildItem call which work as path separators under both Windows and *nix platforms.
Join-Path is used to make sure the output paths use the expected default path separator (either / or \) of the platform.
Related
I'm checking for missing XYZ map tiles using Powershell, but coming unstuck in the nested loops. Essentially the map tiles exist from a "base" folder, within this base folder are multiple directories. Within each directory are the map tiles.
e.g.
C:\My Map\17\ # this is the Base folder, zoom level 17
C:\My Map\17\1234\ # this is a folder containing map tiles
C:\My Map\17\1234\30200.png # this is a map tile
C:\My Map\17\1234\30201.png # this is a map tile
C:\My Map\17\1234\30203.png # this is a map tile, but we're missing 30202.png (have tiles either side)
C:\My Map\17\1234\30204.png # this is a map tile
C:\My Map\17\1235\ # this is another folder containing map tiles [...]
So my idea is for each folder, scan for gaps where we have tiles each side and try to download them.
This is what I have so far:
$BasePath = "C:\_test\17\"
$ColumnDirectories = Get-ChildItem $BasePath -Directory
$ColumnDirectories | ForEach-Object {
$ColumnDirectory = $ColumnDirectories.FullName
$MapTiles = Get-ChildItem -Path $ColumnDirectory -Filter *.png -file
$MapTiles | ForEach-Object {
#Write-Host $MapTiles.FullName
$TileName = $MapTiles.Name -replace '.png',''
$TileNamePlus1 = [int]$TileName + 1
$TileNamePlus2 = [int]$TileName + 2
Write-Host $TileName
}
}
But I'm getting Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Int32".
Eventually I want to go Test-Path on each of $TileName, TileNamePlus1, $TileNamePlus2, and where the middle one doesn't exist to download it again.
e.g.
C:\My Map\17\1234\30201.png -- Exists
C:\My Map\17\1234\30202.png -- Not exists, download from https://somemapsrv.com/17/1234/30202.png
C:\My Map\17\1234\30203.png -- Exists
Any help appreciated! I'm fairly new to Powershell.
The whole problem here is an understanding of how ForEach-Object loops work. Within the loop the automatic variable $_ represents the current iteration of the loop. So as suggested by the comments by dugas and Santiago Squarzon you need to change this line:
$TileName = $MapTiles.Name -replace '.png',''
to this:
$TileName = $_.Name -replace '\.png',''
Or more simply this (the BaseName property is the file name without the extension):
$TileName = $_.BaseName
Since all your png files have basenames as integer numbers, you could do something like this:
$BasePath = 'C:\_test\17'
$missing = Get-ChildItem -Path $BasePath -Directory | ForEach-Object {
$ColumnDirectory = $_.FullName
# get an array of the files in the folder, take the BaseName only
$MapTiles = (Get-ChildItem -Path $ColumnDirectory -Filter '*.png' -File).BaseName
# create an array of integer numbers taken from the files BaseName
$sequence = $MapTiles | ForEach-Object { [int]$_ } | Sort-Object
$sequence[0]..$sequence[-1] | Where-Object { $MapTiles -notcontains $_ } | ForEach-Object {
Join-Path -Path $ColumnDirectory -ChildPath ('{0}.png' -f $_)
}
}
# missing in this example has only one file, but could also be an array of missing sequential numbered files
$missing # --> C:\_test\17\1234\30202.png
If your file names have leading zero's, this won't work..
I have a little over 12000 files that I need to sort through.
18-100-00000-LOD-H.pdf
18-100-00000-LOD-H-1C.pdf
21-200-21197-LOD-H.pdf
21-200-21197-LOD-H-1C.pdf
21-200-21198-LOD-H.pdf
21-200-21198-LOD-H-1C.pdf
I need a way to go through all the files and delete the LOD-H version of the files.
EX:
21-200-21198-LOD-H.pdf
21-200-21198-LOD-H-1C.pdf
With the partial match being the 5 digit code I need a script that would delete the LOD-H case of the partial match.
So far this is what I have but it won't work because I need to supply values for the pattern but since there isn't one set pattern and more like multiple patterns I don't know what to supply it with
$source = "\\Summerhall\GLUONPREP\Market Centers\~Pen Project\Logos\ALL Office Logos"
$destination = "C:\Users\joshh\Documents\EmptySpace"
$toDelete = "C:\Users\joshh\Documents\toDelete"
$allFiles = #(Get-ChildItem $source -File | Select-Object -ExpandProperty FullName)
foreach($file in $allFiles) {
$content = Get-Content -Path $file
if($content | Select-String -SimpleMatch -Quiet){
$dest = $destination
}
else{
$dest = $toDelete
}
}
Any help would be super appreciated, even links to something similar or even links to documentation so I can start piecing a script of my own would be super helpful.
Thank you!
This should work for what you need:
# Get a list of the files with -1C preceeding the extension
$1cFiles = #( ( Get-ChildItem -File "${source}/*-LOD-H-1C.pdf" ).Name )
# Retreive files that match the same pattern without 1C, and iterate over them
Get-ChildItem -File "${source}/*-LOD-H.pdf" | ForEach-Object {
# Get the name of the file if it had the -1C suffix preceeding the .ext
$useName = $_.Name.Insert($_.Name.LastIndexOf('.pdf'), '-1C')
# If the -1C version of the file exists, remove the current (non-1C) file
if( $1cFiles -contains $useName ) {
Remove-Item -Force $_
}
}
Basically, look for the 1C files in $toDelete, then iterate over the non-1C files in $toDelete, removing the non-1C file if adding -1C before the file extension matches an existing file with 1C in the name.
I hope you are all safe in this time of COVID-19.
I'm trying to generate a script that goes to the directory and compresses each file to .zip with the same name as the file, for example:
sample.txt -> sample.zip
sample2.txt -> sample2.zip
but I'm having difficulties, I'm not that used to powershell, I'm learning and improving this script. In the end it will be a script that deletes files older than X days, compresses files and makes them upload in ftp .. the part of excluding with more than X I've already managed it for days, now I grabbed a little bit on this one.
Last try at moment.
param
(
#Future accept input
[string] $InputFolder,
[string] $OutputFolder
)
#test folder
$InputFolder= "C:\Temp\teste"
$OutputFolder="C:\Temp\teste"
$Name2 = Get-ChildItem $InputFolder -Filter '*.csv'| select Name
Set-Variable SET_SIZE -option Constant -value 1
$i = 0
$zipSet = 0
Get-ChildItem $InputFolder | ForEach-Object {
$zipSetName = ($Name2[1]) + ".zip "
Compress-Archive -Path $_.FullName -DestinationPath "$OutputFolder\$zipSetName"
$i++;
$Name2++
if ($i -eq $SET_SIZE) {
$i = 0;
$zipSet++;
}
}
You can simplify things a bit, and it looks like most of the issues are because in your script example $Name2 will contain a different set of items than the Get-ChildItem $InputFolder will return in the loop (i.e. may have other objects other than .csv files).
The best way to deal with things is to use variables with the full file object (i.e. you don't need to use |select name). So I get all the CSV file objects right away and store in the variable $CsvFiles.
We can additionally use the special variable $_ inside the ForEach-Object which represents the current object. We also can use $_.BaseName to give us the name without the extension (assuming that's what you want, otherwise use $_Name to get a zip with the name like xyz.csv).
So a simplified version of the code can be:
$InputFolder= "C:\Temp\teste"
$OutputFolder="C:\Temp\teste"
#Get files to process
$CsvFiles = Get-ChildItem $InputFolder -Filter '*.csv'
#loop through all files to zip
$CsvFiles | ForEach-Object {
$zipSetName = $_.BaseName + ".zip"
Compress-Archive -Path $_.FullName -DestinationPath "$OutputFolder\$zipSetName"
}
I have many folders and inside these different files. Each folder and their children files have the same name and different extension, so in the ABC folder there are the ABC.png, ABC.prj, ABC.pgw files, in the DEF folder there are the DEF.png, DEF.prj, DEF.pgw files and so on.
With a script I have created a txt file with the list of png file names. Then I put in row 2 a new name for the name in row1, in row 4 a new name for the name in row 3, and so on.
Now I'm searching a powershell script that:
- scan all folder for the name in row 1 and replace it with name in row2
- scan all folder for the name in row 3 and replace it with name in row4 and so on
I have try with this below, but it doesn't work.
Have you some suggestions? Thank you
$0=0
$1=1
do {
$find=Get-Content C:\1\Srv\MapsName.txt | Select -Index $0
$repl=Get-Content C:\1\Srv\MapsName.txt | Select -Index $1
Get-ChildItem C:\1\newmaps -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}
until ($0 -eq "")
I believe there are several things wrong with your code and also the code Manuel gave you.
Although you have a list of old filenames and new filenames, you are not using that in the Get-ChildItem cmdlet, but instead try and replace all files it finds.
Using -replace uses a Regular Expression replace, that means the special character . inside the filename is regarded as Any Character, not simply a dot.
You are trying to find *.png files, but you do not add a -Filter with the Get-ChildItem cmdlet, so now it will return all filetypes.
Anyway, I have a different approach for you:
If your input file C:\1\Srv\MapsName.txt looks anything like this:
picture1.png
ABC_1.png
picture2.png
DEF_1.png
picture3.png
DEF_2.png
The following code will use that to build a lookup Hashtable so it can act on the files mentioned in the input file and leave all others unchanged.
$mapsFile = 'C:\1\Srv\2_MapsName.txt'
$searchPath = 'C:\1\NewMaps'
# Read the input file as an array of strings.
# Every even index contains the file name to search for.
# Every odd index number has the new name for that file.
$lines = Get-Content $mapsFile
# Create a hashtable to store the filename to find
# as Key, and the replacement name as Value
$lookup = #{}
for ($index = 0; $index -lt $lines.Count -1; $index += 2) {
$lookup[$lines[$index]] = $lines[$index + 1]
}
# Next, get a collection of FileInfo objects of *.png files
# If you need to get multiple extensions, remove the -Filter and add -Include '*.png','*.jpg' etc.
$files = Get-ChildItem -Path $searchPath -Filter '*.png' -File -Recurse
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
$find = $file.Name
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
Write-Host "Renaming '$($file.FullName)' --> $($lookup[$find])"
$file | Rename-Item -NewName $lookup[$find]
}
}
Edit
If the input text file 'C:\1\Srv\MapsName.txt' does NOT contain filenames including their extension, change the final foreach loop into this:
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
# Look for the file name without extension as it is not given in the 'MapsName.txt' file.
$find = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
# Make sure to add the file's extension if any.
$newName = $lookup[$find] + $file.Extension
Write-Host "Renaming '$($file.FullName)' --> '$newName'"
$file | Rename-Item -NewName $newName
}
}
Hope that helps
The problem in your snippet is that it never ends.
I tried it and it works but keeps looping forever.
I created a folder with the files a.txt, b.txt and c.txt.
And in the map.txt I have this content:
a.txt
a2.md
b.txt
b2.md
c.txt
c2.md
Running the following script I managed to rename every file to be as expected.
$0=0
$1=1
$find=Get-Content D:\map.txt | Select -Index $0
while($find) {
$find=Get-Content D:\map.txt | Select -Index $0
$repl=Get-Content D:\map.txt | Select -Index $1
if(!$find -Or !$repl) {
break;
}
Get-ChildItem D:\Files -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}
I have a folder called files which has a path like : C:\users\xxxx\desktop\files
Inside this folder are different folders: 2015-12-02, 2015-12-01, 2015-11-30, etc
Inside each folder there are multiple files. I was looking to append the folder date at the end of each file inside the folder. I have written the below script for that:
function checkfile($file) {
$filenm = $file.FullName
return($filenm.Contains('.txt'))
}
function renamefile($file) {
$filenm = $file.Name
$ip = $file.FullName.Substring(34)
$ip1 = $ip.Substring(1,4) + $ip.Substring(6,2) + $ip.Substring(9,2)
$txt = $filenm.Split(".")[1] + "_" + $file.name.Split(".")[3] + "_" + $file.name.Split(".")[4] + "." + $file.name.Split(".")[2] + "." + $ip1
Rename-Item $file.FullName -NewName $txt
}
$sourcepath = "C:\users\xxxx\desktop\files"
$inputfiles = (Get-ChildItem -Path $sourcepath -Recurse) | Where-Object { checkfile $_ }
foreach ($inputfile in $inputfiles) {
renamefile $inputfiles
}
The problem I'm facing is in the above script I have used substring(34) to extract the date from the file path. If for some reason the source path changes (to say : H:\powershell\scripts\files) then 34 will not work.
How can I extract the correct date from the file path irrespective of the full file path?
Why not:
$sourcepath = "C:\users\xxxx\desktop\files"
Get-ChildItem -Path $sourcepath -Include "*.txt" -Recurse | % {
Rename-Item $_ "$($_.BaseName)_$($_.Directory)$($_.Extension)"
}
BaseName is the file name without the extension
Directory is the directory name (your date)
Extension is the file extension (i.e. .txt)
$(...) is used to make sure ... is evaluated properly
% is an alias for ForEach-Object and will iterate over the objects coming from the pipeline.
$_ will hold the current object in the ForEach loop
Here, your checkfile function is replaced by -Include "*.txt".
Example :
C:\users\xxxx\desktop\files\2015-12-02\sample.txt
becomes
C:\users\xxxx\desktop\files\2015-12-02\sample_2015-12-02.txt
Not sure if you need it, but if you want to remove the dashes from the date, you could use:
Rename-Item $_ "$($_.BaseName)_$($_.Directory -replace '-','')$($_.Extension)"
EDIT : OP wished to remove the dashes but append the date after the file extension, so:
$sourcepath = "C:\users\xxxx\desktop\files"
Get-ChildItem -Path $sourcepath -Include "*.txt" -Recurse | % {
Rename-Item $_ "$($_.Name).$($_.Directory.Name -replace '-', '')"
}
The particulars of the problem aren't entirely clear. I gather that the date your are interested in is in the fullpath. You want to extract the date from the path and rename the file, such that the new filename includes that date at the end.
However your script implies that there are at least five periods in the path. But I don't see that mentioned in the OP anywhere.
So there are a few problems and open items I see:
1. What is the syntax of a full path? That includes the five or more periods
2. Will the date always be at the same directory depth? I'm guessing xxxx represents the date. If so the date is the second subdirectory. Will the date always be in the second subdirectory?
3. Related to #2, will there ever be paths that include two or more dates?
Assuming my guesses are correct AND that the date will always be the second subdirectory, then extracting the date would be:
`$dateString = $file.fullpath.split('\')[3]
If some of my guesses are incorrect then please add details to the OP. If #3 is true then you'll need to also explain how to know which date is the correct date to use.
An option that you could do is just cd to each path. Then use Get-ChildItem in each dir, without using -Recurse.
In rename $ip would just be $file, no need for FullName.
For example, define your functions and then:
$sourcefile = 'Whateversourcediris'
cd $sourcefile
$directories = (Get-ChildItem)
foreach ($direct in $directories) {
cd $direct
$inputfiles = (Get-ChildItem)|where-object{checkfile $_}
foreach ($inputfile in $inputfiles) {
renamefile $inputfile
}
cd..
}
Hope this helps.