Enter individual folder(s) and execute PowerShell command - powershell

I have many folders with even more subfolders, and as posted in my first question
How to create a powershell script / or windows .bat file for ffmpeg
I want to encode all video files in the folders.
The Script I got from mklement0 works fine but lazy as I am, I was wondering if there was a way to tell the PowerShell to enter folder 1, go to subfolder_1, and execute the ps1 script (would be perfect if it executed in a new powershell instance), wait a certain time and go into subfolder_2
Repeat until no more subfolders available.
Is this possible?
Edit:
The Script I got:
Get-ChildItem *.mkv | where BaseName -notlike '*`[encoded]' | foreach {
ffmpeg -i $_ -c:v libx265 -c:a copy -x265-params crf=25 "$($_.BaseName)[encoded].mkv"
pause
}
What is the reason for the desire to process each subfolder in a separate instance of powershell.exe? by Mathias R. Jessen
Because I want to encode multiple folders at once to save some time.
If there is a way to execute the script in the same PowerShell (as far as my understanding goes, I can only encode one folder at one time if I use the same PowerShell instance)

You could wrap the whole thing in another Get-ChildItem to find all the subdirectories in your main folder, then set-location to that path and run in each of those:
$directories = get-childitem -path "C:\Path\To\Videos\Folder" -Recurse -Directory
Foreach ($dir in $directories) {
Set-Location $dir.fullname
*Run your code here*
}
This would be better than trying to do them all in parallel with different PowerShell instances. Correct me if I'm wrong, but video encoding uses a lot of your computer's resources, so trying to run them in parallel would actually be more of a hindrance.

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.

Powershell Cannot create a file when that file already exists when using Move-Item

I'm trying to move png images in subfolders to a subfolder in the folder they are in.
Main folder is called "stuff", images are in variously named subfolders in the "stuff" main folder, and each of these subfolders have a folder name "Oneshot" in them, I'm trying to move these images that are in M:/Stuff/FolderExample/ to M:/Stuff/FolderExample/Oneshot/.
Current code I am using
Get-ChildItem -Path C:\Users\a\Desktop\stuff\*.png -Recurse | Move-Item -Destination "$($_.FullName)\Oneshot\"
How can I make this work? I'm just trying to learn powershell so I can automate this process as I otherwise would need to repeat the process manually for thousands of times
Use a scriptblock ({...}) as the argument to -Destination - this will allow you to access the current pipeline item as $_ during parameter binding:
Get-ChildItem -Path C:\Users\a\Desktop\stuff\*.png -Recurse |Where-Object Directory -notlike *\OneShot | Move-Item -Destination {"$($_.Directory.FullName)\Oneshot\"}
The Where-Object command in the middle of the pipeline will ensure we don't attempt to move pictures already in one of the OneShot folders.
Powershell is not the best idea to move files. The main reason is that you run into problems in case the path becomes too long >256 characters. I would use robocopy for such tasks. It is part of every current windows version.
robocopy M:/Stuff/FolderExample/ M:/Stuff/FolderExample/Oneshot/ /MIR
Please keep in mind that this code will also delete everything that is in M:/Stuff/FolderExample/Oneshot/ and not in M:/Stuff/FolderExample/. So use /MIR with caution.
You could still invoke robocopy in an ps1 file and wrap other powershell code around it e.g. for variables.

What can I do to automatic run a bat file when a new item on a folder is added?

I have a ps1 file which contains the code and a bat file which when i double click runs the code.
The ps1 file renames all files in that folder.
Here is the code:
get-childitem -path C:\Users\ASUS\Videos\Downloads\*.mp4 | foreach {rename-item $_ $_.name.replace("_"," ")}
How can I run the bat file to automatically rename all files that are added to that folder?
I saw #olaf mentioned FileSystemWatcher and, honestly, it one of the better options for what you're trying to do.
Alternatively, you can use Task Scheduler to run at specific times, dates, actions, etc. There's also no need to point to the bat file, it can run ps1 files just as easy without having to have .bat execute the .ps1.
Change your ps1 to the following. It's passing only the items that were created less than 30 minutes ago; can be changed to days, minutes, etc. Then, it's just renaming them(:
Get-ChildItem -Path C:\Users\ASUS\Videos\Downloads\*.mp4 | Where-Object {$_.CreationTime -gt (Get-Date).AddMinutes(-30)}| foreach {rename-item $_.FullName $_.name.replace("_"," ")}

Need Powershell script to remove all spaces from *.msg files in directory and subdirectories

I have a whole bunch of users that are simply dragging emails from outlook on to a network share. That network SMB share gets sync'd with OneDrive and one drive hates to have leading spaces in a filename. Also some of the file names are so large that windows refuses to let me do anything with it (over 255 characters). I've found that simply renaming the files and taking out all the spaces does the trick. I have thousands of files in many sub directories and they keep adding more every day. I'd like a simple script that I can run everyday on the file server that scans all the *.msg files in a directory and all the sub directories and simply removes all the spaces. Can someone help me with a powershell script that will accomplish this?
The following variation of your own answer should perform significantly better:
Get-ChildItem -Recurse -Filter *.msg |
Rename-Item -NewName { $_.Name.Replace(' ', '') }
Note: You could add -File to Get-ChildItem in order to limit matches to files for extra robustness, but it actually slows down the command a bit (not much).
-Filter filters at the source and is therefore much faster than using -Path (implied in your command) or -Include, which require PowerShell itself to examine all items.
Using a delay-bind script block as the -NewName argument performs better than a call to the ForEach-Object cmdlet in whose script block Rename-Item is called once every iteration.
Note:
There's a risk of name collisions.
Attempts to rename a file to itself - if the name contains no spaces - are quietly ignored.
This script works great:
get-childitem -recurse *.msg | foreach { rename-item $_ $_.Name.Replace(" ", "") }

If a file is present move another file in another folder

in a particular folder I have files created with random name for example:
file1.xml
file2.xml
when these files are succesfully created, a .ack file is created.
So I will have
file1.xml
file1.ack
file2.xml
file2.ack
What I have to do:
Move a .xml file only if the corresponding .ack is created.
The difficult part: file names are random and I have no control over them.
Is there a way to create a .bat or a powershell to check and move with these requirements run at scheduled times?
Many thanks for your help
the ideal would be a powershell task, indeed. Now, you will want to leverage window's Scheduled Tasks in order to run it at a scheduled time.
In a nutshell, what you'll have to do with powershell is to
List the xml files in your folder with Get-ChildItem -filter "*.xml"
Pipe it to a Where-Object statement to make sure the .xml has a .ack counterpart, leveraging Test-Path
For each produced item, move the .xml file.
Move-Item -Path $source -Destination $target
Optionally, you could also clean the .ack files with Remove-Item.
Find every filename that appears with two extensions, and move them both.
gci | group basename |? Count -eq 2 | select -expand group | move -Dest c:\temp
Because it's fun not to use any loops. If you want loops, maybe: to move the XML and delete the .ack.
gci *.ack |% { move ($_.BaseName +'.xml') "c:\temp" ; rm $_ }