Replacing an images with different file names with one image - file

I am working on a project and need to replace many images:
I have one image 1.png which needs to replace about 40 different png files with different names so 2.png, 3.png etc. What is important is that when 1.png replaces 2.png, 3.png etc. the file name does not change. In other words, the content of 1.png should replace the content of 2.png and 3.png, but it should not replace the file name.
I am not sure if this is possible, however, if it is I would be very happy to hear how.

I think you could achieve this with powershell.
Something along these lines.
$files = Get-ChildItem -Path 'c:\source\folder' -Recurse
ForEach ($file In $files)
{
Write-Host 'Replacing:'$file.FullName
If ($file.Name -eq '2.png')
{
Copy-Item -Path 'c:\source\file\1.png' -Destination $file.FullName -Force
Rename-Item -Path $file.FullName -NewName "2.png"
}
}
Here is a link in case you haven't created a PS script before
https://www.windowscentral.com/how-create-and-run-your-first-powershell-script-file-windows-10

Related

PowerShell How to convert Get-ChildItem output to a string array

Alright, been working on this for hours and researching like crazy, but still not getting something to work. I need a string[] object created from get-childitem to pass to the Copy-Item -exclude parameter.
The hurdle is that I need to do recursion and need to have relative paths, so this is what I came up with:
$((Get-ChildItem -Path $Dest -Recurse -File).FullName.TrimStart($Dest))
This results in a clean list of existing files in $dest that are presented with a relative path to $dest. The problem is, if I add this to the copy-item -exclude parameter it seems to ignore it. Further research online suggests that copy-item will ignore the -exclude parameter if it is not of type string[].
If I check the type returned by the above command, I get System.Object[]. I expect it to be System.String[] or just plain String[].
How do I convert the output of the above command to a string array?
The full command using copy-item, for clarity, is:
Copy-Item -Path (Join-Path $src "*") -Destination $dest -Recurse -Force -Exclude $((Get-ChildItem -Path $Dest -Recurse -File).FullName.TrimStart($Dest))
My end goal is to copy files recursively without overwriting existing files.
To get a string[] from the names of get-childitem cmdlet use the following
[string[]]$files = (Get-ChildItem).Name
This will do what it appears you say that you want? But, I think that may not be everything to your question.
$((Get-ChildItem -Path $Dest -Recurse -File).FullName.Replace("$Dest",'.'))
#mklement0 is right, -Exclude and -Include support file name patterns (i.e. "*.txt") and not an array of explicit paths.
This sounds like an awful lot like an XY Problem ;-)
If you simply want to copy files recursively without overwriting them, use Robocopy with the Mirror switch. e.g.:
robocopy C:/Source C:/Dest /mir
EDIT:
By default Copy-Item will always overwrite the files on copy, and there is no switches to get around this. I usually recommend Robocopy as it really simplifies things like this and is very "robust" and reliable.
If your requirements are for a "pure" PowerShell version, then you have to break the scrip out into two parts, 1. Get a list of all the files 2. Iterate through the filer and test to see if they are already in the destination before copying.
$SrcPath = "C:/Source"
$DestPath = "C:/Dest"
$SrcFiles = Get-ChildItem $SrcPath -Recurse
#Iterate through files testing:
$SrcFiles | ForEach-Object {
#Calculate Destination File/Folder name/path
$DestObj = $_.FullName.Replace($SrcPath, $DestPath)
if(Test-Path -LiteralPath $DestObj)
{
#File already Exists
Write-Host "File already Exists: $DestObj"
}
else
{
#File Does not exist - Copy
Write-Host "File Does not Exist Copy: $DestObj"
Copy-Item -Path $_ -Destination $DestObj
}
}

Powershell ISE - How do I copy many files and rename them at the same time?

I'm trying to use Powershell ISE to help me do the following:
Perform a search for many files (with an extension of *props.tmpl) under a certain folder and to include all sub-directories.
When found, I want to copy that file to its current location, but with an extension of *.tmpl2 (what I really want is to skip this step and copy *props.tmpl to a file called *props)
Then rename all *.tmpl2 files and remove the tmpl2 entirely, leaving just the *.props extension.
Ideally, what I want is to copy existing files to the same directory with a new name. It seems like all of the searches I've ran on Powershell ISE are not coming up with the right info I need (or I'm not searching for the right way to do it - trying 'powershell ise copy many files with new names' didn't help.
I had the replacement piece down and working, but I no longer want to eliminate the original tmpl files (they are templates so I may want to review them later for their original content).
What I was doing to replace them was this:
Get-ChildItem -Filter "*props.tmpl" -Recurse |
Rename-Item -NewName { $_.name -replace '.tmpl',''}
Which works great other than completely removing the original file.
I started trying to piece something together, but I'm not understanding how to properly name the copy and stopped at this point with just an error (this was an attempt to skip the extra copy and just simply rename the copy instead of adding the extra step of '*.tmpl2'):
# Get all *props.tmpl files
Get-ChildItem -Filter "*props.tmpl" -Recurse |
# Iterate through each found file
ForEach-Object {
Copy-Item $_.name |
Rename-Item -NewName { $_.name -replace '.props.tmpl','.props' }
}
Any help would be really appreciated (not much of a Powershell guy, but I'm trying to learn since powershell tends to be a little more dynamic then oldschool batch scripts).
Thanks in advance
Final version of this script per help from #ssennett
Here's my final version:
# Get all *props.tmpl files
Get-ChildItem -Filter "*props.tmpl" -Recurse |
# Iterate through each found file and copy it to non-template form in same location
ForEach-Object {
Copy-Item $_.FullName ($_.Name -replace '.tmpl','')
}
You're not too far from the answer! It's just how Copy-Item is being handled.
Without a Destination being specified, the Copy-Item will effectively try and copy the file onto itself. Instead of piping it to Rename-Item, you can handle the renaming with the -Destination parameter, as below.
$files = Get-ChildItem -Filter "*props.tmpl" -Recurse
$files | % { Copy-Item -Path $_.FullName -Destination ($_.Name -replace 'props.tmpl','.props') }
This would copy a file called RandomFileprops.tmpl into another file RandomFile.props. If you want to remove the original, you can use the Move-Item cmdlet with the same parameters, which effectively renames the original file.

Move files from an array of subfolders to a new directory with same array names

Using powershell I would like to search through an array of sub-folders in multiple directories containing 1 folder called incoming and move the files from incoming to a staging area using the same folder name as its source.
IE: Files arrive in folders like this:
z:\folder1\incoming\file.*, z:\folder2\incoming\file.*, z:\folder3\sub1\incoming\file.*, z:\folder3\sub2\incoming\file.* etc.
Then would need to be moved to a staging area using the same folder structure:
\nas\staging\folder1\incoming\file.*, \nas\staging\folder2\incoming\file.*, \nas\staging\folder3\sub1\incoming\file.*, \nas\staging\folder3\sub2\incoming\file.* etc.
Basically the only sub-folders i want to extract from are the ones containing the "Incoming" folder with files. Since there is no predefined folder names other than the sub-folder "Incoming" i would need to loop through every folder within Z:.
Any help or advice would be greatly appreciated.
I was able to come up with this which affectively moves the folder structure excluding "outgoing" to the staging directory which is great but in my environment there would be to many exclusions to check for this way. Also is there a chance i can move the files to specific folders from maybe a list if needed?
$from = 'C:\ftp'
$to = 'C:\staging'
$excludeMatch = #("Outgoing")
Get-ChildItem -Path $from -Recurse -Exclude $exclude |
where { $excludeMatch -eq $null -or $_.FullName.Replace($from, "") -notmatch $excludeMatch } |
Copy-Item -Destination {
if ($_.PSIsContainer) {
Join-Path $to $_.Parent.FullName.Substring($from.length)
} else {
Join-Path $to $_.FullName.Substring($from.length)
}
} -Force -Exclude $exclude
Sooooo, what we need is, to identify any file whos parent directory is called 'incoming'? There are at least two ways of solving this I can think of, but there may be a neater, more idiomatic solution that eludes me.
Method 1 - Identify 'incoming' folders then copy contents
get-childitem -Path $from -recurse -Filter "incoming" |
where-object { $_.PSIsContainer }
If you are using PowerShell 3, I believe (read 'untested') this could be shortened to
get-childitem -Path $from -recurse -Filter "incoming" -Directory
Either of the above should result in a stream of 'directory' objects, all of which are 'incoming' folders. For each one, copy the files to appropriate destination.
Method 2 - Identify all files then process those whos folder is 'incoming'
get-childitem -path $from -recurse |
where-object { -not $_.PSIsContainer } |
where-object { (Split-Path $_.Directory -Leaf) -eq "incoming" }
This results in a stream of 'file' objects, all of which are in a folder called 'incoming'. For each one, copy to appropriate destination.

copy files from one location to another in powershell script, in addition checking for certain values

So I have folder with files in it, in a certain location
C:\Users\ainfowara\Desktop\testfiles
so I want to move those files to this location
C:\Users\ainfowara\Desktop\destinationTestfiles
the "testfiles" have files of this format txt.*.test.* so basically I wanna check before I move the files that they have those two main stuff (txt) and (test) in the third part.
can someone help me, how can perform this in powershell script
I know I can do this, to set the folders paths
path_src= C:\Users\ainfowara\Desktop\testfiles
path_dst= C:\Users\ainfowara\Desktop\destinationTestfiles
thanks in advance for the help
If there are no subfolders in testfiles(at least that you need files from), try this:
$src = "C:\Users\ainfowara\Desktop\testfiles"
$dst = "C:\Users\ainfowara\Desktop\destinationTestfiles"
Get-ChildItem $src -Filter "txt.*.test.*" | Move-Item -Destination $dst -Force
If you have files in subfolders of the source-path, try this:
$src = "C:\Users\ainfowara\Desktop\testfiles"
$dst = "C:\Users\ainfowara\Desktop\destinationTestfiles"
Get-ChildItem $src -Filter "txt.*.test.*" -Recurse | % {
#Creates an empty file at the destination to make sure that subfolders exists
New-Item -Path $_.FullName.Replace($src,$dst) -ItemType File -Force
Move-Item -Path $_.FullName -Destination $_.FullName.Replace($src,$dst) -Force
}
Be aware that if your filename contains square-bracket [ ] you need another script (known PS bug).

move-item doesn't work in loop

ls *.gif | Foreach { $newname = $_.Name -replace '\[','' -replace '\]',''
write-host $_.Name $newname
move-Item -Path $_.Name -Destination $newname; }
ls *.gif
So while trying to help someone rename files with [], I found out move-item doesn't work in a loop. It seems to work just fine outside the loop.
Ideas?
Update: Based on the comment below, I want to clarify this: The special characters in the file names require you to use -LiteralPath parameter. -Path cannot handle those characters. Outside a loop, -Path works since you are escapting the special characters using `. This isn't possible when walking through a collection.
In a loop, you need to use -LiteralPath parameter instead of -Path.
-LiteralPath <string[]>
Specifies the path to the current location of the items. Unlike Path, the value of
LiteralPath is used exactly as it is typed. **No characters are interpreted as
wildcards. If the path includes escape characters, enclose it in single quotation
marks.** Single quotation marks tell Windows PowerShell not to interpret any
characters as escape sequences.
SO, this will be:
GCI -Recurse *.txt | % { Move-Item -LiteralPath $_.FullName -Destination "SomenewName" }
If you use the pipeline binding feature of PowerShell, you can make this much simpler and eliminate the need for the explicit Foreach-Object e.g.:
ls *.gif | Move-Item -Destination {$_ -replace '\[|\]',''} -WhatIf
This works because the LiteralPath parameter is set up to bind ByPropertyName. However you may wonder, where does it get a property by the name of "LiteralPath" from on the output of Get-ChildItem (alias ls). Well it doesn't find that property name, however the LiteralPath parameter has an alias of PSPath defined which does exist on each object output by Get-ChildItem. That's how it binds to the LiteralPath paramter. The other speed tip here is that because the Destination parameter is also pipeline bound (ByPropertyName), you can use a scriptblock to provide the value. And inside that scriptblock you have access to the pipeline object.
Inside the scriptblock, this uses the -replace operator to come up with the new name based on the original full name. While I could have used $_.FullName or even $_.Name in this case (assuming you want to essentially rename the files within the same dir), I use just $_. Since -replace is a string operator, it will coerce $_ to a string before using it. You can see what this would be by executing:
ls *.gif | Foreach {"$_"}
Which is the full path in this case but you have to be careful because you don't always get the full path e.g.:
ls | Foreach {"$_"}
displays just the filename. In your examples (rename to same dir) this doesn't matter but in other cases it does. It is probably a good practice just to be explicit and use $_.Name or $_.FullName in a script but when hacking this stuff out at the console, I tend to use just $_. The saying: it's a sharp stick, don't poke your eye out applies here. :-)
You can find the "official" informations about the role of "[" in Path strings on this Microsoft article.
Or look in google for : Windows PowerShell Tip of the Week : "Taking Things (Like File Paths) Literally".
The only tip wich was not clear for me is that Rename-Item does not support LiteralPath, and that we can use Move-Item to rename files or directories.
JP
This worked for me (atleast in my situation.
Move-Item -literalpath $_.FullName -Destination ( ( ( (Join-Path -Path (Get-Location) -ChildPath $_.BaseName) -replace "\[","`[") -replace "\]","`]") )
Had hundreds of movies and it associated subtitles stored without folders. Decided to put each of the movies and subtitles in their own folders
Full Code
Get-ChildItem -File | %
{
if(Test-Path -LiteralPath ( Join-Path -Path (Get-Location) -ChildPath $_.BaseName ))
{
Move-Item -literalpath $_.FullName -Destination ( ( ( (Join-Path -Path (Get-Location) -ChildPath $_.BaseName) -replace "\[","`[") -replace "\]","`]") )
}
else
{
New-Item ( Join-Path -Path (Get-Location) -ChildPath $_.BaseName ) -ItemType Directory
Move-Item $_ -Destination ( Join-Path -Path (Get-Location) -ChildPath $_.BaseName )
}
}

Resources