PowerShell 5 (now updated to 7) - Check if multiple levels / depths of subfolders exists and if so move its contents up one level - if-statement

Normally I wouldn't even post without some framework together, but I'm stumped on where to begin.
I have a directory of hundreds of subfolders that should only be one level deep but some are two levels deep:
C:\MainDirectory\SubfolderA\
C:\MainDirectory\SubfolderB\
C:\MainDirectory\SubfolderC\
C:\MainDirectory\SubfolderC\SubfolderC1\
I need to recursively go through the MainDirectory, and if there's a second sublevel of files, then move just those files up one level (so that SubfolderC1's files are actually placed in SubfolderC) and then those files need to have SubFolderC1's name appended to the front of the file.
Ex:
C:\MainDirectory\SubfolderC\SubfolderC1\FileA.type
would become:
C:\MainDirectory\SubfolderC\SubfolderC1FileA.type
I don't know the subfolders or filenames ahead of time, they could be named anything.
The following feels like a start.
$source = "C:\MainDirectory"
$levels = Get-ChildItem $source -Directory -Recurse | ForEach-Object {$_.PSPath.Split('\').Count}
if ($levels -gt 3) {
}
But I don't know how to use the results for anything meaningful.

Related

remove some character string from multiple filenames

my OS is Win 10. i have a folder which contains files like: '1.JPG.JPG.jpg' or '99.JPG.jpg' or '335.JPG.JPG.jpg' -almost 470 files-
notice that the file names are look like value of ID column to reference and some files have 2 times '.JPG' but some have 1 time. also there are some files contains 'jpg' instead of 'JPG' (both without quotes) in between file name.
i want to rename all files with number value in start of file name and then add .jpg to all files like 1.jpg or 99.jpg or 335.jpg etc. this is sure all files are jpeg, there is no .png or .bmp etc.
please help how i can do this?
EDIT: can i used to get digit part of file name and hard code .jpg and replace all file names at once using script, if yes, please guide how it can be done?
its done using following Powershell commands searched from internet:
step 1) ls | Rename-Item -NewName {$_.name -replace ".JPG.JPG",""}
step 2) ls | Rename-Item -NewName { [io.path]::ChangeExtension($_.name, "jpg") }
hope peoples like me (the novice) will get benefit.
Link: https://www.windowscentral.com/how-rename-multiple-files-bulk-windows-10#:~:text=You%20can%20press%20and%20hold,file%20to%20select%20a%20group.

How do I match a filename without quotes to a name with quotes?

If somebody knows a better title, please do tell me.
Given these examples:
Three files called: Jacobs.Ladder.txt, Emma.Unplugged.2020.txt & Emma.txt
Three folders called: Jacobs.Ladder.2019, Emma.Unplugged & Emma
Three database-entries called: Jacob's Ladder, Emma: Unplugged & Emma
My PowerShell script is presented with the file, then needs to find the right folder, copy it there, then find the entry in the database and update it there. I need a script that catches all three cases.
So far, what I've done is this:
I take the file's BaseName property:
Jacobs.Ladder, Emma.Unplugged, Emma
I use Get-ChildItem to put all directories in a variable, do:
$Directories | Where-Object -Property 'Name' -Contains $File.BaseName
This finds exact matches, so it will only find the folder Emma
Next, if nothing was found, I do:
$Directories | Where-Object -Property 'Name' -Like ('{0}*' -f $File.BaseName)
This finds the folder Jacobs.Ladder.2019. It has already found Emma in the previous step, so that has been handled. It will not however, find Emma.Unplugged because the file's BaseName is Emma.Unplugged.2020.
So that's my first challenge/question: how do I write a conditional that matches Emma.Unplugged.2020 to Emma.Unplugged?
Next, to match the file name with the database entries, I start with a replace to turn the dots into spaces.
I then search for an almost exact match:
$Database | Where-Object -Property 'Name' -Like $FileNameWithSpaces.Replace(' ','*')
I know, I could have just replaced the dots with asterisks. But I don't know yet if the name with spaces is going to be useful somewhere else.
Although this does compensate for the colon in Emma.Unplugged, this once again only finds Emma.
There is my second challenge: how do I form my query to match (again) Emma.Unplugged.2020 to Emma: Unplugged but also Jacobs.Ladder to Jacob's Ladder?
Of course, if you can think of more scenarios that I didn't catch here, feel free to add those in your code.
As for the topic title, my biggest challenge is that single quote in the word. Right now the best way I can think of is if I on-the-fly turn all database-names into their filename equivalent. Then Jacob's Ladder would become Jacobs.Ladder and I would have a match. Which would leave the 2020 as my biggest challenge.

Rename multiple files with different names to same name and different numbers

I have multiple pictures of trucks with random messy names and different formats (jpeg, jpg, png etc.) and I want to rename them to "truck1.jpeg", "truck2.jpg", "truck3.png" and so on. How do I do it using the rename command?
It's probably easier to use bash and mv, since AFAIK you need something like bash to generate the number sequence. In bash
i=1
for x in *; do
echo $x '->' truck$i.${x##*.}
mv "$x" truck$i.${x##*.} && i=$((i+1))
done
The for x in * operates on all files whose names do not begin with a dot and are in the current directory. You can adjust the glob to be more exclusive, but this script will need modification if the files are in other directories. Again, probably easier to collect the files in one directory, or maybe put it in a script file and execute it in multiple directories using find ... -exec.
This uses i as a counter to generate the digits. The trick is the ${x##*.} expression which takes the file name and deletes everything up to the final dot. This allows you to preserve and reattach the file extension to the new name. You have to be careful to set i correctly or you will overwrite old truck1 files with new ones.

Check if string is like regex

I have a folder of many applications that are used for deployment on laptops or workstations. Now this folder is becoming a big mess because multiple people use this folder and everyone uses a different storage method. Therefore I wanted to write a script that helps manage the files in a way we can always find them.
In Powershell I want to.
List all the files (.msi, .exe, and maybe more)
Determine if the files are correctly stored already (Developer\Application\Application_version_architecture.extension ie. Adobe\Flashplayer\Flashplayer_22_x64.msi)
If true, leave it.
If not, I question the user of the script
some things about the application so the script then renames and
moves it to the correct folder.
Currently I'm stuck on step 2. I want to use a regex where I determine what the standard should be. However, it keeps ruling out applications that are correctly named.
I use the following command to retrieve the filenames:
Get-ChildItem -Path $path -Recurse -Name
This retrieves the files in the application folder with complete path like
"Adobe\Flashplayer\Flashplayer_22_x64.msi"
Or when incorrect
"Adobe\flashplayeractivex.msi"
I then use the following regex to check if they are correct or incorrect
\w*\\\w*\\[a-zA-Z]*\_[0-9a-zA-Z\.]*\_(([x][6][4])|([x][8][6])|([b|B][o][t][h]))\.(([m|M][s|S][i|I])|([e|E][x|X][e|E]))
Which I have confirmed working on Rubular.
However, I cannot get it working with powershell. I've tried the following:
if ($file -match '\w*\\\w*\\[a-zA-Z]*\_[0-9a-zA-Z\.]*\_(([x][6][4])|([x][8][6])|([b|B][o][t][h]))\.(([m|M][s|S][i|I])|([e|E][x|X][e|E]))') {......commands...}
Which does not seem to work because of the escapes (Powershell threw some errors at me). I then tried:
$pattern = [regex]::Escape('\w*\\\w*\\[a-zA-Z]*\_[0-9a-zA-Z\.]*\_(([x][6][4])|([x][8][6])|([b|B][o][t][h]))\.(([m|M][s|S][i|I])|([e|E][x|X][e|E]))')
if ($file -match $pattern) {......commands...}
Which didn't gave me errors, but did not work because it didn't "match" "Apple\iTunes\iTunes_12.3_x64.exe" which does match on Rubular.
Does anyone recognize this problem or see what I do wrong?
I wouldn't try all this in a single regex. Instead I would check each policy individually:
$path = 'C:\tmp'
$validExtensions = #('.msi', '.exe')
$filnameRegex = '\w+_[0-9a-zA-Z\.]+_(?:x32|x64|[b|B]oth)'
Get-ChildItem -Path $path -Recurse | ForEach-Object {
if (-not ($_.Extension -cin $validExtensions))
{
Write-Host "$($_.FullName) has an invalid extension."
}
if (-not ($_.BaseName -match $filnameRegex))
{
Write-Host "$($_.FullName) doesn't match the filename policy."
}
if (3 -ne ($_.FullName.Split([System.IO.Path]::DirectorySeparatorChar).Length `
- $path.Split([System.IO.Path]::DirectorySeparatorChar).Length))
{
Write-Host "$($_.FullName) doesn't match the directory policy."
}
}

Regex and shell - multiple recursive rename

I have a folder with several hundreds of folders inside it. These folders contain another folder each, called images, and in this folder there is sometimes a strictly numerically named .jpg file. Sometimes there are other JPG files in the folder as well, but these need to be ignored if they aren't strictly numeric.
I would like to learn how to write a script which would, when run in a given folder, traverse every single subfolder and look for this numeric file. It would then add the "_n" suffix to a copy of each, if such a file does not already exist.
Can this be done through the unix terminal easily?
To be more specific, this is the structure I'm dealing with:
master folder
18556
images
2234.jpg
47772
images
2234.jpg
2234_n.jpg
some_pic.jpg
77377
images
88723
images
22.jpg
some_pic.jpg
After the script is run, the situation would look like this:
master folder
18556
images
2234.jpg
2234_n.jpg
47772
images
2234.jpg
2234_n.jpg
some_pic.jpg
77377
images
88723
images
22.jpg
22_n.jpg
some_pic.jpg
Update: Sorry about the typo, I accidentally put 2235 into 47772.
Update 2: Regarding the 2nd comment on the mathematical.coffee's answer, the OS I am currently on (at work) is MacOS, but my main machines are running CentOS and Ubuntu at home, so I just assumed my situation applies to all unix based systems.
You can use the -regex switch to find to match /somefolder/images/numeric.jpg:
find -type f -regex './[^/]+/images/[0-9]+\.jpg$'
Edit: refinement from #JonathanLeffler: add -type f to find so it only finds files (ie don't match a directory called '12345.jpg').
The ./[^/]+/ is for the first folder (if that first folder is always numeric too you can change it to [0-9]+).
The [0-9]+\.jpg$ means a jpg file with file name only being numeric.
You might want to change the jpg to jpe?g to allow .jpeg, but that's up to you.
Then it's a matter of copying these to xxx_n.jpg.
for f in $(find -type f -regex './[^/]+/images/[0-9]+\.jpg$')
do
# replace '.jpg' in $f (filename) with '_n.jpg'
newf=${f/\.jpg/_n\.jpg}
# see if this new file exists
if [ ! -f $newf ];
then
# if not exists, copy it.
cp "$f" "$newf"
fi
done
What should be the logic behind the renames in Folder 47772? If we assume you want to rename all the files just consisting of numbers to numbers + _n
With mmv you could write it like:
mmv "[0-9][0-9]*.jpg" "#1#2#3_n.jpg"
Note: mmv is for moving; mcp is for copying, and so is more appropriate to this question.
Question of Vader:
Well I checked the man page and the problem is that it's a bit strange.
I was thinking [0-9]* would match zero or more numbers. I turns out that this assumption was wrong.
The problem is that I could not tell I want two or more numbers at the start of the name.
So [0-9][0-9]* matches a name starting with at least two numbers (after that it takes all the rest up to the .. Now every [0-9] is one pattern and so I had to make the to pattern into:
"#1#2#3_n.jpg" With e.g 1234.jpg I have #1 = 1; #2 = 2, #3 = 34 So
#1#2#3 -> 1234; _n appends the _n and .jpg the extension
However it would rename also files with 12some_other_stuff.jpg sot 12some_other_stuff_n.jpg. It's not ideal but achieves in this context what was intended.