7zip - powershell exclude files/folders with variable - powershell

I'm trying to zip a folder but exclude multiple directories using powershell.
I got the following:
if (-not (test-path "$env:ProgramFiles\7-Zip\7z.exe")) {throw "$env:ProgramFiles\7-Zip\7z.exe needed"}
set-alias sz "$env:ProgramFiles\7-Zip\7z.exe"
$Source = "D:\Zip\*"
$Target = "D:\backup.7z"
$exclude = 'Customer\Images\*'
sz a -xr!'$Customer\Images\*' $Target $Source
This works fine.. But when I want the change the -xr! to -xr!'$exclude' or -xr!$exclude it stops working for some reason. Does the variable assigned above not get parsed?

Try this...
if (-not (Test-Path "$env:ProgramFiles\7-Zip\7z.exe")) {throw "$env:ProgramFiles\7-Zip\7z.exe needed"}
$7ZipPath = "$env:ProgramFiles\7-Zip\7z.exe"
$Source = "D:\Zip\*"
$Target = "D:\backup.7z"
$foldersToExclude = 'folder1', 'folder2'
$7zipParams = #('a',$Target)
#($foldersToExclude) | ForEach-Object { $7zipParams += "`"-xr!$_`"" }
$7zipParams += $Source
Write-Host ($7ZipPath+' '+$7zipParams -join ' ') # debug - show the command line
&$7ZipPath $7zipParams
We create an array of params to pass into 7zip, including the excluded folders. As we're appending the excluded folders, we prepend the folder names with the -xr! flags and wrap these in double qoutes.
The #debug line outputs the resultant command line to the console
I call 7zip slightly differently to you, by creating a string with the 7zip executable, then prepending the call to that string path with an ampersand.
This method can exclude multiple folders from the final archive, including folder\subfolder structure.

Two things:
Use the variable inside double quotes if you want it to be expanded:sz a "-xr!$exclude" $Target $Source
Using the pattern Customer\Images\* excludes all files in the directory Images, but includes an empty directory in the resulting file. Use Customer\Images if you don't want to include the directory (or any of its files).

Related

Moving, Encrypting and storing .txt and .pc files to .pgp in PowerShell

I have been learning how to use PowerShell to do some automation that will convert files to a .pgp format. These files end in .txt or .qc. These files are created on one of the servers in the environment, then converted manually, then transferred to another server in the same environment.
I have developed a script that will perform this task, however it will only do one file (I would like for to do all of the files). I the script will not strip the native extension and replace it with only a .pgp extension.
I have included the script below.
PowerShell Script
$_SourcePath = (\\Server1\Location1\*.txt",\\Server1\Location1\*.qc")
$_DestinationPath = "\\Server1\Location2\"
$_SourcePath2 = "\\Server1\Location2\"
$_DestinationPath2 = "\\Server2\Location1\"
Move-item –path $_SourcePath –destination $_DestinationPath
ConvertTo-PgpEncryptedFile -Path "\\Server1\Location2\*.*" -key "\\server3\location1\Enycrption key.asc"
Move-item –path $_SourcePath2 –destination $_DestinationPath2
Again, with this script I can move all the files I need, but it will only encrypt one file and move it to the correct location. I would like for it to encrypt all the files and remove the .txt or .qc extension and replace it with .pgp.
Any Help would be appreciated….
You probably want something along this line:
$SourcePath = '\\Server1\Location1\'
$DestinationPath = '\\Server1\Location2\'
$DestinationPath2 = '\\Server2\Location1\'
$GCIArgs = #{Path = "$($SourcePath)*"
Include = "*.txt","*.qc"
}
$SrcFiles = Get-ChildItem #GCIArgs
ForEach ($File in $SrcFiles) {
Move-item –path "$File.FullName" –destination "$DestinationPath"
$CTPGPArgs =
#{Path = $(Join-Path "$DestinationPath" "$File.Name")
Key = '\\server3\location1\Enycrption key.asc'
}
ConvertTo-PgpEncryptedFile #CTPGPArgs
$EncName = $($_.Name -split('.'))[0] + '.pgp'
$MIArgs = #{Path = "$($CTPGPArgs.Path)"
Destination = $(Join-Path "$DestinationPath2" "$EncName")
}
Move-item #MIArgs
}
I've used single quotes in strings where interpretation is not needed. I've also used splatting to shorten lines and make reading easier.
I eliminated the _ in regular variable names so not to confuse them with items from a pipeline.
Used final Move-Item to also change file extension to .pgp.
Note: I tested as much of this as I could piece-meal but not having servers or the cmdlet ConvertTo-PgpEncryptedFile I couldn't test the entire program.

Compress File per file, same name

I hope you are all safe in this time of COVID-19.
I'm trying to generate a script that goes to the directory and compresses each file to .zip with the same name as the file, for example:
sample.txt -> sample.zip
sample2.txt -> sample2.zip
but I'm having difficulties, I'm not that used to powershell, I'm learning and improving this script. In the end it will be a script that deletes files older than X days, compresses files and makes them upload in ftp .. the part of excluding with more than X I've already managed it for days, now I grabbed a little bit on this one.
Last try at moment.
param
(
#Future accept input
[string] $InputFolder,
[string] $OutputFolder
)
#test folder
$InputFolder= "C:\Temp\teste"
$OutputFolder="C:\Temp\teste"
$Name2 = Get-ChildItem $InputFolder -Filter '*.csv'| select Name
Set-Variable SET_SIZE -option Constant -value 1
$i = 0
$zipSet = 0
Get-ChildItem $InputFolder | ForEach-Object {
$zipSetName = ($Name2[1]) + ".zip "
Compress-Archive -Path $_.FullName -DestinationPath "$OutputFolder\$zipSetName"
$i++;
$Name2++
if ($i -eq $SET_SIZE) {
$i = 0;
$zipSet++;
}
}
You can simplify things a bit, and it looks like most of the issues are because in your script example $Name2 will contain a different set of items than the Get-ChildItem $InputFolder will return in the loop (i.e. may have other objects other than .csv files).
The best way to deal with things is to use variables with the full file object (i.e. you don't need to use |select name). So I get all the CSV file objects right away and store in the variable $CsvFiles.
We can additionally use the special variable $_ inside the ForEach-Object which represents the current object. We also can use $_.BaseName to give us the name without the extension (assuming that's what you want, otherwise use $_Name to get a zip with the name like xyz.csv).
So a simplified version of the code can be:
$InputFolder= "C:\Temp\teste"
$OutputFolder="C:\Temp\teste"
#Get files to process
$CsvFiles = Get-ChildItem $InputFolder -Filter '*.csv'
#loop through all files to zip
$CsvFiles | ForEach-Object {
$zipSetName = $_.BaseName + ".zip"
Compress-Archive -Path $_.FullName -DestinationPath "$OutputFolder\$zipSetName"
}

Powershell - How to finding all files of specified type and the folders next to them

I'm new to PowerShell. I've spend almost two days experimenting on finding some folders. Whatever I do it doesn't work. I have a following structure in file system.
My-GitRepo
|-Src
|-MyCSharpProject_A
|-bin
|-MyCSharpProject_A.csproj
|-MyCSharpProject_B
|-bin
|-MyCSharpProject_B.csproj
|-MyCSharpProject_C
|-bin
|-MyCSharpProject_C.csproj
|-Tools
|-MyPowerShellScript.ps1
I'm currently working on this "MyPowerShellScript.ps1" file. What I'm trying to do is to get *.csproj files from "Src" folder and it works Get-ChildItem -Path "..\Src\*\*.csproj". But the goal is a bit more complicated. I tried to use Get-ChildItem with pipeline to:
Get *.csproj files as specified above
Get folders containing them
Combine folder paths with "bin" subfolder name
Test "bin" folders exist
And put existing "bin" folder paths into local array variable.
As a result I need to dynamically create an array that I currently have hard-coded in my script.
$folders = (
"..\Src\MyCSharpProject_A\bin",
"..\Src\MyCSharpProject_B\bin",
"..\Src\MyCSharpProject_C\bin",
);
Get-ChildItem -Path "..\Src\*\bin" is not what I'm looking for. I'm trying to find "bin" folders that exists next to csproj files.
If I could write the same in C#, it would be one of the following:
string[] qryA = new DirectoryInfo(Environment.CurrentDirectory)
.Parent
.GetDirectories(#"Src")
.Single()
.GetDirectories()
.SelectMany(d => d.GetFiles(#"*.csproj"))
.Select(f => f.Directory.FullName)
.Select(d => Path.Combine(d, "obj"))
.Where(d => Directory.Exists(d))
.ToArray();
string[] qryB = new DirectoryInfo(Environment.CurrentDirectory)
.Parent
.GetDirectories(#"Src")
.Single()
.GetDirectories()
.SelectMany(d => d.GetFiles(#"*.csproj"))
.Select(f => f.Directory.GetDirectories("obj").SingleOrDefault())
.Where(d => d?.Exists == true)
.Select(d => d.FullName)
.ToArray();
What is important:
To be able to use relative path from "Tools" folder which is current directory for executed script
Not to change current directory during script execution,
To have single pipeline (more functional style) which I can convert into function later and share it among many scripts.
UPDATE: Now I have something that works but it still does not have the form of the pipeline:
$csProjs = Get-ChildItem -Path "..\Src\*\*.csproj" -File;
$folders = #();
foreach ($csProj in $csProjs)
{
$prjFolder = Split-Path $csProj;
$objPath = Join-Path $prjFolder "obj";
if (Test-Path $objPath -PathType Container)
{
$folders += $objPath;
}
}
Is it possible to convert it to a pipeline?
Please HELP !!!
Well, if I got your question right, here is something that would do it.
Get *.csproj file - Accomplished with Get-ChildItem with -Filter parameter.
Get folders containing them - This can be done with Split-Path
Combine folders path with "bin" subfolder name - Simple string concatenation
Test "bin" folder exist - Test-Path
And put "bin" folder paths into local array variable. - Since your example showed a relative path, I used Set-Location along with Resolve-Path with -Relative switch parameter
Code
$BasePath = 'PathToSearch'
# Set-Location - This is so we get relative paths
Set-location $BasePath
$CsProjects = Get-ChildItem -Path $BasePath -Filter '*.csproj' -File -recurse
$OutPaths = Foreach ($CSP in $CsProjects) {
$Binpath = "$(Split-Path $CSP.FullName)\bin"
if (Test-Path $Binpath) {
Resolve-Path $Binpath -Relative
}
}
Output based on my search
.\ConsoleHook\bin
.\ConsoleHook.Rx\bin
.\FormsExample\bin

Powershell Move file to new destination based on trimmed file name

I have a folder where files get dropped, I wish to pull the files from that folder and move to a new folder based on part of the file name. If the new folder is missing then create it.
I have attempted to put together the below, however it throws an error about the path already existing and doesn't move the file.
File names can be any thing with out pattern except the last 16 characters of the file, I am removing these and using the remaining as the folder name.
I am really new to scripting so if i have made a silly mistake explanations are appreciated.
Edit
I have played with different orders of operations, added a "-Force" to the new item command, tried with using "Else" and not "If (!(".
I am now at the point where it proudly displays the new directory and then stops.
Could i add the move-item part to a new for each loop so it is processed after the dir is created and tested? If so how do you arrange the { } parts?
Edit 2
I finally have it working, updated script below, the movie-item command was having issues when running into special characters in file names, in my case it was square brackets. The -literalpath switch fixed that for me.
Thanks every one for your input.
Updated script 3.0
#Set source folder
$source = "D:\test\source\"
#Set destination folder (up one level of true destination)
$dest = "D:\test\dest\"
#Define filter Arguments
$filter = "*.txt"
<#
$sourcefile - finds all files that match the filter in the source folder
$trimpath - leaves $file as is, but gets just the file name.
$string - gets file name from $trimpath and converts to a string
$trimmedstring - Takes string from $trimfile and removes the last 16 char off the end of the string
Test for path, if it exists then move on, If not then create directory
Move file to new destination
#>
pushd $source
$sourcefile = Get-ChildItem $source -Filter $filter
foreach ($file in $sourcefile){
$trimpath = $file | split-path -leaf
$string = $trimpath.Substring(0)
$trimmedstring = $string.Substring(0,$string.Length-16)
If(!(Test-Path -path "$dest\$trimmedstring")){New-Item "$dest\$trimmedstring" -Type directory -Force}
move-Item -literalpath "$file" "$dest\$trimmedstring"
}
You may have to tweak the paths being used but the below should work.
$sourcefiles = ((Get-ChildItem $source -Filter $filter).BaseName).TrimEnd(16)
foreach ($file in $sourcefiles)
{
if(!(Test-Path "$dest\$file")){
New-item -ItemType directory -path "$dest\$file"
}
Move-Item "$source\$file" "$dest\file"
}
I finally have it working, updated script below, the movie-item command was having issues when running into special characters in file names, in my case it was square brackets. The -literalpath switch fixed that for me. Thanks every one for your input.
#Set source folder
$source = "D:\test\source\"
#Set destination folder (up one level of true destination)
$dest = "D:\test\dest\"
#Define filter Arguments
$filter = "*.txt"
<#
$sourcefile - finds all files that match the filter in the source folder
$trimpath - leaves $file as is, but gets just the file name.
$string - gets file name from $trimpath and converts to a string
$trimmedstring - Takes string from $trimfile and removes the last 16 char off the end of the string
Test for path, if it exists then move on, If not then create directory
Move file to new destination
#>
pushd $source
$sourcefile = Get-ChildItem $source -Filter $filter
foreach ($file in $sourcefile){
$trimpath = $file | split-path -leaf
$string = $trimpath.Substring(0)
$trimmedstring = $string.Substring(0,$string.Length-16)
If(!(Test-Path -path "$dest\$trimmedstring")){New-Item "$dest\$trimmedstring" -Type directory -Force}
move-Item -literalpath "$file" "$dest\$trimmedstring"
}

Powershell Copying files with varying folders in the path

Right up front apologies for my lack of knowledge with Powershell. Very new to the language . I need to copy some files located in a certain path to another similar path. For example:
C:\TEMP\Users\<username1>\Documents\<varyingfoldername>\*
C:\TEMP\Users\<username2>\Documents\<varyingfoldername>\*
C:\TEMP\Users\<username3>\Documents\<varyingfoldername>\*
C:\TEMP\Users\<username4>\Documents\<varyingfoldername>\*
etc....
to
C:\Files\Users\<username1>\Documents\<varyingfoldername>\*
C:\Files\Users\<username2>\Documents\<varyingfoldername>\*
C:\Files\Users\<username3>\Documents\<varyingfoldername>\*
C:\Files\Users\<username4>\Documents\<varyingfoldername>\*
etc....
So basically all files and directories from path one need to be copied to the second path for each one of the different paths. The only known constant is the first part of the path like C:\TEMP\Users...... and the first part of the destination like C:\Files\Users.....
I can get all the different paths and files by using:
gci C:\TEMP\[a-z]*\Documents\[a-z]*\
but I am not sure how to then pass what's found in the wildcards so I can use them when I do the copy. Any help would be appreciated here.
This should work:
Get-ChildItem "C:\TEMP\*\Documents\*" | ForEach-Object {
$old = $_.FullName
$new = $_.FullName.Replace("C:\TEMP\Users\","C:\Files\Users\")
Move-Item $old $new
}
For additional complexity in matching folder levels, something like this should work:
Get-ChildItem "C:\TEMP\*\Documents\*" -File | ForEach-Object {
$old = $_.FullName
$pathArray = $old.Split("\") # Splits the path into an array
$new = [system.String]::Join("\", $pathArray[0..1]) # Creates a starting point, in this case C:\Temp
$new += "\" + $pathArray[4] # Appends another folder level, you can change the index to match the folder you're after
$new += "\" + $pathArray[6] # You can repeat this line to keep matching different folders
Copy-Item -Recurse -Force $old $new
}