Get-ChildItem piped to Copy-Item with rename to same directory - powershell

I am trying to bulk copy/rename files of a certain extension in a specific folder to the same folder with a prefix. I am certain this is a problem with my own Powershell inexperience. I simply do not know how to set up the command.
I am trying to take all the files of extension .mxd within a folder and rename them from something.mxd to _prefix_something.mxd_
I am pretty sure the first section is fine: Get-ChildItem *.mxd
But I don't know how to work with Copy-Item well enough. My understanding is that -Destination is where you would put the new file name, so how do I input ("current-folder\prefix" + $_.Name + ".mxd") ??
I am sure I am just missing a few key points to get this to work. All I am getting is a single "prefix_.mxd" file. It is the same size as the last file in the Get-ChildItem list. But if I bring the foreach loop back in, it asks for path values. What am I missing?

Like this? The doc for rename-item has a similar example.
dir *.mxd | copy-item -destination { 'prefix_' + $_.name } -whatif

Stupid. I have to force scriptblock with curlybraces {} instead of using parenthesis (), and I can skip the path to the current folder by starting with the prefix.

Related

Powershell Script: Search for BATs with specific name and run them

back with another request to try and make my life a little easier. The problem: one of the programs I use deposits BMPs (yes, bitmaps, this is an ancient app, and no, I can't configure it not to make BMPs) where I don't need them. I've got a BAT file that can sweep a folder and remove them, but what I'd really like to do is put a copy of said BAT file in each folder where it leaves them, and then every time I run a backup cycle, have it search for those BAT files, and wherever it finds one, run it. (I'd also need to know how to tell it "look in the same folder you're in"--I think I can do that by something like $searchfolder = "." but please correct me if I'm wrong)
I'm guessing this is a Get-Childitem and ForEach, but I've taken a few stabs at it and it won't work. Does anyone have an idea how to go about it?
This is what I've got so far for the parent script to find all instances of "Clear_BMPs.bat":
Get-ChildItem $sourceDir -Include Clear_BMPs.bat -Recurse | ForEach-Object { call "Clear_BMPs.bat" }
And this is what I've got in the child script, to get rid of the BMPs themselves (the filename for it is "Clear_BMPs.bat":
$searchfile = "*.bmp"
$targetdir = ".\"
Get-ChildItem $targetdir -Include $searchfile | foreach{ "Removing file $($_.FullName)"; Remove-Item -force $_}
I'm still trying to get the Clear_BMPs.bat files to work properly but in my vision it will only search the root of the folder it's in, and not recurse through subdirectories.
Since you're calling from PowerShell, there's no reason to involve batch files, given that the code is under your control.
Indeed, what you show as the content of a Clear_BMPs.bat batch file is PowerShell code, which means you need to store it in a .ps1 file, not a .bat file.
Therefore, your recursive invocation that executes all .ps1 files should look like this:
# Find all 'Clear_BMPs.ps1' scripts in the subdir. tree of $sourceDir
# and invoke them.
Get-ChildItem -Recurse -LiteralPath $sourceDir -Filter Clear_BMPs.ps1 |
ForEach-Object { & $_.FullName }
And the Clear_BMPs.ps1 files in the various directories should contain:
# Remove all *.bmp files from the same dir. in which this .ps1 script is located.
Remove-Item -Path "$PSScriptRoot/*.bmp"
Note the use of the automatic $PSScriptRoot variable, which refers to the directory in which the enclosing .ps1 file is located.

Renaming a Directory with wildcard on Powershell?

I have a folder that needs to be renamed.
$home/dir1/dir_to_rename_12344343
$value=abcd-1234
I know the beginning pattern of the folder but not the whole name.
I was trying to do something like this
cd $home/dir1
Rename-Item 'dir_to_rename*' '$value'
but it keeps giving me an error.
I am also happy if I can just trim the last few chars of the folder name.
i.e., $home/dir1/dir_to_rename_12344343 --> $home/dir1/dir_to_rename
I was finally able to get it to work
Get-ChildItem -Path $home/dir1 | Rename-Item -NewName $Value

Using the rename-item command to overwrite an existing file?

I'm trying to tidy up imported files from my iPhone using a PowerShell script, specifically wanting edited photos (IMG_E001.jpg) to overwrite the non-edited photos (IMG_001.jpg).
I'm using the rename-item but can't figure out how to get it to overwrite the file. My current workaround is to move all of the edited files into their own folder, run the script, and then copy them over the top of the original files but this is a bit of a pain, plus it doesn't seem as reliable as a fully automated script.
Here's the code I'm using currently:
get-childitem -recurse -Include *.JPG | foreach { rename-item $_ $_.Name.Replace("E", "") };
Please can anyone help me to get this script to overwrite the existing files? Thank you
This is a limitation of rename-item as #postanote mentioned in his comment this is because windows does not allow you to rename a file to an existing name. You will need to remove the other file first or overwrite it. Instead you can use move-item -force for essentially the same effect as it will perform the overwrite.
get-childitem -recurse -Include *.txt | foreach { Move-Item $_ $_.Name.Replace("E", "") -Force };
For more information; here is a related thread rename-item and override if filename exists

Copy files in a directory, to folders with same name as file

I have multiple files that I want to copy each file to a folder with same name
For example, the files
orange_file100 , orange_file200 , orange_file300 , apple_file120 , apple_file150
I want to move each file to a folder that contain part of the filename say orange and apple so the result will be
orange\orange_file100
orange\orange_file200
orange\orange_file300
apple\apple_file120
apple\apple_file150
How can I do that through powershell, should I use Get-ChildItem then ForEach{Copy-Item) ?
You can use Get-Childitem with a -File or -Directory to only grab the files or folders in a folder, that way you wont grab a folder and try place it in itself.
For example, the code below will only grab the files in the current directory
Get-Childitem -File
You can then use some regex to split the names so you can get the fruit name e.g.
$String.split('_')[0]
You should insert it into a list or array or something to store it, but now you have a list of files and fruit names.
Now you can loop over the list and start to move or copy the files into the right folder structure
Foreach($file in $FileList){
if($file.name -matches $Fruitname){
if($file.name -notmatch $pwd.path ){
mkdir $file.name
cd $file.name
move-item $file.fullname $pwd
}
}
}
The code above is just a quick attempt. It probably wont work the first time and you should make adjustments to understand what you are doing.
A few notes
$pwd gets the current directory. I'm assuming Get-Childitem returns the list of files in the correct order, so you will get Orange_100, then Orange_200 and so on
Get-Childitem returns a powershell object. The file names can be accessed using $_.name or the full path using $_.fullname
If -matches doesn't work, you can also try -like or -in
I didn't add in the first fruit folder into the code above, but it won't be hard to create
Remember to play around and find whats best for you.

How can I use Powershell to keep two directories updated with the latest files

I have two directories:
C:\G\admin\less
C:\G\user\less
Inside of those directives I have multiple less and css files. I know all the names of the files so I would like to hardcode a list of these into the script. Perhaps this could be in an array or something but my knowledge of PowerShell is not even enough to know if there are arrays in the scripting language.
C:\G\admin\less
html-light.less
html-light.css
html-dark.less
html-dark.css
do-not-track-me.less
C:\G\user\less
html-light.less
html-light.css
html-dark.less
html-dairk.css
do-not-track-me.less
Is there a way I can use PowerShell to check each of these files (that I want to hardcode in my program) one by one and copy the last modified file from its directory to the other directory so that both directories will contain the same latest versions of these files?
Note that I would need to evaluate the predefined list of files one by one. Comparing modified date in one directory with the other and copying over as needed.
again assume that this isn't the best solution or approach
This solution assumes following
- when the LastWriteTime on one folder is bigger than the other it copy it to another folder.
- I'm not doing the path validation because of laziness but if you want with path validation just ask.
- I'm assuming that all the files on those folder must be tracked otherwise read the comment on code.
- i suggest you backup your folder before you run the script.
#if there is a file you don't want to track on those folder (for example you don't want to track txt files)
#just write $userFiles=dir C:\G\user\less\ -Exclude "*.txt"
#if you don't want track txt but only one of them should be track among with other file format
#$userFiles=dir C:\G\user\less\ -Exclude "*.txt" -Include "C:\G\user\less\Txtadditionaltotrack.txt"
$userFiles=dir C:\G\user\less\
$adminfiles=dir C:\G\admin\less\
foreach($userfile in $userFiles)
{
$exactadminfile= $adminfiles | ? {$_.Name -eq $userfile.Name} |Select -First 1
#my suggestion is to validate if it got the file.
#By now because of my lazy i will not call the test-path to validate if it got the file
#I'm assuming all directory are exact copy of each other so it will find the file.
if($exactadminfile.LastWriteTime -gt $userfile.LastWriteTime)
{
Write-Verbose "Copying $exactadminfile.FullName to $userfile.FullName "
Copy-Item -Path $exactadminfile.FullName -Destination $userfile.FullName -Force
}
else
{
Write-Verbose "Copying $userfile.FullName to $exactadminfile.FullName "
Copy-Item -Path $userfile.FullName -Destination $exactadminfile.FullName -Force
}
}
you can improve it because the way this code is it always copy file from one directory to another inside the else you can validate so that when the lastwriteTime is equal on both it doesn't copy.
You can improve it in many ways. i hope you got the ideia
Find the modification made to code so that it can archieve this requirement.
PLEASE READ THE COMMENT IN CODE.
NOTE THAT I'M NOT FOLLOWING THE BEST PRATICE (avoid unexpected error, name correctly all variable, ...)
#to make it more dynamical you can save on one file
#all the file names including extension in different lines.
#For example on path C:\FilesToWatch\watcher.txt
#$filestowatch=get-content C:\FilesToWatch\watcher.txt
$filestowatch="felicio.txt","marcos.txt"
$userFiles=dir C:\G\user\less\
$adminfiles=dir C:\G\admin\less\
#Optionally instead of use this if approach you can
#$userFiles=dir C:\G\user\less\ |? {$filestowatch -contains $_.Name}
#$adminfiles=dir C:\G\admin\less\|? {$filestowatch -contains $_.Name}
#loading in the above manner the first if statement on code bellow can be removed because
#We make sure that $userFiles and $adminfiles only have correct file to monitor
foreach($userfile in $userFiles)
{
if($filestowatch -contains $userfile.Name)
{
$exactadminfile= $adminfiles | ? {$_.Name -eq $userfile.Name} |Select -First 1
#my suggestion is to validate if it got the file.
#By now because of my lazy i will not call the test-path to validate if it got the file
#I'm assuming all directory are exact copy of each other so it will find the file.
if($exactadminfile.LastWriteTime -gt $userfile.LastWriteTime)
{
Write-Verbose "Copying $exactadminfile.FullName to $userfile.FullName "
Copy-Item -Path $exactadminfile.FullName -Destination $userfile.FullName -Force
}
else
{
Write-Verbose "Copying $userfile.FullName to $exactadminfile.FullName "
Copy-Item -Path $userfile.FullName -Destination $exactadminfile.FullName -Force
}
}
}
I think what you need is symbolic links, aka symlinks.
With symlinks, you can define files and folders that will be always in-sync, where the target file is updated automatically when the original is modified.
To create a symbolic link, enter the following in the console/command prompt:
mklink /[command] [link path] [file or folder path]
Mklink can create several types of links, according to these commands:
/D – creates a soft symbolic link, which is similar to a standard folder or file shortcut in Windows. This is the default option, and mklink will use it if you do not enter a command.
/H – creates a hard link to a file.
/J – creates a hard link to a folder.
The syntax is simple. Choose your option, define the path you want for the symlink, and finally the path of the original file/folder.
For example, imagine I'm developing a new project, and I want to share it to my client via Dropbox shared folder. I don't want to move all my workspace to dropbox, I just want to share that specific folder to them:
mklink /J C:\Dropbox\clients_shared_folders\project_x C:\my_dev_rootfolder\project_x
Note that the first path is the symbolic folder I want to create, while the second path is the existing directory.
In you case, I'll be assuming your working on the admin folder, and want to generate a syncd copy on the user folder:
mklink /J C:\G\user\less C:\G\admin\less
Here's a nice article for more info:
http://www.howtogeek.com/howto/16226/complete-guide-to-symbolic-links-symlinks-on-windows-or-linux/