Powershell Script, 2x variable in foreach loop - powershell

I'm losing my mind. I have to confess I'm a typical copy-paste non-scripting guy, stand here with something new I cannot solve. I want to work with ocrmypdf.exe where I have to read a network-folder for PDFs and put it on a subfolder.
ocrmypdf works simple: ocrmypdf.exe
I have 3 variables like:
$source = #(Get-ChildItem -Path 'X:\OCR\*.pdf') # <-- here are my files, filtered for pdfs
$destname = "X:\ocr\done" #destination-folder where the pdf-files should be written in
$destfiles = foreach ($file in $source) {"$destname\$($file.name)"} # <--- destination path + the same source-file-name
when I have to run a command-exe in Powershell, I should run it like
Foreach ($a in $source)
{
& $command $param
}
where $command and $param is (not) something like this:
$command = 'ocrmypdf.exe'
$param = '$source', '$destfiles'
but as I already know this is not working because the foreachloop can not work with my variables.
Could someone please help me to solve this? Yes my laziness reading a powershell-book comes over and over me now, but I try my luck anyway :)
Thank you in advance

You can supply as many arguments to a command invocation as you want:
$source = #(Get-ChildItem -Path 'X:\OCR\*.pdf')
$destname = "X:\ocr\done"
$command = 'ocrmypdf.exe'
foreach ($file in $source) {
$sourcePath = $file.FullName
$destPath = Join-Path $destname $file.name
# pass both arguments to command
& $command $sourcePath $destPath
}

Related

Powershell: Converting Headers from .msg file to .txt - Current directory doesn't pull header information, but specific directory does

So I am trying to make a script to take a batch of .msg files, pull their header information and then throw that header information into a .txt file. This is all working totally fine when I use this code:
$directory = "C:\Users\IT\Documents\msg\"
$ol = New-Object -ComObject Outlook.Application
$files = Get-ChildItem $directory -Recurse
foreach ($file in $files)
{
$msg = $ol.CreateItemFromTemplate($directory + $file)
$headers = $msg.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E")
$headers > ($file.name +".txt")
}
But when I change the directory to use the active directory where the PS script is being run from $directory = ".\msg\", it will make all the files into text documents but they will be completely blank with no header information. I have tried different variations of things like:
$directory = -Path ".\msg\"
$files = Get-ChildItem -Path $directory
$files = Get-ChildItem -Path ".\msg\"
If anyone could share some ideas on how I could run the script from the active directory without needing to edit the code to specify the path each location. I'm trying to set this up so it can be done by simply putting it into a folder and running it.
Thanks! Any help is very appreciated!
Note: I do have outlook installed, so its not an issue of not being able to pull the headers, as it works when specifying a directory in the code
The easiest way might actually be to do it this way
$msg = $ol.CreateItemFromTemplate($file.FullName)
So, the complete script would then look something like this
$directory = ".\msg\"
$ol = New-Object -ComObject Outlook.Application
$files = Get-ChildItem $directory
foreach ($file in $files)
{
$msg = $ol.CreateItemFromTemplate($file.FullName)
$headers = $msg.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E")
$headers > ($file.name +".txt")
}
All that said, it could be worthwhile reading up on automatic variables (Get-Help about_Automatic_Variables) - for instance the sections about $PWD, $PSScriptRoot and $PSCommandPath might be useful.
Alternative ways - even though they seem unnecessarily complicated.
$msg = $ol.CreateItemFromTemplate((Get-Item $directory).FullName + $file)
Or something like this
$msg = $ol.CreateItemFromTemplate($file.DirectoryName + "\" $file)

Powershell - ForEach statement - concatenate results

I'm hoping to get some help from anyone here regarding powershell scripting.
I'm trying to see if there's a way to call all the results of the ForEach statement:
ForEach ($file in $test) {
$filepath = $path+"\"+$file
write-host $filepath
}
the write-host $filepath inside the ForEach statement returns the following:
c:\....\file1.txt
c:\....\file2.txt
c:\....\file3.txt
etc...
i'm trying to see if i can get all those results and put them into 1 line that i can use outside of the foreach statement. sort of like:
c:\....\file1.txt, c:\....\file2.txt, c:\....\file3.txt etc
right now, if i use write-host $filepath outside of the ForEach statement, it only gives me the last result that $filepath got.
hope i made sense.
thank you in advance.
Nothing easier than that ... ;-)
$FullPathList = ForEach ($file in $test) {
Join-Path -Path $path -ChildPath $file
}
$FullPathList -join ','
First you create an array with the full paths, then you join them with the -join statement. ;-)
Another variant,
$path = $pwd.Path # change as needed
$test = gci # change as needed
#(ForEach ($file in $test) {
$path + "\" + $file
}) -join ", "
You might also want to get a look at the FullName property of Get-ChildItem.
If you do (gci).FullName (or maybe gci | select FullName) you'll directly get the full path.
So if $test is a gci from C:\some\dir, then $test.FullName is the array you are looking for.

Basic File Operations

Please refer to my first code in PS, below:
I created a Test.ps1 file and included the following code:
$path = D:\Five-Levels_Deep_Subfolder\Data
$file = A_Very_Long_INI_FileName.ini
#If $file exists, Delete it:
if (Test-Path + $path) { Remove-Item $path + $file }
#Run the following Application:
& $path + myApplication.exe
But, the result tried to terrify me with a number of horrible error messages. As a matter of fact, none of the above lines of code was error-free.
Please bear with a toddler in PS, and help me to make it a great success :-).
Try this
$path = "D:\Five-Levels_Deep_Subfolder\Data"
$file = "A_Very_Long_INI_FileName.ini"
$filepath = join-path $path $file
#If $filepath exists, Delete it:
if (Test-Path $filepath) { Remove-Item $filepath}
#Run the following Application:
& ($path + "\myApplication.exe")

DotNetZip, Powershell and Relative Paths

I want to zip some selected folders into an archive only with their relative paths.
Some of them have the same folder names (all release-folders, all debug-folders).
The filtering works fine and I have called set-location prior to get-children command.
What is the easiest way to do this job?
Do I really have to implement something like this
foreach ($o in $children)
{ $relPath = $o.FullName.Substring(subPath.Length);
$relPath = $relPath.Substring(0, relPath.LastIndexOf(#"\"));
zip.AddFolder($o.Name, $relPath);
}
Does someone can provide me an example?
Thx
When I need to handle relative paths, I do something like this:
$basePath = "C:\MyBasePath"
$newBasePAth = "C:\NewBasePath"
$files = Get-ChildItem $basePath
$newFileNames = foreach ($f in $files) {
$f.Fullname.Replace($basePath, $newBasePath)
}
Make sure the pattern of slashes you use is the same in $basePath and $newBasePath (use .Trim() to be sure)
Hope this helps
I finally implemented this and it works with the relative paths:
function ZipUp-Files ( $mychildren )
{
foreach ($o in $mychildren)
{
$e= $zipfile.AddDirectory($o.FullName,$o.fullname.substring($pwd.path.length));
}
}
$children =get-childitem -recurse -force | where-object {$_.psiscontainer} | where {$_.name -match $teststring}
[System.Reflection.Assembly]::LoadFrom("C:\dotNetZip\zip-v1.9\Release\Ionic.Zip.dll");
$zipfile = new-object Ionic.Zip.ZipFile($ziptarget);
$zipfile.UseZip64WhenSaving= [Ionic.Zip.Zip64Option]::Always
$zipfile.MaxOutPutSegmentSize=734003200
ZipUp-Files $children
$zipfile.Save("c:\temp\arc_rel.zip")
$zipfile.Dispose()

Powershell: subtract $pwd from $file.Fullname

Given the following files:
c:\dev\deploy\file1.txt
c:\dev\deploy\file2.txt
c:\dev\deploy\file3.txt
c:\dev\deploy\lib\do1.dll
c:\dev\deploy\lib\do2.dll
e.g. if $pwd is the following
c:\dev\deploy
running the statement
$files = get-childitem
I want to take this list and using foreach ($file in $files) I want to substitute my own path for the $pwd e.g. I want to print c:\temp\files like the following:
c:\temp\files\file1.txt
c:\temp\files\file2.txt
c:\temp\files\file3.txt
c:\temp\files\lib\do1.dll
c:\temp\files\lib\do2.dll
How can I peform this i.e.
A = c:\dev\deploy\file1.txt - c:\dev\deploy\
B = c:\temp\files\ + A
giving B = c:\temp\files\file1.txt
?
I would use filter here and consider piping the files like this:
filter rebase($from=($pwd.Path), $to) {
$_.FullName.Replace($from, $to)
}
You can call it like this:
Get-ChildItem C:\dev\deploy | rebase -from C:\dev\deploy -to C:\temp\files\
Get-ChildItem | rebase -from (Get-Location).path -to C:\temp\files\
Get-ChildItem | rebase -to C:\temp\files\
Note that the replacing is case sensitive.
In case you would need case insensitive replace, regexes would help:
(edit based on Keith's comment. Thanks Keith!)
filter cirebase($from=($pwd.Path), $to) {
$_.Fullname -replace [regex]::Escape($from), $to
}
There's a cmdlet for that, Split-Path, the -leaf option gives you the file name. There's also Join-Path, so you can try something like this:
dir c:\dev\deploy | % {join-path c:\temp\files (split-path $_ -leaf)} | % { *action_to_take* }
How about something like:
function global:RelativePath
{
param
(
[string]$path = $(throw "Missing: path"),
[string]$basepath = $(throw "Missing: base path")
)
return [system.io.path]::GetFullPath($path).SubString([system.io.path]::GetFullPath($basepath).Length + 1)
}
$files = get-childitem Desktop\*.*
foreach($f in $files)
{
$path = join-path "C:\somepath" (RelativePath $f.ToString() $pwd.ToString())
$path | out-host
}
I took the simple relative path from here although there is some problems with it, but as you only want to handle paths below your working directory it should be okay.
This works pretty well for me:
gci c:\dev\deploy -r -name | %{"c:\temp\$_"}
The accepted answer only works without Sub-Folders
if you need to "convert" Sub-Folders, the Answer of #stej is better.
Here my Version:
Get-ChildItem -Recurse | ForEach-Object { $_.Fullname.Replace('D:\Work\Test\Release', 'C:\temp\files') }
Note: .Replace doesn't need to be escaped