DotNetZip, Powershell and Relative Paths - powershell

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()

Related

Powershell Script, 2x variable in foreach loop

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
}

How can I find out the exact location where the recursive operation is working?

My problem is, that the string for replacement needs to change according to the folder depth the designated file is located and I don't have a clue how to get that info. I need to work with relative addresses.
I want the script to be run from 2 folder levels above the folder where all the files are that need correcting. So I've set the $path in line 1. That folder suppose to be 'depth 0'. In here, the replacement string needs to be in it's native form -> stylesheet.css.
For files in the folders one level below 'depth 0' the string for replacement needs to be prefixed with ../ once -> ../stylesheet.css.
For files in the folders two level below 'depth 0' the string for replacement needs to be prefixed with ../ twice -> ../../stylesheet.css.
...and so on...
I'm stuck here:
$depth = $file.getDepth($path) #> totally clueless here
I need $depth to contain the number of folders under the root $path.
How can I get this? Here's the rest of my code:
$thisLocation = Get-Location
$path = Join-Path -path $thisLocation -childpath "\Files\depth0"
$match = "findThisInFiles"
$fragment = "stylesheet.css" #> string to be prefixed n times
$prefix = "../" #> prefix n times according to folder depth starting at $path (depth 0 -> don't prefix)
$replace = "" #> this will replace $match in files
$depth = 0
$htmlFiles = Get-ChildItem $path -Filter index*.html -recurse
foreach ($file in $htmlFiles)
{
$depth = $file.getDepth($path) #> totally clueless here
$replace = ""
for ($i=0; $i -lt $depth; $i++){
$replace = $replace + $prefix
}
$replace = $replace + $fragment
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace $match, $replace } |
Set-Content $file.PSPath
}
Here's a function I've written that uses Split-Path recursively to determine the depth of a path:
Function Get-PathDepth ($Path) {
$Depth = 0
While ($Path) {
Try {
$Parent = $Path | Split-Path -Parent
}
Catch {}
if ($Parent) {
$Depth++
$Path = $Parent
}
else {
Break
}
}
Return $Depth
}
Example usage:
$MyPath = 'C:\Some\Example\Path'
Get-PathDepth -Path $MyPath
Returns 3.
Unfortunately, I had to wrap Split-Path in a Try..Catch because if you pass it the root path then it throws an error. This is unfortunate because it means genuine errors won't cause an exception to occur but can't see a way around this at the moment.
The advantage of working using Split-Path is that you should get a consistent count regardless of whether a trailing \ is used or not.
Here is a way to get the depth in the folder structure for all files in a location. Hope this helps get you in the right direction
New-Item -Path "C:\Logs\Once\Test.txt" -Force
New-Item -Path "C:\Logs\Twice\Folder_In_Twice\Test.txt" -Force
$Files = Get-ChildItem -Path "C:\Logs\" -Recurse -Include *.* | Select-Object FullName
foreach ($File in $Files) {
[System.Collections.ArrayList]$Split_File = $File.FullName -split "\\"
Write-Output ($File.FullName + " -- Depth is " + $Split_File.Count)
}
Output is this just for illustration
C:\Logs\Once\Test.txt -- Depth is 4
C:\Logs\Twice\Folder_In_Twice\Test.txt -- Depth is 5

Parse directory listing and pass to another script?

I am trying to write a PowerShell script that will loop through a directory in C:\ drive and parse the filenames with the file extension to another script to use.
Basically, the output of the directory listing should be accessible to be parsed to another script one by one. The script is a compiling script which expects an argument (parameter) to be parsed to it in order to compile the specific module (filename).
Code:
Clear-Host $Path = "C:\SandBox\"
Get-ChildItem $Path -recurse -force | ForEach { If ($_.extension -eq ".cob")
{
Write-Host $_.fullname
}
}
If ($_.extension -eq ".pco")
{
Write-Host $_.fullname }
}
You don't need to parse the output as text, that's deprecated.
Here's something that might work for you:
# getmyfiles.ps1
Param( [string])$Path = Get-Location )
dir $Path -Recurse -Force | where {
$_.Extension -in #('.cob', '.pco')
}
# this is another script that calls the above
. getmyfile.ps1 -Path c:\sandbox | foreach-object {
# $_ is a file object. I'm just printing its full path but u can do other stuff eith it
Write-host $_.Fullname
}
Clear-Host
$Path = "C:\Sandbox\"
$Items = Get-ChildItem $Path -recurse -Include "*.cob", "*.pco"
From your garbled code am guessing you want to return a list of files that have .cob and .pco file extensions. You could use the above code to gather those.
$File = $Items.name
$FullName = $items.fullname
Write-Host $Items.name
$File
$FullName
Adding the above lines will allow you to display them in various ways. You can pick the one that suites your needs then loop through them on a for-each.
As a rule its not a place for code to be writen for you, but you have tried to add some to the questions so I've taken a look. Sometimes you just want a nudge in the right direction.

Storing output into a dictionary

I want to iterate through the folders inside the patch scripts, find every iterated result of DBChangesMain and ContentLbl and store it into a dictionary or hash table, and then print out the results how do i do this?
So far I have
$patchscripts = Get-Item "F:\folder\trunk\Source\Database\Patch Scripts"
foreach ($folders in Get-childitem $patchscripts -recurse -include *.sql )
{
if ($folders -like "*DBChangesMain*")
{
}
if ($folders -like "*ContentLbl*")
{
}
}
Write-Host $DbChanges
Write-Host $contentlbl
if you have a better way of doing this, please let me know, cheers.
I guess after your comments I'd rather go the following route:
$patchscripts = 'F:\folder\trunk\Source\Database\Patch Scripts'
$dbChangesMain = Get-ChildItem $patchScripts -Rec -Inc *DBChangesMain*.sql
$contentLbl = Get-ChildItem $patchScripts -Rec -Inc *ContentLbl*.sql
Afterwards you simply have two arrays containing the matching names of the SQL files.

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