How to replace string in files recursively - windows

I'm developing one application. Some path have to be changed on the whole project. The path are fixed and the files can be edited (it is in ".cshtml" ).
So I think I can use a batch file to change all the "http://localhost.com" to "http://domain.com" for example (I know relative and absolute path, but here I HAVE to make that :-) )
So if you have code that can make that changes in files, it could be marvellous!
To complete my question, here it is the path of files and dir
MyApp
MyApp/Views
MyApp/Views/Index/page1.cshtml
MyApp/Views/Index/page2.cshtml
MyApp/Views/Another/page7.cshtml
...
Thanks to help me :-)

Something like this might work as well:
#!/bin/bash
s=http://localhost.com
r=http://example.com
cd /path/to/MyApp
grep -rl "$s" * | while read f; do
sed -i "s|$s|$r|g" "$f"
done
Edit: Or not, since you just switched from bash to batch-file. A batch solution might look like this:
#echo off
setlocal EnableDelayedExpansion
for /r "C:\path\to\MyApp" %%f in (*.chtml) do (
(for /f "tokens=*" %%l in (%%f) do (
set "line=%%l"
echo !line:
)) >"%%~ff.new"
del /q "%%~ff"
ren "%%~ff.new" "%%~nxf"
)
Doing this in batch is really, really ugly, though (error-prone too), and you'd be far better off using sed for Windows, or (better yet) doing it in PowerShell:
$s = "http://localhost.com"
$r = "http://example.com"
Get-ChildItem "C:\path\to\MyApp" -Recurse -Filter *.chtml | ForEach-Object {
(Get-Content $_.FullName) |
ForEach-Object { $_ -replace [regex]::Escape($s), $r } |
Set-Content $_.FullName
}
Note that -Filter only works in PowerShell v3. For earlier versions you have to do it like this:
Get-ChildItem "C:\path\to\MyApp" -Recurse | Where-Object {
-not $_.PSIsContainer -and $_.Extension -eq ".chtml"
} | ForEach-Object {
(Get-Content $_.FullName) |
ForEach-Object { $_ -replace [regex]::Escape($s), $r } |
Set-Content $_.FullName
}

You can do this:
find /MyApp -name "*.cshtml" -type f -exec sed -i 's#http://localhost.com#http://domain.com#g' {} +
Explanation
find /MyApp -name "*.cshtml" -type f looks for files with .cshtml extension in /MyApp structure.
sed -i 's/IN/OUT/g' replaces the text IN to OUT in the files.
hence, sed -i 's#http://localhost.com#http://domain.com#g' replaces http://localhost.com with http://domain.com.
exec .... {} + executes .... within the files found by find.

Related

Recursively add an extension to a group of files in PowerShell Version. 4

I'm looking at how to add an extension recursively to an entire folder structure. This line does what I need, but only for the current folder. I need to do the same for the subfolders structure.
Get-ChildItem -File | % { mv ($_.BaseName+"") ($_.BaseName+".png") }
But I keep getting this error:
Cannot find path 'C:\Users\Jess...\' because it does not exist.
I used the search, however I only founded a solution for the Bash, not the PowerShell. So I couldn't get the -Recurse to work in the same way.
Get-ChildItem -File -Recurse | % { mv ($_.BaseName+"") ($_.BaseName+".png") }
Here's a way to get the full pathname. In powershell 6 this isn't an issue.
Get-ChildItem -File -Recurse | % { $_ | mv -destination ($_.Name + '.png' ) -whatif }
Or
Get-ChildItem -File -Recurse | ? { ! $_.extension } |
mv -destination { $_.Name + '.png' } -whatif

Batch file: Combine echos into one line

I want to dump all the file names in a folder without extension into a text file. They should be in one line separated by commas.
So in my folder I have
File1.bin
File2.bin
....
With
(for %%a in (.\*.bin) do #echo %%~na,) >Dump.txt
I got
File1,
File2,
But what I want in the end is a text file with, so one long combined string.
File1,File2,...
I'm kinda stuck here and probably need something else than echo.
Thanks for trying to help.
Try like this:
#echo off
setlocal enableDelayedExpansion
for %%a in (.\*.txt) do (
<nul set /p=%%~nxa,
)
check also the accepted answer here and the dbenham's one.
You could also leverage powershell from a batch-file for this task:
#"%__APPDIR__%WindowsPowerShell\v1.0\powershell.exe" -NoProfile -Command "( Get-Item -Path '.\*' -Filter '*.bin' | Where-Object { -Not $_.PSIsContainer } | Select-Object -ExpandProperty BaseName ) -Join ',' | Out-File -FilePath '.\dump.txt'"
This could probably be shortened, if necessary, to:
#PowerShell -NoP "(GI .\*.bin|?{!$_.PSIsContainer}|Select -Exp BaseName) -Join ','>.\dump.txt"

Rename or Remove Characters From a Filename with PowerShell

I am trying to rename or remove part of a filename. I have a scheduled job that moves an updated file from one server to another. Once the file is moved it need to be renamed. For example: Filename_01-23-AB.exe to Filename.exe I want to remove everything from the "_" to the "." and leave the extension intact. I found the following code that should do this but I can't seem to get it to work. Am I headed down the right path here?
## Removes the build number from the filename of "Filename_XX-XX-XX.exe" leaving the new filename to be "Filename.exe" ##
$File = -Path "C:\Temp\TestPath\"
foreach ($File in gci *.exe) {
$Fname = ($File.name).split('.')[0] ## item before the '.' ##
$Prefix = $Fname.split('_')[0] ## item before the '_' ##
$Newname = $Prefix + '.exe'
Rename-Item $file $Newname
}
I've tried simplifying what you're tying to do:
pushd "C:\Temp\TestPath\"
dir | ? { $_.Name -like "*.exe" } |
% { Rename-Item $_ "$($_.BaseName.split('_')[0])$($_.Extension)" }
popd
First, this changes the directory your PowerShell is looking at to your defined directory (pushd).
Then it will get the contents of your directory, find all objects with the extension of .exe, and split the file's base name at the underscore. popd will then set the directory back to where-ever it was previous to running this script.
I think this can be simplifyed even more using a regex to remove everything between the first underscore and the file extension:
$Path= 'C:\Temp\TestPath\'
Get-ChildItem $Path -Filter "*_*.exe" | foreach {
$_ | Rename-Item -NewName ($_.Name -replace '(?=_).+(?=\.)')
}
This also works when the filename doesn't contain an underscore.
And here the regex demo.
!not tested
try this
$path = 'C:\Temp\TestPath\'
gci $path *.exe | ? basename -match '_' | % {
# if the file.txt already exists, rename it to file-1.txt and so on
$num = 1
$base = $_.basename.substring(0, $_.basename.indexof('_'))
$ext = $_.extension
$dir = $_.directory
$newname = Join-Path $dir "$base$ext"
while (Test-Path $newname) {
$newname = Join-Path $dir "$base-$num$ext"
$num++
}
# finally, rename the file
ren $_.fullname $newname
}

Converting a batch FOR /F with multiple commands to a FOREACH in PowerShell

Some time ago, #Magoo was nice enough to help me in working out a FOR /F command to archive files into 7-zip:
Using FOR or FORFILES batch command to individually archive specific files
Since then I've expanded it somewhat and want to do more elaborate things with it. However, to keep this question simple, I've included the basics to try and get this working, which I haven't had much success at.
I'm rather new to PowerShell and I have some specific reasons to use this instead of batch files, moving forward.
I understand that some more experienced users may note that I will have a reduction in performance by using such statements in PowerShell, but it isn't an important issue for me.
$env:Path += ";C:\Program Files\7-Zip"
$sourcedir = read-host "Enter the directory to archive: "
foreach ($aname in {
'cmd /c dir /s/b /a-d "$sourcedir\*.iso" '
'cmd /c dir /s/b /a-d "$sourcedir\*.daa" '
'cmd /c dir /s/b /a-d "$sourcedir\*.nrg" '
'cmd /c dir /s/b /a-d "$sourcedir\*.flp" '}
) {
IF NOT EXIST $aname.7z (
echo 7z a -t7z "$aname.7z" "$aname" -mx9 -mmt >> Z:\test\7z-log.txt
ECHO "$aname" archived.
) ELSE (
ECHO "$aname" archive file already exists.
)
}
I got into some trouble with the IF EXIST statement and even when I removed the IF and had a one-line ECHO just to simplify it even more, but I couldn't get it to output what I wanted.
So, I tried a different approach:
$env:Path += ";C:\Program Files\7-Zip"
$sourcedir = read-host "Enter the directory to archive: "
$dir_iso = ForEach-Object { cmd /c dir /s/b /a-d "$sourcedir\*.iso" }
$dir_daa = ForEach-Object { cmd /c dir /s/b /a-d "$sourcedir\*.daa" }
$dir_nrg = ForEach-Object { cmd /c dir /s/b /a-d "$sourcedir\*.nrg" }
$dir_flp = ForEach-Object { cmd /c dir /s/b /a-d "$sourcedir\*.flp" }
foreach ($aname in $dir_iso,$dir_daa,$dir_nrg,$dir_flp) {
ECHO "$aname" archived.
}
But what this did, is clumped each item of each type together, then appended "archived" to that set. Something like:
C:\folder1\iso1.iso C:\folder1\iso2.iso C:\folder1\iso3.iso archived.
C:\folder2\image.nrg archived.
C:\folder3\app1.flp C:\folder3\app2.flp archived.
instead of:
C:\folder1\iso1.iso archived.
C:\folder1\iso2.iso archived.
C:\folder1\iso3.iso archived.
C:\folder2\image.nrg archived.
C:\folder3\app1.flp archived.
C:\folder3\app2.flp archived.
I'm having a real hard time with getting this to work. Can anyone help?
Thanks.
The first thing I see here is you are using this to get file information from the filesystem
$dir_iso = ForEach-Object { cmd /c dir /s/b /a-d "$sourcedir\*.iso" }
More specifically cmd /c dir /s/b /a-d "$sourcedir\*.iso". This would translate easily to Get-ChildItem
$dir_iso = Get-ChildItem -Path $sourcedir -Filter "*.iso" -Recurse -File | Select-Object -ExpandProperty FullName
Path is the file path of the folder you are checking
Filter you want only files ending with .iso
Recurse all subdirectories are checked
File returns only files and not directories (PowerShell 3.0 or higher!. There is a simple equivalent if this is an issue. )
Select-Object -ExpandProperty FullName would just return the full path of the files found in an array.
IF EXIST could be replaced by Test-Path
If(Test-Path "$aname.7z"){
Do stuff...
}
As for the ForEach loop foreach ($aname in $dir_iso,$dir_daa,$dir_nrg,$dir_flp) there are a couple good approaches with this but the simplest transistion would be
$dir_iso + $dir_daa + $dir_nrg + $dir_flp | ForEach-Object{
Do Stuff
}
I would probably build the file collection in one variable to begin with to avoid the to concat the arrays together
$files = Get-ChildItem -Path $sourcedir -Recurse -File | Where-Object{$_.Extension -match "(iso|daa|nrg|flp)$" } | Select-Object -ExpandProperty FullName
$files | ForEach-Object{
Write-Host "$_"
}
Use write-host instead of echo.
Multidimensional array, so you'll need an inner loop.
foreach ($aname in $dir_iso,$dir_daa,$dir_nrg,$dir_flp) {
foreach ($bname in $aname) {
write-host "$bname" archived.
}
}
Another possibility; this pipes the commands together rather than storing each in a separate variable.
Get-Item $sourcedir\* |
Where {$_.Extension -like ".iso" -or $_.Extension -like ".daa" -or $_.Extension -like ".nrg" -or $_.Extension -like ".flp"} |
Foreach-Object {
if (-Not (Test-Path "$_.BaseName.7z"))
{
Write-Host $_.FullName not yet archived.
}
else
{
Write-Host $_.FullName already archived.
}
}

How can I bulk rename files in PowerShell?

I'm trying to do the following:
Rename-Item c:\misc\*.xml *.tmp
I basically want to change the extension on every files within a directory to .tmp instead of .xml. I can't seem to find a straight forward way to do this in PowerShell.
From example 4 in the help documentation of Rename-Item retrieved with the command:
get-help Rename-Item -examples
Example:
Get-ChildItem *.txt| Rename-Item -NewName { $_.Name -replace '\.txt','.log' }
Note the explanation in the help documentation for the escaping backslash in the replace command due to it using regular expressions to find the text to replace.
To ensure the regex -replace operator matches only an extension at the end of the string, include the regex end-of-string character $.
Get-ChildItem *.txt | Rename-Item -NewName { $_.Name -replace '\.txt$','.log' }
This takes care of the case mentioned by #OhadSchneider in the comments, where we might have a file named lorem.txt.txt and we want to end up with lorem.txt.log rather than lorem.log.log.
Now that the regex is sufficiently tightly targeted, and inspired by #etoxin's answer, we could make the command more usable as follows:
Get-ChildItem | Rename-Item -NewName { $_.Name -replace '\.txt$','.log' }
That is, there is no need to filter before the pipe if our regex sufficiently filters after the pipe. And altering the command string (e.g. if you copy the above command and now want to use it to change the extension of '.xml' files) is no longer required in two places.
This works well too when you're in the desired directory.
Dir | Rename-Item –NewName { $_.name –replace "old","new" }
The existing answers suggest the -replace operator, but what if the file is called a.xml.xml? Both .xml substrings will be replaced and the end result would be a.tmp.tmp. Fortunately, there's a .NET method for this:
Dir *.xml | rename-item -newname { [io.path]::ChangeExtension($_.name, ".tmp") }
(Manish Kumar was close with GetFileNameWithoutExtension but this is more elegant and probably a bit more efficient, not that it overly matters in this case)
Here's another variant that will work.
dir *.xml | Rename-Item -NewName {$_.BaseName + ".tmp"}
$_.BaseName will do the "base" name without the (last) extension.
a shortened version using the alias would be:
ls *.xml | ren -new {$_.BaseName + ".tmp"}
dir -Recurse | where-object -FilterScript {$_.Extension -eq ".xml"} | Rename-Item -NewName {[System.IO.Path]::GetFileNameWithoutExtension($_.fullname) + ".tmp"}
use -WhatIf to evaluate the result first
Even easier - remember that the replace search string is a regular expression,
dir *.xml | rename-item -newname {$_.name -replace "xml$","tmp"}
The "$" represents end-of-string, so the characters "xml" must be the last three chars of the filename.
This seems to work and is a pythonic i.e simple is better than complex (https://www.python.org/dev/peps/pep-0020/) way of doing it (once you are in the directory):
$files = Get-ChildItem -file -Filter *.xml;
ForEach ($file in $files)
{
$n = $file.Basename
Copy-Item -Path $file -Destination "$n.tmp"
Remove-Item "$n.xml"
}

Resources