I would like to recurse through all subfolders of a given folder, and for all files of a given extension (optionally wildcard), search and replace some piece of text.
There's a similar previous question here, and I'm trying to get a modified version of the top answer working for my purpose. It uses Powershell for the find and replace. However, my modified version below is not recursing. I don't remember where I found the recurse part of the first line, so maybe I'm using that incorrectly.
Get-ChildItem *.* -exclude *.ps1* -recurse |
Foreach-Object {
$c = ($_ | Get-Content)
$c = $c -replace 'foo','bar'
[IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
}
I am running the code from the Powershell command line as a PS batch file, .ps1, hence the exclusion of that extension.
This works fine if I run it directly in a folder that has some files I want searched, but if I run it from a parent of that folder, the searching/ recursion of subfolders does not happen. It doesn't even try to, which I can tell from the fact that the command finishes running instantaneously. If I run it in my first subfolder, which has about 50 text files, there's a couple seconds delay before it finishes, indicating to me it's actually searching those files.
I've also tried this with the exclude portion removed and the behaviour is the same.
My guess is that *.* matches only items with a dot in them. Folders have no extension, so they aren't matched.
gci -rec -force -exclude *.ps1 | should do what you want.
Related
I would like to be able to rename multiple directories names's selected parts with a command line in windows.
I looked for the "ren" command, which works with files. For example you can do ren miss*.txt mister*.txt, and all of the files containing miss in their name will have the "miss" modify to "mister", without modify the rest of the file's name. But it is not working with directories names (command line doesn't recognize the "*" command), and I would like to know if someone know a command line which can do the same with directories.
If powershell is an option, you can use following one-liner
gci -di miss* | ren -n {$_.fullname -replace 'miss', 'mister'}
In full
Get-ChildItem -Directory miss* |
Rename-Item -NewName {$_.fullname -replace 'miss', 'mister'}
Pretty noob at writing PS scripts - wrote this up and have been actively using it although still requires some manual intervention trying to achieve my goal, which I would like to automate completely.
I will try my best to explain clearly;
I am trying to copy '.bak' files to a specific directory from a source folder that has files dropped in it on a daily basis. Problem is the way I created the script, every time it runs it creates a new folder with some of the same files as previously copied.
The files being copied all follow the same name structure in date sequence;
xxxx_2018_01_01_2131231.bak
xxxx_2018_01_02_2133212.bak
xxxx_2018_01_03_2199531.bak
How could I write the script so that it copies newer files only and not what has already been copied previously?
It would also be nice to only create a new folder then a certain part of the file name changes.
Here is the script;
$basedir = "Path:\path"
$today = (Get-Date).ToString('MM_dd_yy')
$Filter = '*.bak'
$location = New-Item -Path $basedir -Type Directory -Name $today
Copy-Item -Path 'Path:\path' -Destination $location -Filter $Filter -Recurse
Any pointers are greatly appreciated!
Thanks in advance for your help!
I'm not sure if there is an easy way to code this, but the general answer would be using the Get-ChildItem cmdlet.
"The Get-ChildItem cmdlet gets the items in one or more specified locations. If the item is a container, it gets the items inside the container, known as child items. You can use the -Recurse parameter to get items in all child containers and use the -Depth parameter to limit the number of levels to recurse."
By using the Get-ChildItem, you could get the listing of files that are in both directories, and then compare them to see if they have the same name. Then build an if() argument based on criteria you wish to use to compare them.
It's not the complete answer, but it is a good starting point.
Thanks everyone for pitching in, much appreciated!
I have switched over to the batch file route and have created the following to accomplish my goal;
#echo off
setlocal
set _source="C:\users\user1\desktop\source"
set _dest="C:\users\user1\desktop\dest"
robocopy %_source% %_dest% *.bak /mir /XC /XN /XO
Any opinion on this script is encouraged!
I am searching through folders in order to find the one that has the contents that I desire.
$path = dir "C:\windows\ccmcache\*\Office.en-us" -Directory
echo $path
It returns:
Directory: C:\windows\ccmcache\c
But when I run my command:
Start-Process "$path\setup.exe /uninstall ProPlus /config Uninstall.xml" -Wait
It tries to run:
C:\windows\ccmcache\c\Office.en-us\setup.exe............
Which doesn't exist! So how can I go back a step so I can run the setup.exe command out of the c folder?
Something like:
$path2 = $path\cd..
Thank you all in advance.
You can simply do:
$Path2 = Resolve-Path (Join-Path $Path '..')
Note:
Join-Path is the cross platform way of concatenating path strings
Resolve-Path will give you a fully qualified path name
^^ step is optional, since windows will traverse the .. for you, but it helps to visually see the folder it resolves to.
Does this help?
You are using Get-ChildItem to return a System.IO.DirectoryInfo object. The path you are looking for already exists there as the Parent property.
$path2 = $path.Parent.FullName
No other cmdlets are needed here. You don't even need to save it into another variable if you don't want to.
Beware that your $path could have multiple results which will have consequences later in your code. If you only cared about the first one you could add | Select -First 1 to guarantee only one result.
It can be done simply using Resolve-Path function.
suppose structure is like following
root
Folder1 Folder2
and our current working directory is Folder1 and we want to move to Folder2.
$path2 = Resolve-Path("$path\..\Folder2\fileinFolder2")
I'm not sure I completely understand what you're asking.. Do you mean how to backtrack in your file directory? That command is "cd .."
Do you mean how to call $path THEN move one level higher in the directory? If so you'll need to create a new $var that is one level higher before calling your setup.exe
I'm trying to get PowerShell to evaluate variables before executing a command, for example:
$OutputPath = "C:\Temp"
Get-ChildItem -include *.mp3 | Move-Item -Destination $OutputPath
However, the Move-Item cmdlet tries to interpret this literally, so it doesn't get moved. The script works fine whenever I enter the path directly, but I need to be able to control the path with a variable. How do I do this?
This answer could evolve but as your question stands I see a big issue that is unfortunately poorly documented. -Include and -Exclude only perform their intended functions when partnered with -Recurse. Used without it can yeild 0 results. In your case that would mean nothing is passed through the pipe and Move-Item is not executed.
Currently you are just filtering on *.mp3 which is basic and can just be used with the -filter parameter. While you don't need to I would recommended specifying -Path as well so that you move the files you wanted.
Get-ChildItem -Filter *.mp3 | Other-Stuff ...
You mentioned in comments the following error:
Move-Item : Cannot create a file when that file already exists.
That error is very specific. Either from previous testing or an oversight in new names that file indeed already exists. Two things you can do to help with that problem is use the -WhatIf switch which should quote a file path on the verbose stream so you know where the file would end up.
Second, if you understand the data risk, is to use -Force so that the file will be overwritten by the new one. With Copy-Item it is not a big deal since the original file still exists. Mistakes with Move-Item can be permanent.
I have set of files in a folder with name like abcd.15678
I want to remove the . and replace it with _
Pls suggest the windows command to do this
This solution is reposted from How to Batch Rename Files in Windows: 4 Ways to Rename Multiple Files by Chris Hoffman
PowerShell offers much more flexibility for renaming files in a command-line environment. Using PowerShell, you can pipe the output of one command – known as a “commandlet” in PowerShell terms — to another command, just like you can on Linux and other UNIX-like systems.
First of all, open Powershell ISE and then navigate to the directory (folder) that has the files and folders you'd like to rename by using this command:
cd "C:\your\directory\"
The two important commands you’ll need are Dir, which lists the files in the current directory, and Rename-Item, which renames an item (a file, in this case). Pipe the output of Dir to Rename-Item and you’re in business.
After you launch PowerShell ISE, use the cd command to enter the directory containing your files. You should put the files in their own directory so you don’t accidentally rename other files.
For example, let’s say we don’t want the dot character in our file names – we’d rather have an underscore instead.
The following command lists the files in the current directory and pipes the list to Rename-Item. Rename-Item replaces each dot character with an underscore.
Dir | Rename-Item –NewName { $_.name –replace ".","_" }
Consult Microsoft’s documentation on the Rename-Item commandlet if you want help performing other, more advanced operations.
There isn't a windows command to do this. You should consider writing a script of some sort that obtains a directory listing and enumerates through each entry: changes the dot to an underscore, and calls the windows rename command appropriately.
Actually this should work :
Dir | Rename-Item –NewName { $_.Name.Replace(".","_") }