I need to convert batches of TIFF images to JPEGs. Can it be done with plain vanilla PowerShell, without installing ImageMagick?
After a little research here and here, it seems it can be done. First, by loading a .NET Framework assembly:
[Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”);
But since I'm saving the converted image in a different directory, removing the first four characters of the filename, and changing the extension to jpg, I'm stuck with the syntax:
Get-ChildItem *.tif | %{ $file = new-object System.Drawing.Bitmap($_.FullName); $file.Save({ Join-Path "C:\Users\oscar\Downloads\" ($_.Name -replace '^.{4}(.*?)', '$1.jpg') },"JPEG") }
I get an "invalid characters in path name" error.
I'm not sure if your problem is situated in conversion or creating the new file name. If file name creation is the problem you can try following. Example:
PS C:\temp> "test.txt" -replace "txt", "somethingOther"
In your case:
Get-ChildItem -Include *.tif | %{ $file = new-object System.Drawing.Bitmap($_.FullName); $file.Save(( Join-Path "C:\Users\oscar\Downloads\" ($_.Name -replace "tif", 'jpg') ),"JPEG") }
I also replaced the curly brackets through normal one (at Join-Path).
Hope that helps
I don't like one liners while explaining things.
This script includes the needed .substring(4) for the new file name:
[void] [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$DestDir = "C:\Users\oscar\Downloads\"
$SrcDir = "X:\test\path"
PushD $SrcDir
Get-ChildItem *.tif |
ForEach-Object{
$NewFileName = ( Join-Path $DestDir ($_.BaseName.SubString(4)+'.jpg' ))
$Picture = new-object System.Drawing.Bitmap($_.FullName)
$Picture.Save($NewFileName ,"JPEG")
}
PopD
Related
I want to move all images in a directory, including subdirectories, to a new location while maintaining the existing folder structure.
Following the example, here, I put the objects into a variable, like so:
$picMetadata = Get-FileMetaData -folder (Get-childitem K:\myImages -Recurse -Directory).FullName
The move must be based on the results of a logical expression, such as the following for example.
foreach ($test01 in $picMetadata) {
if ($test01.Height -match "^[0-9]?[0-9] ") {
Write-Host "Test01.Height:" $test01.Height
}
}
Still at an early testing phase So far, I'm having no success even testing for the desired files. In the example above, I thought this simple regex test might provide for anything from "1 pixels" to "99 pixels", which would at least slim down my pictures collection (e.g. an expression without the caret, like "[0-9][0-9] " will return "NN pixels" as well as "NNN Pixels", "NNNNNN pixels", etc.)
Once I figure out how to find my desired images based on a logical, image object dimensions test, I will then need to create a script to move the files. Robocopy /MOV would be nice, but i'm probably in over my head already.
I was going to try to base it on this example (which was provided to a User attempting to COPY (not move / copy/delete) *.extension files). Unfortunately, such a simple operation will not benefit me, as I wish to move .jpg,.png,.gif, etc, based on dimensions not file extension:
$sourceDir = 'K:\myImages\'
$targetDir = ' K:\myImages_psMoveTest\'
Get-ChildItem $sourceDir -filter "*" -recurse | `
foreach{
$targetFile = $targetDir + $_.FullName.SubString($sourceDir.Length);
New-Item -ItemType File -Path $targetFile -Force;
Copy-Item $_.FullName -destination $targetFile
}
Perhaps you have a powershell script that could be used for my intended purpose? I'm just trying to move smaller images out of my collection, without having to overwrite same name images, and lose folder structure, etc.
Thank you very much for reading, and any advisory!
(Edit: Never opposed to improving Powershell skill, if you are aware of a freeware software which would perform this operation, please advise.)
If I understand your question correctly, you want to move image files with a pixel height of 1 up to 99 pixels to a new destination folder, while leaving the subfolder structure intact.
If that is true, you can do:
# needed to use System.Drawing.Image
Add-Type -AssemblyName System.Drawing
$sourceDir = 'K:\myImages'
$targetDir = 'K:\myImages_psMoveTest'
Get-ChildItem $sourceDir -File -Recurse | ForEach-Object {
$file = $_.FullName # need this for when we hit the catch block
try {
# Open image file to determine the pixelheight
$img = [System.Drawing.Image]::FromFile($_.FullName)
$height = $img.Height
# dispose of the image to remove the reference to the file
$img.Dispose()
$img = $null
if ($height -ge 1 -and $height -le 99) {
$targetFolder = Join-Path -Path $targetDir -ChildPath $_.DirectoryName.Substring($sourceDir.Length)
# create the target (sub) folder if it does not already exist
$null = New-Item -Path $targetFolder -ItemType Directory -Force
# next move the file
$_ | Move-Item -Destination $targetFolder -ErrorAction Stop
}
}
catch {
Write-Warning "Error moving file '$file': $($_.Exception.Message)"
}
}
I was downloading a huge torrent (1.2tb with over 6000 folders) divided in 2 parts, so I placed the 2nd part on the designed place and it was not a problem since the master-folder of the torrent is exactly what was needed. The 1st part master-folder was named with some generic torrent name instead of the name I needed, so instead of renaming the torrent name to "source", which I think would have worked and renamed the currently generic name to "source". In files tab I selected all the files and right-click>relocate all of them and bittorrent simply moved all of the files to the same directory, without any subfolder, and created a mess.
So I have a un-finished backup of this torrent and the files are in place, so my idea was using the un-finished one's name, match with the finished ones and put the finished ones in the un-finished matching name's path folder. I hope that was clear.
I tried to resolve this using PowerShell, but I dont know much, so I came up with this and nothing happens, something is wrong. Anyone knows a solution?
$itemlistA = Get-ChildItem -Path "D:\BitTorrent\" |
ForEach-Object {
$objnameA = $_.Name
$objPathA = $_.FullName
}
$itemlistB = Get-ChildItem -Path "E:\DesiredPath\" -recurse |
ForEach-Object{
$objnameB = $_.Name
$objPathB = $_.FullName
}
ForEach-Object{
if($objnameA -eq $objnameB){
Copy-Item -path $objPathA -Destination $objPathB
Write-Host "ffff Object ($objnameA) new Path ($objPathB) ffff"
}
}
If I'm understanding your intent correctly, the script below will accomplish your goal, assuming your goal is to copy files from a flattened directory into some (potentially) nested directories so that the incoming files overwrite files with matching names.
The O(n^2) performance of the nested loops could be improved with a sort and more efficient search.
You'd need to edit the script's params to reflect your own environment.
param(
$pathToFiles = "$PSScriptRoot\BitTorrent\",
$desiredPath = "$PSScriptRoot\DesiredPath\"
)
$itemlistA = Get-ChildItem -Path $pathToFiles | Select-Object -Property Name, FullName
$itemlistB = Get-ChildItem -Path $desiredPath -Recurse | Select-Object -Property Name, FullName
foreach ($fileA in $itemlistA) {
foreach ($fileB in $itemListB) {
if ($fileB.Name -eq $fileA.Name) {
Copy-Item -path $fileA.FullName -Destination $fileB.FullName -Verbose
break
}
}
}
i am trying to loop through all files no matter the type, in a folder, and change a string with one that is input by the user..
i can do this now, with the code below, but only with one type of file extension..
This is my code:
$NewString = Read-Host -Prompt 'Input New Name Please'
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$InputFiles = Get-Item "$scriptPath\*.md"
$OldString = 'SolutionName'
$InputFiles | ForEach {
(Get-Content -Path $_.FullName).Replace($OldString,$NewString) | Set-Content -Path $_.FullName
}
echo 'Complete'
How do i loop through the files, no matter the extension ?
so no matter if it is a md, txt or cshtml or some other, it will replace the string as instructed.
To get all the files in a folder you can get use Get-ChildItem. Add the -Recurse switch to also include files inside of sub-folders.
E.g. you could rewrite your script like this
$path = 'c:\tmp\test'
$NewString = Read-Host -Prompt 'Input New Name Please'
$OldString = 'SolutionName'
Get-ChildItem -Path $path | where {!$_.PsIsContainer} | foreach { (Get-Content $_).Replace($OldString,$NewString) | Set-Content -Path $_.FullName }
this will first get all the files from inside the folder defined in $path, then replace the value given in $OldString with what the user entered in when prompted and finally save the files.
Note: the scripts doesn't make any difference regarding if the content of the files changed or not. This will cause all files modified date to get updated. If this information is important to you then you need to add a check to see if the files contains the $OldString before changing them and saving.
I have never used Powershell (or VBScript) as I am new to IT-Admin and would appreciate some guidance with renaming files.
On a daily basis, I will be downloading files useing Filezilla manually from a SFTP connection to a local directory filder called YYYYMMDD (although later, I will want to write an automated task that does this form me on a scheduled basis). What I would like to set up is some kind of task that when the files land into the distination directory, they get renamed.
For today's download, I have the following files that I would like to rename:
Original Name Renamed Filename
wcm14444.csv.11.10.20_09.32 wcm14444_111020_0932.csv
wcm14444.csv.11.10.21_00.09 wcm14444_111021_0009.csv
wcm14444.pdf.11.10.20_09.32 wcm14444_111020_0932.pdf
wcm14444.pdf.11.10.21_00.10 wcm14444_111021_0010.pdf
wcm1cash.csv.11.10.21_00.56 wcm1cash_111021_0056.csv
wcm1money.csv.11.10.21_00.56 wcm1money_111021_0056.csv
wcm1opnpos.csv.11.10.21_00.56 wcm1opnpos_111021_0056.csv
wcm1trades.csv.11.10.21_00.56 wcm1trades_111021_0056.csv
wcm1_an.pdf.11.10.21_03.26 wcm1_an_111021_0326.pdf
wcm1_ds.pdf.11.10.21_00.22 wcm1_ds_111021_0022.pdf
wcm1_ep.csv.11.10.21_03.26 wcm1_ep_111021_0326.csv
wcm1_ms.pdf.11.10.21_03.26 wcm1_ms_111021_0326.pdf
You will notice in my renaming requirement:
1. The file extension appears in the middle of the filename and gets placed to the end.
2. I am replacing "." with "" where they appear as date seperators. If its of any help, I am only expecting to receive file types of (".csv", ".pdf" , ".xls") and where these appear within the filename, they get replaced with "_".
Currently I would use Excel to perform the task, but this seems quite hard to deploy as a system task? This seems more of a task for Powershell.
If I am creating YYYYMMDD folders for example N:\SFTP\Provider1\YYYYMMDD, what would be the best way of automating the renaming of the files as the files are downloaded into each day's YYYYMMDD (noting that on same days there may not be a folder created because there are no new files).
Many thanks and kind regards,
Bertie.
Thanks all for the help. More for the benefit of those stumbling accross this page. I have now created a Rename_SFTP.ps1 file at N:\SFTP\Provider1\ containing
$pattern = '^([^\.]+)\.(csv|xls|pdf)\.(\d{2})\.(\d{2})\.(\d{2})_(\d{2})\.(\d{2})'
$todayDate = Get-Date -Format "yyyyMMdd"
Get-ChildItem (".\" + $todayDate + "\") -Recurse | Foreach-Object{
if($_.Name -match $pattern)
{
echo $NewName
$NewName = "{0}_{1}{2}{3}_{4}{5}.{6}" -f $matches[1],$matches[3],$matches[4],$matches[5],$matches[6],$matches[7],$matches[2]
Rename-Item (".\" + $todayDate + "\" + $_) -NewName $NewName
}
}
I have then created a RunRenameTaskForToday.bat containing
powershell .\Rename_SFTP.ps1
At least this way, when I download the files manually, all I need to do is double click on a .bat file one level higher up and it will figure the right folder that needs the files to be renamed. All I need to do know is figure out a way to downlaod the SFTP stuff autonatically...
Thanks all.
Quite a complex task here, with several problems (and, most probably several solutions). Here are some guidelines to get you going:
Use Get-ChildItem | Foreach-Item to traverse all files. The $_ holds the current item.
Powershell has very good support for regular expressions, use if ($_ -match "regexp"). I tend to name the matches, to easier identify them. Something like this could work for your specific naming format:
if ($s -match "(?<pre>.*)\.(?<ext>csv|pdf)(?<date>.*)_(?<hour>\d{2})\.(?<min>\d{2})") {
$newname = $matches["pre"] + "_" +
($matches["date"] -replace "\.","") +
"_" + $matches["hour"] + $matches["min"] +
'.' + $matches["ext"]
}
To get the current day, use Get-Date -Format "yyyyMMdd". Use that to create a new folder each day.
Here's another way using a format string to create the new name. Each group of pattern characters surrounded in parenthesis denotes a part of the file name to be captured and is used later on in the $NewName to form the new file name:
$pattern = '^([^\.]+)\.(csv|xls|pdf)\.(\d{2})\.(\d{2})\.(\d{2})_(\d{2})\.(\d{2})'
Get-ChildItem D:\temp -Recurse | Foreach-Object{
if($_.Name -match $pattern)
{
$NewName = "{0}_{1}{2}{3}_{4}{5}.{6}" -f $matches[1],$matches[3],$matches[4],$matches[5],$matches[6],$matches[7],$matches[2]
Rename-Item $_ -NewName $NewName
}
}
Well quickly something called bricolage ...
$ext = ("csv", "pdf", "xls")
foreach ($e in $ext)
{
Get-ChildItem "*$e*" | % {$newName = $_.name.replace('.',''); $newName;
Rename-Item $_.fullname -NewName ($newName -replace "(.*)($e)(.*)",'$1_$3.$2')}
}
How to write script in powershell which finds given string in all files in given directory and changes it to given second one ?
thanks for any help,
bye
Maybe something like this
$files = Get-ChildItem "DirectoryContainingFiles"
foreach ($file in $files)
{
$content = Get-Content -path $file.fullname
$content | foreach {$_ -replace "toreplace", "replacewith"} |
Set-Content $file.fullname
}
If the string to replace spans multiple lines then using Get-Content isn't going to cut it unless you stitch together the output of Get-Content into a single string. It's easier to use [io.file]::ReadAllText() in this case e.g.:
Get-ChildItem | Where {!$_.PSIsContainer} |
Foreach { $txt = [IO.File]::ReadAllText($_.fullname);
$txt -replace $old,$new; $txt | Out-File $_}
Note with with $old, you may need to use a regex directive like '(?s)' at the beginning to indicate that . matches newline characters also.
I believe that you can get the list of all files in a directory (simple?). Now comes the replacement part. Here is how you can do it with power shell:
type somefile.txt | %{$_ -replace "string_to_be_replaces","new_strings"}
Modify it as per your need. You can also redirect the output to a new file the same way you do other redirection (using: >).
To get the list of files, use:
Get-ChildItem <DIR_PATH> -name