Printing multiple-images as a single pdf using powershell - powershell

My code below:
$Source = "C:\Users\xxxx"
$targetFolder = "C:\Users\xxx\new"
Get-ChildItem -Path $Source -Include * | forEach-Object{
$fileObject = $_
$filename = $_.BaseName.Substring(26)
Get-ChildItem -Path $Source -Include * | forEach-Object{
$tempObject = $_
$temp = $_.BaseName.Substring(26)
if($temp -eq $filename -And $tempObject -ne $fileObject){
Start-Process $fileObject + $tempObject -Verb Print -ArgumentList $targetFolder
}
}
I'm able to successfully match files based on part of their names, now I'd like to print the two matching images vertically on top of each other as a PDF, the Printer name would be "Microsoft Print to PDF"
What I truly want is as long as the images are matching according to my if statement, to be combined into one image, if there's another solution that can make that happen I'm open to it as well!!!
Any ideas????

Related

Concatenating Output from Folder

I have thousands of PDF documents that I am trying to comb through and pull out only certain data. I have successfully created a script that goes through each PDF, puts its content into a .txt, and then the final .txt is searched for the requested information. The only part I am stuck on is trying to combine all the data from each PDF into this .txt file. Currenly, each successive PDF simply overwrites the previous data and the search is only performed on the final PDF in the folder. How can I alter this set of code to allow each bit of information to be concatenated into the .txt instead of overwriting?
$all = Get-Childitem -Path $file1 -Recurse -Filter *.pdf
foreach ($f in $all){
$outfile = -join ', '
$text = convert-PDFtoText $outfile
}
Here is my entire script for reference:
Start-Process powershell.exe -Verb RunAs {
function convert-PDFtoText {
param(
[Parameter(Mandatory=$true)][string]$file
)
Add-Type -Path "C:\ps\itextsharp.dll"
$pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $file
for ($page = 1; $page -le $pdf.NumberOfPages; $page++){
$text=[iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,$page)
Write-Output $text
}
$pdf.Close()
}
$content = Read-Host "What are we looking for?: "
$file1 = Read-Host "Path to search: "
$all = Get-Childitem -Path $file1 -Recurse -Filter *.pdf
foreach ($f in $all){
$outfile = $f -join ', '
$text = convert-PDFtoText $outfile
}
$text | Out-File "C:\ps\bulk.txt"
Select-String -Path C:\ps\bulk.txt -Pattern $content | Out-File "C:\ps\select.txt"
Start-Sleep -Seconds 60
}
Any help would be greatly appreciated!
To capture all output across all convert-PDFtoText in a single output file, use a single pipeline with the ForEach-Object cmdlet:
Get-ChildItem -Path $file1 -Recurse -Filter *.pdf |
ForEach-Object { convert-PDFtoText $_.FullName } |
Out-File "C:\ps\bulk.txt"
A tweak to your convert-PDFtoText function would allow for a more concise and efficient solution:
Make convert-PDFtoText accept Get-ChildItem input directly from the pipeline:
function convert-PDFtoText {
param(
[Alias('FullName')
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string] $file
)
begin {
Add-Type -Path "C:\ps\itextsharp.dll"
}
process {
$pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $file
for ($page = 1; $page -le $pdf.NumberOfPages; $page++) {
[iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,$page)
}
$pdf.Close()
}
}
This then allows you to simplify the command at the top to:
Get-ChildItem -Path $file1 -Recurse -Filter *.pdf |
convert-PDFtoText |
Out-File "C:\ps\bulk.txt"

Fix script to allow a scanning of all fields for every row to find if that field contains a target folder name

This is an deepening extension to solve a previously question
Since the input csv file is inconsistent, I cannot use that in this way.
The folder to move to is not always in the same column, so any code trying to use that as input will hit the problem of the value not corresponding to the folder I want to move to. It simply reads garbage ("spam") where it expects the folder name.
The only way to do it is by examining all fields for every row to find if that field contains a target folder name you can use. That means a LOT of Test-Path lines
This is the incriminated code part
Foreach ($fileName in $_.Group.FileName) {
$ValidFileName = $filename -replace $invalid
$targetFile = Join-Path -Path $TargetFolder -ChildPath $fileName
Error is explicitly telling it to move those folders when I iterate through the FileName column. Foreach ($fileName in $_.Group.FileName) {.. this is reading: (1959 10) Showcase Presents n 22,(1959 12) Showcase Presents n 23,alfa, da definire.
My request: Since that examination of all fields for every row is required so I ask for a code editing of this script. Also if this means a LOT of Test-Path lines I suppose that there is no alternative.
However this script below don't create folders and move anything so my request is to try to fix it
$csvpath = 'C:\temp\temp.csv'
$invalid = "[{0}]" -f [RegEx]::Escape(([IO.Path]::GetInvalidFileNameChars() -join ''))
$sourcePath = 'C:\temp\'
Import-Csv C:\temp\temp.csv -Header Title,FileName,Link -Delimiter ';' |
Group-Object Title |
Foreach {
# I prefer to add a trailing slash to folder names
$TargetFolder = Join-Path -Path $sourcePath -ChildPath (($_.Name -replace $invalid)+'\')
# We don't have to create the new folders, because -Force will create them for us
Foreach ($fileName in $_.Group.FileName) {
$ValidFileName = $filename -replace $invalid
$targetFile = Join-Path -Path $TargetFolder -ChildPath $fileName
# Write your values to the console - Make sure the folder is what it should be
Write-Output "Moving '$targetFile' to '$TargetFolder'"
Move-Item $targetFile $TargetFolder -Force -WhatIf
}
}
I was hesitant to even comment on this since you're just not taking any of our advise on all the previoous posts, and just repeating what we say in a new question. We are here to help with your code, and not write it for you but, learning isn't a crime so I will just assume you're just unfamiliar with powershell in general.
$csvpath = 'C:\tomp\temp.csv'
#$invalid = "[{0}]" -f [RegEx]::Escape(([IO.Path]::GetInvalidFileNameChars() -join ''))
$sourcePath = 'C:\tomp\'
Import-Csv $csvpath -Header Title,FileName,Link -Delimiter ',' |
Group-Object Title |
ForEach-Object `
-Begin {
$FoldersLocation = Get-ChildItem -Path C:\tomp -Directory
$Source_Folder = ($FoldersLocation.Parent.FullName | Select-Object -Unique)
#Create array list to hold both columns
[System.Collections.ArrayList]$CombinedRows = #()
} `
-Process {
# Create folder if it doesn't exist
$Destination = Join-Path -Path $Source_Folder -ChildPath $_.Name
if ((Test-Path -Path $Destination) -eq $false) {
"Created: $Destination"
#$null = New-Item -Path ($FoldersLocation.Parent.FullName | Select -Unique) -Name $_.Name -ItemType Directory
}
#region Combine two columns
Foreach ($fileName in $_.Group.FileName) {
$null = $CombinedRows.Add($fileName)
}
Foreach ($link in $_.Group.Link) {
$null = $CombinedRows.Add($link)
}
#endregion
} `
-End {
Foreach ($name in $FoldersLocation) {
if ($name.Name -in $CombinedRows) {
if ($name.Name -match "Showcase"){
# No need for output when supplying the -verbose switch
"Moving: '$($Name.FullName)' to 'C:\tomp\Lanterna Verde - Le Prime Storie'"
# Move-Item -Path $Name.FullName -Destination "C:\tomp\Lanterna Verde - Le Prime Storie"
}
else {
# No need for output when supplying the -verbose switch
"Moving: '$($Name.FullName)]' to 'C:\tomp\Batman DC'"
# Move-Item -Path $Name.FullName -Destination "C:\tomp\Batman DC"
}
}
}
}
All it comes down to is control of flow logic. If a certain condition is met, then do this, or do something else if its not. Added very few inline comments as most of it pretty self explanatory.
I recommend reading up on some powershell instead of hoping others could do everything for you.
Powershell

How to parse through folders and files using PowerShell?

I am trying to construct a script that moves through specific folders and the log files in it, and filters the error codes. After that it passes them into a new file.
I'm not really sure how to do that with for loops so I'll leave my code bellow.
If someone could tell me what I'm doing wrong, that would be greatly appreciated.
$file_name = Read-Host -Prompt 'Name of the new file: '
$path = 'C:\Users\user\Power\log_script\logs'
Add-Type -AssemblyName System.IO.Compression.FileSystem
function Unzip
{
param([string]$zipfile, [string]$outpath)
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)
}
if ([System.IO.File]::Exists($path)) {
Remove-Item $path
Unzip 'C:\Users\user\Power\log_script\logs.zip' 'C:\Users\user\Power\log_script'
} else {
Unzip 'C:\Users\user\Power\log_script\logs.zip' 'C:\Users\user\Power\log_script'
}
$folder = Get-ChildItem -Path 'C:\Users\user\Power\log_script\logs\LogFiles'
$files = foreach($logfolder in $folder) {
$content = foreach($line in $files) {
if ($line -match '([ ][4-5][0-5][0-9][ ])') {
echo $line
}
}
}
$content | Out-File $file_name -Force -Encoding ascii
Inside the LogFiles folder are three more folders each containing log files.
Thanks
Expanding on a comment above about recursing the folder structure, and then actually retrieving the content of the files, you could try something line this:
$allFiles = Get-ChildItem -Path 'C:\Users\user\Power\log_script\logs\LogFiles' -Recurse
# iterate the files
$allFiles | ForEach-Object {
# iterate the content of each file, line by line
Get-Content $_ | ForEach-Object {
if ($_ -match '([ ][4-5][0-5][0-9][ ])') {
echo $_
}
}
}
It looks like your inner loop is of a collection ($files) that doesn't yet exist. You assign $files to the output of a ForEach(...) loop then try to nest another loop of $files inside it. Of course at this point $files isn't available to be looped.
Regardless, the issue is you are never reading the content of your log files. Even if you managed to loop through the output of Get-ChildItem, you need to look at each line to perform the match.
Obviously I cannot completely test this, but I see a few issues and have rewritten as below:
$file_name = Read-Host -Prompt 'Name of the new file'
$path = 'C:\Users\user\Power\log_script\logs'
$Pattern = '([ ][4-5][0-5][0-9][ ])'
if ( [System.IO.File]::Exists( $path ) ) { Remove-Item $path }
Expand-Archive 'C:\Users\user\Power\log_script\logs.zip' 'C:\Users\user\Power\log_script'
Select-String -Path 'C:\Users\user\Power\log_script\logs\LogFiles\*' -Pattern $Pattern |
Select-Object -ExpandProperty line |
Out-File $file_name -Force -Encoding ascii
Note: Select-String cannot recurse on its own.
I'm not sure you need to write your own UnZip function. PowerShell has the Expand-Archive cmdlet which can at least match the functionality thus far:
Expand-Archive -Path <SourceZipPath> -DestinationPath <DestinationFolder>
Note: The -Force parameter allows it to over write the destination files if they are already present. which may be a substitute for testing if the file exists and deleting if it does.
If you are going to test for the file that section of code can be simplified as:
if ( [System.IO.File]::Exists( $path ) ) { Remove-Item $path }
Unzip 'C:\Users\user\Power\log_script\logs.zip' 'C:\Users\user\Power\log_script'
This is because you were going to run the UnZip command regardless...
Note: You could also use Test-Path for this.
Also there are enumerable ways to get the matching lines, here are a couple of extra samples:
Get-ChildItem -Path 'C:\Users\user\Power\log_script\logs\LogFiles' |
ForEach-Object{
( Get-Content $_.FullName ) -match $Pattern
# Using match in this way will echo the lines that matched from each run of
# Get-Content. If nothing matched nothing will output on that iteration.
} |
Out-File $file_name -Force -Encoding ascii
This approach will read the entire file into an array before running the match on it. For large files it may pose a memory issue, however it enabled the clever use of -match.
OR:
Get-ChildItem -Path 'C:\Users\user\Power\log_script\logs\LogFiles' |
Get-Content |
ForEach-Object{ If( $_ -match $Pattern ) { $_ } } |
Out-File $file_name -Force -Encoding ascii
Note: You don't need the alias echo or its real cmdlet Write-Output
UPDATE: After fuzzing around a bit and trying different things I finally got it to work.
I'll include the code below just for demonstration purposes.
Thanks everyone
$start = Get-Date
"`n$start`n"
$file_name = Read-Host -Prompt 'Name of the new file: '
Out-File $file_name -Force -Encoding ascii
Expand-Archive -Path 'C:\Users\User\Power\log_script\logs.zip' -Force
$i = 1
$folders = Get-ChildItem -Path 'C:\Users\User\Power\log_script\logs\logs\LogFiles' -Name -Recurse -Include *.log
foreach($item in $folders) {
$files = 'C:\Users\User\Power\log_script\logs\logs\LogFiles\' + $item
foreach($file in $files){
$content = Get-Content $file
Write-Progress -Activity "Filtering..." -Status "File $i of $($folders.Count)" -PercentComplete (($i / $folders.Count) * 100)
$i++
$output = foreach($line in $content) {
if ($line -match '([ ][4-5][0-5][0-9][ ])') {
Add-Content -Path $file_name -Value $line
}
}
}
}
$end = Get-Date
$time = [int]($end - $start).TotalSeconds
Write-Output ("Runtime: " + $time + " Seconds" -join ' ')

How to add headers to csv file

I am creating a script to get the file version of the dlls and export the output to a csv file , but I want to give particular headers to csv file. How should I do it ? I also need to add a new system date and time column to the csv file.
$j = 'C:\Program Files (x86)\anyfilepatha\'
$files = get-childitem $j -recurse -Include *.dll
$cvsdataFile = 'C:\Program Files\MySQL\Connector ODBC 8.0\dllsinfo.csv'
# Add-Content -Path "C:\Program Files\MySQL\Connector ODBC 8.0\dllsinfo.csv" -Value "Dlls" , "Version Name" , "Location"
$header = foreach ($i in $files)
{
if($i.Name -like '*Eclipsys*' -or $i.Name -like '*Helios*')
{
continue;
}
$verison = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($i).FileVersion
if($verison -eq $null)
{
$i.FullName | Out-File NoVersion.txt -append
}
else
{
Write-Host $i ----> $verison
"{0}`t{1}`t{2} " -f $i, [System.Diagnostics.FileVersionInfo]:: GetVersionInfo($i).FileVersion, $i.CreationTime, $i.FullName | Out-File -Append $cvsdataFile
}
}
You could do it with a one liner. You can exclude items with -Exclude parameter and PowerShell calculated properties for adding new columns. See more about calculated properties here.
Get-ChildItem -Path $j -File -Exclude '*Eclipsys*','*Helios*' -Include *.dll |
Select-Object -Property #{E={$_.versioninfo.Fileversion};l='Version'},#{E={Get-Date -f 'dd/mm/yyyy'};l='Date'},#{E={Get-Date -f 'hh:mm:ss ttt='};L='Time'}

powershell backup script with error logging per file

Really need help creating a script that backs up, and shoots out the error along the file that did not copy
Here is what I tried:
Creating lists of filepaths to pass on to copy-item, in hopes to later catch errors per file, and later log them:
by using $list2X I would be able to cycle through each file, but copy-item loses the Directory structure and shoots it all out to a single folder.
So for now I am using $list2 and later I do copy-item -recurse to copy the folders:
#create list to copy
$list = Get-ChildItem -path $source | Select-Object Fullname
$list2 = $list -replace ("}"),("")
$list2 = $list2 -replace ("#{Fullname=") , ("")
out-file -FilePath g:\backuplog\DirList.txt -InputObject $list2
#create list crosscheck later
$listX = Get-ChildItem -path $source -recurse | Select-Object Fullname
$list2X = $listX -replace ("}"),("")
$list2X = $list2X -replace ("#{Fullname=") , ("")
out-file -FilePath g:\backuplog\FileDirList.txt -InputObject $list2X
And here I would pass the list:
$error.clear()
Foreach($item in $list2){
Copy-Item -Path $item -Destination $destination -recurse -force -erroraction Continue
}
out-file -FilePath g:\backuplog\errorsBackup.txt -InputObject $error
Any help with this is greatly appreciated!!!
The answer to complex file-copying or backup scripts is almost always: "Use robocopy."
Bill
"Want to copy all the items in C:\Scripts (including subfolders) to C:\Test? Then simply use a wildcard character..."
Next make it easier on yourself and do something like this:
$files = (Get-ChildItem $path).FullName #Requires PS 3.0
#or
$files = Get-ChildItem $path | % {$_.Fullname}
$files | Out-File $outpath
well it took me a long time, considering my response time. here is my copy function, which logs most errors(network drops, failed copies , etc) the copy function , and targetobject.
Function backUP{ Param ([string]$destination1 ,$list1)
$destination2 = $destination1
#extract new made string for backuplog
$index = $destination2.LastIndexOf("\")
$count = $destination2.length - $index
$source1 = $destination2.Substring($index, $count)
$finalstr2 = $logdrive + $source1
Foreach($item in $list1){
Copy-Item -Container: $true -Recurse -Force -Path $item -Destination $destination1 -erroraction Continue
if(-not $?)
{
write-output "ERROR de copiado : " $error| format-list | out-file -Append "$finalstr2\GCI-ERRORS-backup.txt"
Foreach($erritem in $error){
write-output "Error Data:" $erritem.TargetObject | out-file -Append "$finalstr2\GCI- ERRORS-backup.txt"
}
$error.Clear()
}
}
}