Performing minus from parent to child files using powershell - powershell

I have three files in test1 folder:
I have three files in test2 folder:
Common between these two folders is file2.
I want to subtract common files between these two folders and get only unique files from test1 folder.
My expected output is:
file1.txt
file3.txt
I tried using:
cls
$parent='D:\test1'
$child='D:\test2'
$final = [System.Collections.ArrayList]#()
$parentarrays=Get-ChildItem $parent
$childarrays=Get-ChildItem $child
foreach ($p1 in $parentarrays) {
foreach ($p2 in $childarrays){
if($p1 -notcontains $p2){
$final.add($p1)
}
}
}
Write-Host $final
But I am getting output:
file1.txt.txt file1.txt.txt file1.txt.txt file2.txt.txt file2.txt.txt file2.txt.txt file3.txt.txt file3.txt.txt file3.txt.txt

Get a list of filenames in folder 2 and do the same for folder1. Use a Where-Object clause to filter out any filename that is also in the reference list:
$filesInFolder2 = (Get-ChildItem -Path 'D:\Test2' -File).Name
(Get-ChildItem -Path 'D:\Test1' -File).Name | Where-Object { $filesInFolder2 -notcontains $_ }
Output:
file1.txt
file3.txt

Related

Retrieving file information of specific date using powershell

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.

Split property in array into many properties

I use get-childitem to get files from directory structure.
Get-ChildItem $path -Recurse -include *.jpg,*.png | select-object Directory, BaseName, Extension
I get an array of objects with properties Directory, BaseName, Extension.
Like:
Directory BaseName Extension
C:\dir1\dir2\dir3\dir4 file txt
I wan't to break directory structure into multiple properties inside same array - each subdirectory level it's own property.
The end result properties should be (I can remove c:\ earlier in script):
dir1 dir2 dir3 dir4 Basename Extension
dir1 dir2 dir3 dir4 file txt
I used to export that to csv and import it back with delimiter to another array and than rebuilding the original array, but I think there must be an easier way!
Here is a possible approach:
$path = 'C:\test'
$maxDepth = 0
Set-Location $path # Set base path for Resolve-Path -Relative
# Get all files and splits their directory paths
$tempResult = Get-ChildItem $path -Recurse -include *.jpg,*.png | ForEach-Object {
# Make directory path relative to $path
$relPath = Resolve-Path $_.Directory -Relative
# Create an array of directory path components, skipping the first '.' directory
$dirNames = $relPath -replace '^\.\\' -split '\\|/'
# Remember maximum directory depth
$maxDepth = [Math]::Max( $dirNames.Count, $maxDepth )
# Implicit output that PowerShell adds to $tempResult
[PSCustomObject]#{
dirNames = $dirNames
fileInfo = $_ | Select-Object BaseName, Extension
}
}
# Process $tempResult to add directory properties and file name properties
$finalResult = $tempResult.ForEach{
$outProps = [ordered] #{}
# Add directory properties
for( $i = 0; $i -lt $maxDepth; ++$i ) {
$outProps[ "Dir$i" ] = if( $i -lt $_.dirNames.Count ) { $_.dirNames[ $i ] } else { $null }
}
# Add all fileInfo properties
$_.fileInfo.PSObject.Properties.ForEach{ $outProps[ $_.Name ] = $_.Value }
# Implicit output that PowerShell adds to $finalResult
[PSCustomObject] $outProps
}
$finalResult # Output to console
This is done in two passes to ensure all output elements have the same number of directory properties:
Iterate over all files and split their directory paths. Determine maximum directory depth (number of directory properties).
Iterate over the intermediate result to create the desired objects consisting of directory properties and file name properties. This is done by first adding the properties to an ordered hashtable and then converting that hashtable to a PSCustomObject. This is easier and more efficient than using Add-Member.
Test input:
C:\test
\subdir1
file1.png
file2.jpg
\subdir2
\subdir3
file3.jpg
Output:
Dir0 Dir1 BaseName Extension
---- ---- -------- ---------
subdir1 file1 .png
subdir1 file2 .jpg
subdir2 subdir3 file3 .jpg

How to Create a 'Missing' File(s) List

I am trying to compile a list of missing files between two folder/directories which have text/image files with the same basename.
# file directory/folder with .txt files
$filesPathText = "C:\test\test3"
# file directory/folder with .JPG files
$filesPathImage = "C:\test\test4"
Content:
Copy of 0002.txt
Copy of 0003.txt
Copy of 0004.txt
Copy of 0006.txt
Copy of 0002.jpg
Copy of 0003.jpg
Copy of 0004.jpg
Copy of 0005.jpg
Copy of 0006.jpg
I want to output that the 'missing' file is: Copy of 0005.txt
I have tried this kind of thing:
$texts = Get-ChildItem -Path $filesPathText
$images = Get-ChildItem -Path $filesPathImage
$result = $images | Where-Object{$texts -notcontains $images}
$result
To me the logic reads correctly but the result is an output of all the image files.
Even though this is a simple example and would seem common usage I have not been able to find a similar question that has been answered.
Any suggestions would be welcome.
Your Where-Object script block is comparing 1 array of objects ($texts) against the other array of object ($images) instead of comparing each object ($_) against an array of objects. You're also not referencing the Property (.BaseName) you want to compare.
$texts = Get-ChildItem -Path $filesPathText
$images = Get-ChildItem -Path $filesPathImage
# missing text files
$images | Where-Object { $texts.BaseName -notcontains $_.BaseName } | ForEach-Object {
$_.BaseName + '.txt'
}
# missing images
$texts | Where-Object { $images.BaseName -notcontains $_.BaseName } | ForEach-Object {
$_.BaseName + '.jpg'
}

Search and replace files and folders names with txt file support

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
}

Powershell script to pull info from file name from list of folders

I have a list of files in a folder each are in this format: custID_invID_prodID. For every file I need to break it into sections based on '_'. Currently I have this code
$files = Get-ChildItem test *.txt
$Part = $files.Split(';')[$($files.Split('_').Count-1)]
Write-Host "${Part}"
The problem is that it appears that $files is a list of all the files and I need it to run each file on its own. Can anyone offer some pointers on how to do this for the entire folder?
"The problem is that it appears that $files is a list of all the files and I need it to run each file on its own."
That is correct, running get-childitem on a folder will give you a list of all files (and subfolders). In order to go through them one by one, you need a loop:
$file = #()
$files = Get-ChildItem Test *.txt
foreach($f in $files)
{
$file += ([String]$f).Split("_")
$count = ([String]$f).Split("_") | Measure-Object | select count
}
if($count.count -eq 3) {
for($i=2; $i -lt $file.length; $i+=3) {
$file[$i] = $file[$i].trimend(".txt")
}
}
I was able to fix this by using this code:
$Part = $file.name.Split('_')[$($file.name.Split('_').Count-1)]
Write-Host "${Part}"