I am trying to build a script with a variable based on a text file of folder names, find them in one directory and move them to a different directory (or delete them in an additional script).
For example:
$foldernames = Get-Content -Path "C:\Users\Me\Desktop\Folders.txt"
$startpath = "C:\Users\Me\Desktop\Test"
$topath = "C:\Users\Me\Desktop\Moved"
Then find each line item in folder names in the $startpath and if they are found move them to the $topath.
this will do the job. [grin] it fakes reading in your text file of to-move dirs [use Get-Content to do that for real], then gathers a list of dirs in the source location. next, it tests to see if the .Name is in the list of dirs to be moved. finally, it runs Move-Item to move the dir.
note the -WhatIf on the end ... remove that when you are ready to do your live test on your test data set. [grin]
# fake reading in a text file
# in real life, use Get-Content
$ToMoveDirNameList = #(
'test1'
'TuTu'
'GoGoGadget'
'WPF'
)
$SourceDir = $env:TEMP
$DestDir = 'd:\temp'
$SourceDirList = Get-ChildItem -LiteralPath $SourceDir -Directory
foreach ($SDL_Item in $SourceDirList)
{
if ($SDL_Item.Name -in $ToMoveDirNameList)
{
Move-Item -LiteralPath $SDL_Item.FullName -Destination $DestDir -WhatIf
}
}
output ...
What if: Performing the operation "Move Directory" on target "Item: C:\Temp\test1 Destination: D:\temp\test1".
What if: Performing the operation "Move Directory" on target "Item: C:\Temp\TuTu Destination: D:\temp\TuTu".
What if: Performing the operation "Move Directory" on target "Item: C:\Temp\WPF Destination: D:\temp\WPF".
note #2 - there is NO error checking. this will give you lots of red error text if the destination already has a dir with the same name.
note #3 - there is no record of not-found dirs, either. [grin]
Related
I just want to know how to convert an images folder into a CBZ file READABLE for my Ebook (I checked the ebook and she can read this format).
Optimally, I would like to convert it without having to install everything. Just a script.
For those who are fast, I already answered my question... Just sharing it.
GO SEE UPDATE PART
Assuming your OS is Windows, we can do it with Batch or PowerShell.
For this one its process is quite easy, we just need to understand that a CBZ file IS a ZIP file with images in it. So we will just:
zip with 7zip because for some reasons the files converted with WinRAR didn't worked in my Ebook (wasn't even in the library) ;
Change the extension from .zip to .cbz.
I'm only going to show the PowerShell one because the .bat script had known issues.
Architecture
Architecture of the directory
The architecture should be:
My first folder
first image
second image
My second folder
first image
second image
PowerShell
Here's the code from my "#_ImagesFolderToCBZ.ps1"
Clear-Host
# INPUT - Script folder path
$i = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
# 7Zip path
$7zipPath = "$env:ProgramFiles\7-Zip\7z.exe"
# A list of the path of the zipped files
$listOfZippedFiles = New-Object Collections.Generic.List[String]
$listOfZippedNames = New-Object Collections.Generic.List[String]
# Ask the user if we delete the folders after their conversion
$isSdel = Read-Host -Prompt "Do you want to delete the folders after conversion [Y/N]: "
# Get the files inside the INPUT path and forEach children
Get-ChildItem "$i" | ForEach-Object {
# Get the full path of the file
$pathFolder = $_.FullName
# If the path here is a folder
if (Test-Path -Path "$pathFolder" -PathType Container) {
# Set the alias
Set-Alias 7z $7zipPath
# If the user asked for deletion of folders
if ("Y" -eq $isSdel.ToUpper()) {
# Zip the content of the folder
7z a "$pathFolder.zip" "$pathFolder\*" -sdel | FIND "ing archive"
}
else {
# Zip the content of the folder
7z a "$pathFolder.zip" "$pathFolder\*" | FIND "ing archive"
}
# Add the file name into the list
$listOfZippedFiles.Add("$pathFolder.zip")
$listOfZippedNames.Add("$_.zip")
}
# If the user asked for deletion of folders
if ("Y" -eq $isSdel) {
# Remove the now blank folders
if( $_.psiscontainer -eq $true){
if((gci $_.FullName) -eq $null){
$_.FullName | Remove-Item -Force
}
}
}
}
# For each zipped file
foreach ($file in $listOfZippedFiles) {
# Change the extension to CBZ
$dest = [System.IO.Path]::ChangeExtension("$file",".cbz")
Move-Item -Path "$file" -Destination $dest -Force
}
# Write for the user
Write-Host "`r`n`r`nConverted:"
# Displaying the converted files by their names
foreach ($file in $listOfZippedNames) {
$newName = [System.IO.Path]::ChangeExtension("$file",".cbz")
Write-Host "-- $newName"
}
# Blank line
Write-Host ""
# Pause to let us see the result
Pause
Output
output
As we can see, the folder is sucessfully created AND without loops like : I have ZIP files in the root folder of the script and they are also renamed into CBZ ones (I had this loop for my batch script).
I also added the choice to automatically delete the converted folders OR not.
Obviously, there's room for improvements (especially in how we delete the folders). I'll gladly take any advice.
UPDATE
I updated my script and it's much better. Less instructions, a list (in the prompt) that update itself when each folder is really converted. So no more: 1) ZIP all folders 2) rename their extension.
So a code that's more logic and also useful to show a beautiful process in real time.
Here's the updated code :
Clear-Host
# ROOT - Script folder path
$root = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
# 7Zip path
$7zipPath = "$env:ProgramFiles\7-Zip\7z.exe"
# Test if 7zip is installed
if (-not (Test-Path -Path $7zipPath -PathType Leaf)) {
throw "7 zip file '$7zipPath' not found"
}
# Ask the user if we delete the folders after their conversion
$isSdel = Read-Host -Prompt "Do you want to delete the folders after conversion [Y/N]: "
# Write for the user
Write-Host "`r`nConverted:"
# Get the files inside the INPUT path and forEach children
Get-ChildItem "$root" | ForEach-Object {
# Get the full path of the file
$pathFolder = $_.FullName
# If the path here is a folder
if (Test-Path -Path "$pathFolder" -PathType Container) {
# If the user asked for deletion of folders
if ("Y" -eq $isSdel.ToUpper()) {
# Zip the content of the folder while deleting the files zipped
& $7zipPath a "$pathFolder.zip" "$pathFolder\*" -sdel > $null
# Remove the now blank folder
if( $_.psiscontainer -eq $true){
if((gci $_.FullName) -eq $null){
$_.FullName | Remove-Item -Force
}
}
}
else {
# Zip the content of the folder
& $7zipPath a "$pathFolder.zip" "$pathFolder\*" > $null
}
# Change the extension to CBZ
$newName = [System.IO.Path]::ChangeExtension("$pathFolder.zip",".cbz")
Move-Item -Path "$pathFolder.zip" -Destination $newName -Force
# Tells the user this one is finished converting
Write-Host "--" -ForegroundColor DarkGray -NoNewline
Write-Host " $_.cbz"
}
}
# Tells the user it's finished
Write-Host "`r`nFinished`r`n" -ForegroundColor Green
# Pause to let us see the result
Pause
UPDATE 2
I made a GitHub project for this one. The URL is here:
https://github.com/PonyLucky/CBZ-Manga-Creator.
I have a folder with images that multiple people will be contributing, and I have multiple folders that other people will be creating with additional content, the folder names will be similar but not identical to the file names (see example below). I have tried writing a Powershell script (I'm still a bit green with it) that moves each image to its respective folder but I hit a wall, I'm getting the "Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Destination'. Specified method is not supported." I tried converting the destination to a string but had trouble looping through the list of folders. I'm adding the code here in hopes someone will spot the error I made in the code and will point me in the right direction. Thanks in advance for taking the time to help.
$sourceFolder = Get-ChildItem 'C:\Example_Files_Folder' -Recurse -Filter *.png
$targetFolder = Get-ChildItem -Path 'C:\Example_Target_Folder' -Recurse -Directory
foreach ($file in $sourceFolder) {
Where-Object { $targetFolder.Name -cin $sourceFolder.BaseName }
move-Item -Path $file.FullName -Destination $targetFolder.FullName -WhatIf
Write-Output "...copying $file"
}
<# ### Folder Structure
▼sourceFolder #Root Folder containing files
├───►looking inside this folder.png
├───►you should be able to find.png
├───►quite a long list of files.png
├───►matching many folders names.png
└───►that exist inside other folders.png
▼targetFolder #Root Folder
├───▼subfolder01
│ └───►looking inside #this is a folder
├───▼subfolder02
| └───▼subfolder002
| └───▼subfolder0002
| └───►you should be #also a folder
├───▼subfolder03
| └───►quite a long #not a file, just a folder
├───▼subfolder04
| └───►matching many folders #folder
├───▼subfolder05
| └───►sometimes with no matching file name #yep, a folder
├───▼subfolder06
| └─────►that exist within many folders #you get it
└───►subfolder07
### #>
Continuing from my comment ...
You can simplify this to say something like this:
# Target folder tree
$targetFolders = Get-ChildItem -Path 'D:\Temp\Target' -Recurse -Directory
# Results
<#
Directory: D:\Temp\Target
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 17-Feb-21 10:22 Book
d----- 17-Feb-21 10:10 TextData
Directory: D:\Temp\Target\Book
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 17-Feb-21 10:10 Date
#>
# Source folder files to move
Get-ChildItem 'D:\Temp' -Recurse -Filter '*.txt' |
ForEach {
ForEach ($targetFolder in $targetFolders)
{
If ($PSItem.BaseName -match $targetFolder.Name)
{Move-Item -Path $PSItem.FullName -Destination $targetFolder.FullName -WhatIf}
}
}
# Results
<#
What if: Performing the operation "Move File" on target "Item: D:\Temp\Book- Copy (2).txt Destination: D:\Temp\Target\Book\Book- Copy (2).txt".
....
What if: Performing the operation "Move File" on target "Item: D:\Temp\DateData.txt Destination: D:\Temp\Target\Book\Date\DateData.txt".
...
What if: Performing the operation "Move File" on target "Item: D:\Temp\TextData.txt Destination: D:\Temp\Target\TextData\TextData.txt".
#>
I have Several zip files that Contain multiple filetypes. The ONLY ones I am interested in are the .txt files. I need to extract the .txt files only and place them in a folder of their own, ignoring all other file types in the zips files.
All the zip files are in the same folder.
Example
-foo.zip
--1.aaa
--2.bbb
--3.ccc
--4.txt
-foo2.zip
--5.aaa
--6.bbb
--7.ccc
--8.txt
I want to have 4.txt and 8.txt extracted to another folder. I can't for the life of my figure it out and spent ages looking, googling and trying. Even managing to delete to zips once in a while :-)
Thanks in advance
Use the ZipArchive type to programmatically inspect the archive before extracting:
Add-Type -AssemblyName System.IO.Compression
$destination = "C:\destination\folder"
# Locate zip file
$zipFile = Get-Item C:\path\to\file.zip
# Open a read-only file stream
$zipFileStream = $zipFile.OpenRead()
# Instantiate ZipArchive
$zipArchive = [System.IO.Compression.ZipArchive]::new($zipFileStream)
# Iterate over all entries and pick the ones you like
foreach($entry in $zipArchive.Entries){
if($entry.Name -like '*.txt'){
# Create new file on disk, open writable stream
$targetFileStream = $(
New-Item -Path $destination -Name $entry.Name -ItemType File
).OpenWrite()
# Open stream to compressed file, copy to new file stream
$entryStream = $entry.Open()
$entryStream.BaseStream.CopyTo($targetFileStream)
# Clean up
$targetFileStream,$entryStream |ForEach-Object Dispose
}
}
# Clean up
$zipArchive,$zipFileStream |ForEach-Object Dispose
Repeat for each zip file.
Note that the code above has very minimal error-handling, and is to be read as an example
Try this:
Set-Location "Extraction path"
#("full path of foo.zip","full path of foo2.zip") | ForEach {
& "Full path of 7z.exe" x '-i!*.txt' $_.FullName
}
Sets location to the path where files will be extracted.
Passes a array of zip files to for loop.
Exexute 7z command to extract only zip files.
Here is one approach:
Go through each .zip file in a folder.
Extract archive into separate folder.
Extract .txt file from folder.
Copy files into destination folder containing all .txt files. This will overwrite files if they already exist in the destination folder.
Cleanup extracted folders once finished.
Demo:
function Copy-ZipArchiveFiles {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[ValidateScript({
if (-not(Test-Path $_ -PathType Container))
{
throw "The source path $_ does not exist. Please enter a valid source path."
}
else
{
$true
}
})]
[string]$Path,
[Parameter(Mandatory=$true)]
[ValidateScript({
if ([string]::IsNullOrEmpty($_.Trim()))
{
throw "The Destination path is null or empty. Please enter a valid destination path."
}
else
{
$true
}
})]
[string]$Destination,
[Parameter(Mandatory=$false)]
[AllowNull()]
[AllowEmptyString()]
[AllowEmptyCollection()]
[string[]]$Include
)
# Create destination folder if it doesn't already exist
if (-not(Test-Path -Path $Destination -PathType Container))
{
try
{
New-Item -Path $Destination -ItemType Directory -ErrorAction Stop
}
catch
{
throw "The destination path $Destination is invalid. Please enter a valid destination path."
}
}
# Go through each .zip file
foreach ($zipFile in Get-ChildItem -Path $Path -Filter *.zip)
{
# Get folder name from zip file w/o .zip at the end
$zipFolder = Split-Path $zipFile -LeafBase
# Get full folder path
$folderPath = Join-Path -Path $Path -ChildPath $zipFolder
# Expand .zip file into folder if it doesn't exist
if (-not(Test-Path -Path $folderPath -PathType Container))
{
Expand-Archive -Path $zipFile.FullName -DestinationPath $folderPath
}
# Copy files into destination folder
foreach ($file in Get-ChildItem $folderPath -Include $Include -Recurse)
{
Copy-Item -Path $file.FullName -Destination $Destination
}
# Delete extracted folders
Remove-Item -Path $folderPath -Recurse -Force
}
}
Usage:
Copy-ZipArchiveFiles `
-Path "C:\path\to\zip\files" `
-Destination "C:\path\to\text\files" `
-Include "*.txt"
Note: Could also use this for multiple extension types as well by passing -Include *.txt, *.pdf. I also went a bit overboard in the parameter error checking, but I believe in writing robust code. Good habit to get into when writing your own cmdlets anyways :)
File names
In the above picture is an example of the file structure.
I have looked at many cmd lines for mass renaming but they all have either the same prefix or the same amount of characters.
Is there a way to rename multiple files with different prefixes but keep the text and file type. so 111 tiger.txt to tiger.txt and 32133_lion.txt to lion.txt.
This is relatively easy in PowerShell. The key is using a regex match to strip off the leading digits and '_' and space.
C:>type .\rd\doit.ps1
[cmdletbinding()]
Param()
Get-ChildItem "C:\src\t\rd" |
ForEach-Object {
$_.Name -match "\d*[_ ]*(.*)" | Out-Null
if ($matches[1] -ne $null) {
if ($_.Name -ne $matches[1]) {
Rename-Item $_.FullName $matches[1] -WhatIf
}
}
}
When you believe that correct renaming will be done, remove the -WhatIf from the Rename-Item command.
C:>.\rd\doit.ps1
What if: Performing the operation "Rename File" on target "Item: C:\src\t\rd\123dog.txt Destination: C:\src\t\rd\dog.txt".
What if: Performing the operation "Rename File" on target "Item: C:\src\t\rd\124cat.txt Destination: C:\src\t\rd\cat.txt".
I'm trying to write a powershell script that will create outlook logs (using NETMON) from the local client to the exchange server. The problem is the file name is not incrementing correctly. I found this incrementing script online and i understand the logic (kinda). Am i using the right way to initialize the counter? It isn't renaming the file correctly. It gives the 2nd file just a _ then the 3rd file gets a _2 then after that it stops renaming files.
ALSO is invoke-expression waiting for my cmd to exit or will it just go to the next step of $count-- ?
#Directory to complete script in
$path = "c:\Outlook_Logs\"
cd $path
$cmdline = "Nmcap.exe /network * /captureipv4.address==X.X.X.X /file :\outlook_logs\client_$count.chn:100MB"
#Writes out number of files in directory to console
$count = (get-childitem $path -name).count
Write-Host "Number of Files: $count"
#Sorts items by decsending order
$items = Get-ChildItem | Sort Extension -desc
#Deletes oldest file by file extension number
del $items[0]
#Copy file from original directory to backup directory
Copy-Item c:\Outlook_Logs\* c:\Outlook_Logs_Temp\
#Sorts items by decsending order
$items = Get-ChildItem | Sort Extension -desc
#Renames files in quotes after NewName argument
$items | ForEach-Object -begin { $count= (get-childitem $path -name).count } -process { rename- item $_ -NewName "client_$count.cap";Invoke-Expression "$cmdline"; $count-- }
There's no need to increment the names yourself. chn captures files are created and names incrementally automatically when they reach the size specified after the colon.
What happens when you lower the size to 1MB and execute the command?