i'd like to flatten folder-structure and in one way include each parent directory name to filename. i've tried this, but get an error:
Missing ')' in method call
I quite don't see what's the problem
(ls -r -include *.ext) | % { mv -literal $_\3 $_\3.Name.Insert(0, [String]::Format("{0} - ", $_\3.Directory.Name))}
Try this:
ls . -r *.ext -name | ?{!$_.PSIsContainer} | mi -dest {$_ -replace '\\','_'} -whatif
Or if on V3:
ls . -r *.ext -name -file | mi -dest {$_ -replace '\\','_'} -whatif
Remove the -whatif to actually perform the move.
Do you want to flatten the folder structure and move all of the renamed files to the root directory? For example:
$rootPath = 'C:\TempPath'
(ls $rootPath -r -include *.ext) | %{
[string]$newFilename = $_.Name.Insert(0, [String]::Format("{0} - ", $_.Directory.Name))
#write-host $newFilename
mv -literal $_ "$rootPath$newFilename"
}
Related
I have the following structure:
mainfolder
folder1
000000.jpg
000001.jpg
000003.jpg
000004.jpg
folder2
000000.jpg
000001.jpg
folder3
000000.jpg
000001.jpg
000002.jpg
....
I want to copy and rename all the jpg-files. After the copying and renaming it should look like this:
mainfolder
folder1_000000.jpg
folder1_000001.jpg
folder1_000003.jpg
folder1_000004.jpg
folder2_000000.jpg
folder2_000001.jpg
folder3_000000.jpg
folder3_000001.jpg
folder3_000002.jpg
....
Can someone tell me how the PowerShell Skript for this looks like?
I would like to share the beautiful oneliner that lets you do this in PowerShell:
Get-ChildItem -Path mainfolder -Recurse -Filter "*.jpg" | % { $_ | Move-Item -Destination ("mainfolder\{0}_{1}" -f $_.Directory.Name, $_.Name) }
try this:
$DirRank=New-Object 'system.collections.generic.dictionary[string,int]'
$mainfolder="c:\temp"
Get-ChildItem $mainfolder -Recurse -File | %{
#Build or increment key for every sub-directory
if ($DirRank.ContainsKey($_.DirectoryName))
{
$DirRank[$_.DirectoryName]++
}
else
{
$DirRank[$_.DirectoryName]=0
}
#build new file name
$NewName="{0}\{1}_{2:d6}{3}" -f $mainfolder, $_.Directory.Name, $DirRank[$_.DirectoryName], $_.Extension
$fullename=$_.FullName
#rename and move item (remove -whatif for really rename)
move-Item $_.FullName $NewName -WhatIf -Force
}
On Windows XP, in a folder of files, I need to rename some files, replacing one character in the filename with another and overwriting any files that already have that name.
For example, the folder contains these 2 files:
fileA.xml
fileb.xml
I need to rename fileA.xml to fileb.xml, overwriting the original fileb.xml
Using PowerShell, I have this command:
Get-ChildItem *.* -include *.xml | Rename-Item -NewName { $_.name.Replace("A","b")}
The rename doesn't work as the file already exists.
Doesn't have to be done in PowerShell, but this is the closest I've come so far.
You can try the Move-Item command instead, with the -Force parameter.
Get-ChildItem . -include *.xml | Move-Item -Destination { $_.name.Replace("A","b")} -Force
First, you need to filter to get the files that you actually want to rename.
Get-ChildItem . -include *.xml | Where-Object { $_.name -match "A$" }
And feed this to Move-Item to rename:
Get-ChildItem . -include *.xml | Where-Object { $_.name -match "A$" } | Move-Item -destination { $_.name -replace "A$", "b" }
I have been looking for the way to do this using get-children and rename combined with if but no luck.
I want to search all subdirectories (subdir1, subdir2, etc) of a directory (test) for a file "trigger.txt" and if the file exists in one of these subdirectories I want to change the name of another file in the same subdirectory (changeme.txt to _changeme.txt)
Rephrasing:
If file subdir1\"trigger.txt" exists change subdir1\"changename.txt" to subdir1\"_changename.txt"
I have found scripts to change the name of file if it exists and to look for a file but I cannot connect it together... Any help?
Try this:
Get-ChildItem -r trigger.txt | Foreach {Rename-Item (Join-Path $_.DirectoryName changeme.txt) _changeme.txt}
And if you want the terse version:
ls -r trigger.txt | %{rni (join-path $_.DirectoryName changeme.txt) _changeme.txt}
Perhaps something like this?
ls -filter trigger.txt -recurse | %{
$original = Join-Path $(Split-Path $_.FullName -Parent) "ChangeMe.txt"
$newName = Join-Path $(Split-Path $_.FullName -Parent) "_ChangeMe.txt"
if ([IO.File]::Exists($original)) {
Write-Host "Renaming $($original)..."
ren -Path $original -NewName $newName
}
}
I have a cleanup script that moves files based on their extension to appropriate preset locations.
For example, a file with the extension .xls will be moved to ~\XLS folder, .sql to ~\SQL and so on. Here is the my script.
$dirtyfolder = "\\server\c$\Documents and Settings\user\Desktop\"
$org = "\\BACKUPS\users\"
dir $dirtyfolder -fil *.doc | mv -dest "$($org)ORG\doc"
dir $dirtyfolder -fil *.txt | mv -dest "$($org)ORG\txt"
dir $dirtyfolder -fil *.sql | mv -dest "$($org)ORG\sql"
dir $dirtyfolder -fil *.log | mv -dest "$($org)ORG\log"
dir $dirtyfolder -fil *.zip | mv -dest "$($org)ORG\zip"
dir $dirtyfolder -fil *.7z | mv -dest "$($org)ORG\zip"
dir $dirtyfolder -fil *.png | mv -dest "$($org)ORG\img"
dir $dirtyfolder -fil *.jpg | mv -dest "$($org)ORG\img"
dir $dirtyfolder -fil *.mp3 | mv -dest "$($org)ORG\mp3"
I am fully aware that this in an inelegant way to achieve my objective. So I would like to know how I can modify the script so that I can
reuse repetitive code
if the destination folder does not exist, it should be created.
group similar extensions, like png and jpg
Tested. A (not-recursive) solution that does not manage grouping:
ls $dirtyfolder/* | ? {!$_.PSIsContainer} | %{
$dest = "$($org)ORG\$($_.extension)"
if (! (Test-Path -path $dest ) ) {
new-item $dest -type directory
}
mv -path $_.fullname -destination $dest
}
Solution with grouping:
ls $dirtyfolder/* | ? {!$_.PSIsContainer} | %{
$dest = "$($org)ORG\$(get-destbytype $_.extension)"
if (! (Test-Path -path $dest ) ) {
new-item $dest -type directory
}
mv -path $_.fullname -destination $dest
}
where get-destbytype is the following function:
function get-destbytype($ext) {
Switch ($ext)
{
{$ext -match '(jpg|png|gif)'} { "images" }
{$ext -match '(sql|ps1)'} { "scripts" }
default {"$ext" }
}
}
This is my working test
$source = "e:\source"
$dest = "e:\dest"
$file = gci $source | ? {-not $_.psiscontainer}
$file | group -property extension |
% {if(!(test-path(join-path $dest -child $_.name.replace('.','')))) { new-item -type directory $(join-path $dest -child $_.name.replace('.','')).toupper() }}
$file | % { move-item $_.fullname -destination $(join-path $dest -child $_.extension.replace(".",""))}
The script will find all different extensions within source folder. For each extension, if the folder doesn't already exist within destination, it will be created.
Last row will loop each file from source and move it to the right subfolder destination.
If you want to put images with different extensions within the same folder you need to make some further check, using an if or a switch statement.
I am trying to rename a bunch of files recursively using Powershell 2.0. The directory structure looks like this:
Leaflets
+ HTML
- File1
- File2
...
+ HTMLICONS
+ IMAGES
- Image1
- Image2
- File1
- File2
...
+ RTF
- File1
- File2
...
+ SGML
- File1
- File2
...
I am using the following command:
get-childitem Leaflets -recurse | rename -newname { $_.name.ToLower() }
and it seems to rename the files, but complains about the subdirectories:
Rename-Item : Source and destination path must be different.
I reload the data monthly using robocopy, but the directories do not change, so I can rename them by hand. Is there any way to get get-children to skip the subdirectories (like find Leaflets -type f ...)?
Thanks.
UPDATE: It appears that the problem is with files that are already all lower case. I tried changing the command to:
get-childitem Leaflets -recurse | if ($_.name -ne $_name.ToLower()) rename -newname { $_.name.ToLower() }
but now Powershell complains that if is not a cmdlet, function, etc.
Can I pipe the output of get-childitem to an if statement?
UPDATE 2: This works:
$files=get-childitem Leaflets -recurse
foreach ($file in $files)
{
if ($file.name -ne $file.name.ToLower())
{
rename -newname { $_.name.ToLower() }
}
}
Even though you have already posted your own answer, here is a variation:
dir Leaflets -r | % { if ($_.Name -cne $_.Name.ToLower()) { ren $_.FullName $_.Name.ToLower() } }
Some points:
dir is an alias for Get-ChildItem (and -r is short for -Recurse).
% is an alias for ForEach-Object.
-cne is a case-sensitive comparison. -ne ignores case differences.
$_ is how you reference the current item in the ForEach-Object loop.
ren is an alias for Rename-Item.
FullName is probably preferred as it ensures you will be touching the right file.
If you wanted to excludes directories from being renamed, you could include something like:
if ((! $_.IsPsContainer) -and $_.Name -cne $_.Name.ToLower()) { ... }
Hopefully this is helpful in continuing to learn and explore PowerShell.
Keep in mind that you can pipe directly to Rename-Item and use Scriptblocks with the -NewName parameter (because it also accepts pipeline input) to simplify this task:
Get-ChildItem -r | Where {!$_.PSIsContainer} |
Rename-Item -NewName {$_.FullName.ToLower()}
and with aliases:
gci -r | ?{!$_.PSIsContainer} | rni -New {$_.FullName.ToLower()}
There are many issues with the previous given answers due to the nature of how Rename-Item, Piping, Looping and the Windows Filesystem works. Unfortunatly the the most simple (not using aliases for readability here) solution I found to rename all files and folders inside of a given folder to lower-case is this one:
Get-ChildItem -Path "/Path/To/You/Folder" -Recurse | Where{ $_.Name -cne $_.Name.ToLower() } | ForEach-Object { $tn="$($_.Name)-temp"; $tfn="$($_.FullName)-temp"; $nn=$_.Name.ToLower(); Rename-Item -Path $_.FullName -NewName $tn; Rename-Item -Path $tfn -NewName $nn -Force; Write-Host "New Name: $($nn)";}
slight tweak on this, if you only want to update the names of files of a particular type try this:
get-childitem *.jpg | foreach { if ($_.Name -cne $_.Name.ToLower()) { ren $_.FullName $_.Name.ToLower() } }
this will only lowercase the jpg files within your folder and ignore the rest
You need to temporarily rename them to something else then name them back all lower case.
$items = get-childitem -Directory -Recurse
foreach ($item in $items)
{
if ($item.name -eq $item.name.ToLower())
{
$temp = $item.FullName.ToLower() + "_"
$name = $item.FullName.ToLower()
ren $name $temp
ren $temp $name
}
It's more idomatic in PowerShell to use where instead of if in a pipeline:
gci -Recurse Leaflets |
? { $_.Name -ne $_.Name.ToLower()) } |
% { ren -NewName $_.Name.ToLower() }
A small but important correction to the answer from Jay Bazuzi. The -cne (case sensitive not equal) operator must be used if the where-part should return anything.
Additionally I found that the Path parameter needed to be present. This version worked in my setup:
gci -Recurse |
? { $_.Name -cne $_.Name.ToLower() } |
% { ren $_.Name -NewName $_.Name.Tolower() }
for everyone who is following this thread; the following line can also be used to lower both files and directories.
Get-ChildItem -r | Rename-Item -NewName { $_.Name.ToLower().Insert(0,'_') } -PassThru | Rename-Item -NewName { $_.Name.Substring(1) }
Main post: https://stackoverflow.com/a/70559621/4165074