If a file is present move another file in another folder - powershell

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 $_ }

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. Combine text files in folders across multiple directories

I have tried to do my research, but I can't fathom this one out.
I can combine multiple .txt files in a folder. no problem:
dir C:\Users\XXXX\AC1\22JUN *.txt | get-content | out-file C:\Users\XXXX\22JUN\AC1_22JUN.txt
however, I have 14 Directories each with subdirectories. (months of the year), and this will only ever grow. How can I write it so that it will go into each directory AC1 - AC14 and then look into each folder JAN-DEC and in each subdirectory create a combined file for AC1_22JUN, AC2_22JUN AC1_22JUL, AC2_22JUL and so on and so on?
is there also a way to rename the output file with data, such as the number of .txt files that have been combined. i.e. AC1_22JUN_314.txt
many thanks in advance
What you need to do is iterate over all your directories and their subdirectories and run a particular command in each of them. This is easy enough to achieve using the cmdlet Get-ChildItem and a pair of nested foreach loops.
In addition, you need to count the number of text files you've processed so that you can name your aggregate file appropriately. To do this you can break your pipeline using the temporary variable $files. You can later begin a new pipeline with this variable and use its count property to name the aggregate file.
The code for this is as follows:
$dirs = Get-ChildItem -Directory
foreach ($dir in $dirs)
{
$subdirs = Get-ChildItem $dir -Directory
foreach ($subdir in $subdirs)
{
$files = Get-ChildItem *.txt -Path $subdir.fullname
$name = "$($dir.name)_$($subdir.name)_$($files.count).txt"
$files | Get-Content | Out-File "$($subdir.fullname)/$name"
}
}
A few things to note:
The script needs to be run from the containing folder - in your case the parent folder for AC1-AC14. To run it from elsewhere you will have to change the first statement into something like $dirs = Get-ChildItem C:\path\to\container -Directory
Get-ChildItem is the same as the command dir. dir is an alias for Get-ChildItem. This is NOT the same as the variable $dir which I've used in the script.
Running the script multiple times will include any and all of your old aggregate files! This is because the output file is also a .txt file which is caught in your wildcard search. Consider refining the search criteria for Get-ChildItem, or save the output elsewhere.

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.

How to prevent PowerShell -Recurse from renaming first file twice?

When using powershell to rename files with their directory name and file name, my code works, except in the first file in a directory, it gives it two copies of the directory name. So the file book1.xlsx in folder folder1 should become folder1book1.xlsx but it becomes folder1folder1book1.xlsx. The remaining files in folder1 are correctly named folder1book2.xlsx, folder1book3.xlsx, etc.
I have a directory, with many sub-directories. In each sub-dir are files that need their sub-dir name added in.
I've been following this code. For me it looks like:
dir -Filter *.xlsx -Recurse | Rename-Item -NewName {$_.Directory.Name + "_" + $_.Name}
I've also tried
--setting the Recurse -Depth 1 so that it doesn't keep looking for folders in the sub-folders.
--using ForEach-Object {$_ | ... after the pipe, similar to this.
--running it in Visual Studio Code rather than directly in PowerShell, which turns it into:
Get-ChildItem "C:\my\dir\here" -Filter *.xls -Recurse | Rename-Item -NewName {$_.DirectoryName + '_' + $_.Name}
--putting an empty folder inside the sub-directory, setting -Depth 2 to see if that will "catch" the recurse loop
I would expect the files to be named folder1_book1.xlsx, folder1_book2.xlsx, folder1_book3.xlsx.
But all of the attempted changes above give the same result. The first file is named folder1_folder1_book1.xlsx [INCORRECT], folder1_book2.xlsx[CORRECT], folder1_book3.xlsx[CORRECT].
A workaround might be writing an if statement for "not files that contain the sub-directory name" as suggested here. But the link searches for a text string not an object (probably not the correct term) like #_.Directory.Name. This post shows how to concatenate objects but not something like #_.Directory.Name. Having to put in an if statement seems like an unnecessary step if -Recurse worked the way it should, so I'm not sure this workaround gets at the heart of the issue.
I'm running windows 10 with bootcamp on a 2018 iMac (I'm in Windows a lot because I use ArcMap). Powershell 5.1.17134.858. Visual Studio Code 1.38.0. This is a task I would like to learn how to use more in the future, so explanations will help. I'm new to using PowerShell. Thanks in advance!
This was a script I created for one of my customers that may help
<##################################################################################################################################
This script can be used to search through folders to rename files from their
original name to "filename_foldername.extension". To use this script
please configure the items listed below.
Items to Congfigure
-$Original
-$Source
-$Destination
-$Files
Also please change the Out-File date on line 29 to today's date ****Example: 2019-10-02****
We've also added a change log file that is named "FileChange.txt" and can be found in the location identified on line 30
>
$Original="C:\temp\test" #Location of ".cab" files copied
$Source="C:\temp\Test" #Location were ".cab" files are stored
$Destination="C:\temp\Test\2019-10-02" #Location were you want to copy ".cab" files after the file name change. Be sure to change the date to the date you run this script. The script creates a folder with todays date
$Files=#("*.cab") #Choose the file type you want to search for
$ErrorActionPreference = "SilentlyContinue" #Suppress Errors
Get-ChildItem $Original -Include "*.cab" -File -Recurse | Rename-Item -NewName {$_.BaseName+"_"+$_.Directory.Name +'.cab'}
New-Item -ItemType Directory -Path ".\$((Get-Date).ToString('yyyy-MM-dd'))"; Get-ChildItem -recurse ($Source) -include ($Files) | Copy-Item -Destination ($Destination) -EA SilentlyContinue
Get-ChildItem $Original | Where {$_.LastWriteTime -ge [datetime]::Now.AddMinutes(-10)} | Out-File C:\temp\test\2019-10-02\FileChange.txt

Copy files in a directory, to folders with same name as file

I have multiple files that I want to copy each file to a folder with same name
For example, the files
orange_file100 , orange_file200 , orange_file300 , apple_file120 , apple_file150
I want to move each file to a folder that contain part of the filename say orange and apple so the result will be
orange\orange_file100
orange\orange_file200
orange\orange_file300
apple\apple_file120
apple\apple_file150
How can I do that through powershell, should I use Get-ChildItem then ForEach{Copy-Item) ?
You can use Get-Childitem with a -File or -Directory to only grab the files or folders in a folder, that way you wont grab a folder and try place it in itself.
For example, the code below will only grab the files in the current directory
Get-Childitem -File
You can then use some regex to split the names so you can get the fruit name e.g.
$String.split('_')[0]
You should insert it into a list or array or something to store it, but now you have a list of files and fruit names.
Now you can loop over the list and start to move or copy the files into the right folder structure
Foreach($file in $FileList){
if($file.name -matches $Fruitname){
if($file.name -notmatch $pwd.path ){
mkdir $file.name
cd $file.name
move-item $file.fullname $pwd
}
}
}
The code above is just a quick attempt. It probably wont work the first time and you should make adjustments to understand what you are doing.
A few notes
$pwd gets the current directory. I'm assuming Get-Childitem returns the list of files in the correct order, so you will get Orange_100, then Orange_200 and so on
Get-Childitem returns a powershell object. The file names can be accessed using $_.name or the full path using $_.fullname
If -matches doesn't work, you can also try -like or -in
I didn't add in the first fruit folder into the code above, but it won't be hard to create
Remember to play around and find whats best for you.