I can see that the there have been similar question answered but nothing seems to have the answers I'm looking for, I have tried using the solutions/modifying them to suit my needs but am unable to get the correct outputs.
My main question is: Can you pass Powershell variables to 7zip's command line.
Currently i have the script:
##########Alias Setting##########
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"
############################################
#### Variables
$filePath = "D:\Logs\test\201310\" #location to look in
$Archive = "D:\Logs\Test\201310\"
$Extension = "*.log" #extensions to look for
$Days = "30" #Number of days past today's date that will be archived
$CutDay = [DateTime]::Now.AddDays($Days)
$log = Get-ChildItem $filepath -include $Extension -recurse | Where-Object {$_.LastWriteTime -lt $CutDay}
########### END of VARABLES ##################
foreach ($File in $log) #current gui output - temp only using for debugging purpose if required and to confirm files.
{
write-host "File Name : $File " $File.LastWriteTime
}
foreach ($file in $log) # actually zipping
{
$archivename = $_.CutDay
sz a -t7z $archivename $archivename -m0=PPMd
}
########### END OF SCRIPT ##########
What i would like to have is have the 7zip command line read the file name and destination from variables, I plan to try and make this as a generic script as possible so the $archivename would depend on folder name and current date.
I would like the script to be able to read the locations it is zipping via parameters but currently I cannot get it to read the variables!
Any help would be appreciate or if you require clarification please let me know.
I figured out that the best way to pass parameter to an executable is using the splatting parameter (#variablename):
$7zipPath = "$env:ProgramFiles\7-Zip\7z.exe"
$parameter = #('a', '-t7z', '-m0=PPMd', "$target", "$source")
&$7zipPath #parameter
sz a -t7z $archivename $archivename -m0=PPMd
There are two $archivename variables you pass to 7zip. The first should be the Target (e.g. c:\test.zip) and the second the Source (e.g c:\test.text)
Related
This question already has answers here:
Determine and Grab a Number Within Directory Name and Use That Number In an Expression
(2 answers)
Closed 1 year ago.
I recently wanted to improve my code by cleaning it up a bit. In my simple copy and paste program, I want to create a log file every time after the program ran. The problem is with naming the text file: I don’t want to overwrite any log-files and therefore give the file a unique name. Right now it looks like this:
start-transcript - Path $Sourcefolder\log-$logname_$logH.$logM.$logS
Basically it creates a log file based on the time you ran the program and a unique name will be given (I had to use hours, minutes and seconds in a seperate function otherwise special characters would be used but that’s not the problem)
The result would look like this:
log-08.11.202114.47.56
Not really that great.
My solution to the problem would be adding the variable $i and adding +1 every time the program run.
But if I try to do so, the value will automatically reset to 0 after it’s done.
My question, is there a way to implement this or is there another way?
(Sorry if bad format, currently on mobile)
Why not put an ISO8601-like timestamp in the log filename. Is granularity greater than one (1) second needed?
PS C:\> $LogFilename = 'log-' + (Get-Date -Format 'yyyyMMddTHHmmss') + '.log'
PS C:\> $LogFilename
log-20211108T083105.log
You could also use below reusable helper function to always ensure a unique filename:
function Get-UniqueFileName {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
[Alias('FullName')]
[string]$Path # the suggested full path and filename
)
$directory = [System.IO.Path]::GetDirectoryName($Path)
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($Path)
$extension = [System.IO.Path]::GetExtension($Path) # this includes the dot
# get an array of all files with the same extension currently in the directory
$allFiles = #(Get-ChildItem $directory -File -Filter "$baseName*$extension" | Select-Object -ExpandProperty Name)
# construct the possible new file name (just the name, not the full path and name)
$newFile = $baseName + $extension
$seed = 1
while ($allFiles -contains $newFile) {
# add the seed value between brackets. (you can ofcourse leave them out if you like)
$newFile = "{0}({1}){2}" -f $baseName, $seed, $extension
$seed++
}
# return the full path and filename
return Join-Path -Path $directory -ChildPath $newFile
}
In your case you can then do:
$Sourcefolder = 'D:\Test'
$logFile = Join-Path -Path $Sourcefolder -ChildPath ('{0:yyyyMMdd}.log' -f (Get-Date)) | Get-UniqueFileName
Example:
If a file called D:\Test\20211108.log already exists in your source folder, this will return a new filename D:\Test\20211108(1).log. The next one will be D:\Test\20211108(2).log etc.
or, as already commented, you can use the DateTime Ticks property te be pretty sure the filename will be unique:
$logFile = Join-Path -Path $Sourcefolder -ChildPath ('{0}.log' -f (Get-Date).Ticks)
Returns D:\Test\637719832681865978.log
I am trying to join multiple log files into a single archive file, then move that archive file to another location, in an effort to both clean up old log files, and save hard drive space. We have a bunch of tools that all log to the same root, with a per-tool folder for their logs. (E.g.,
C:\ServerLogs
C:\ServerLogs\App1
C:\ServerLogs\2ndApp
each of which will have log files inside, like
C:\ServerLogs\App1\June1.log
C:\ServerLogs\App1\June2.log
C:\ServerLogs\2ndApp\June1.log
C:\ServerLogs\2ndApp\June2.log
I want to go into each of these subfolders, archive up all the files older than 5 days, then move the archive to another (long-term storage) drive and delete the now-zipped files. The tools I'm using are PowerShell and 7zip. The below code is using test locations.
I have cobbled together two scripts from various sources online, over the course of two full shifts, but neither one works right. Here's the first:
# Alias for 7-zip
if (-not (test-path "$env:ProgramFiles\7-Zip\7z.exe")) {throw "$env:ProgramFiles\7-Zip\7z.exe needed"}
set-alias 7zip "$env:ProgramFiles\7-Zip\7z.exe"
$Days = 5 #minimum age of files to archive; in other words, newer than this many days ago are ignored
$SourcePath = C:\WorkingFolder\FolderSource\
$DestinationPath = C:\Temp\
$LogsToArchive = Get-ChildItem -Recurse -Path $SourcePath | Where-Object {$_.lastwritetime -le (get-date).addDays(-$Days)}
$archive = $DestinationPath + $now + ".7z"
#endregion
foreach ($log in $LogsToArchive) {
#define Args
$Args = a -mx9 $archive $log
$Command = 7zip
#write-verbose $command
#invoke the command
invoke-expression -command $Command $Args
The problem with this one is that I get errors trying to invoke the expression. I've tried restructuring it, but then I get errors because my $Args have an "a"
So I abandoned this method (despite it being my preferred), and tried this set.
#region Params
param(
[Parameter(Position=0, Mandatory=$true)]
[ValidateScript({Test-Path -Path $_ -PathType 'container'})]
[System.String]
$SourceDirectory,
[Parameter(Position=1, Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[System.String]
$DestinationDirectory
)
#endregion
function Compress-File{
#region Params
param(
[Parameter(Position=0, Mandatory=$true)]
[ValidateScript({Test-Path -Path $_ -PathType 'leaf'})]
[System.String]
$InputFile,
[Parameter(Position=1, Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[System.String]
$OutputFile
)
#endregion
try{
#Creating buffer with size 50MB
$bytesGZipFileBuffer = New-Object -TypeName byte[](52428800)
$streamGZipFileInput = New-Object -TypeName System.IO.FileStream($InputFile,[System.IO.FileMode]::Open,[System.IO.FileAccess]::Read)
$streamGZipFileOutput = New-Object -TypeName System.IO.FileStream($OutputFile,[System.IO.FileMode]::Create,[System.IO.FileAccess]::Write)
$streamGZipFileArchive = New-Object -TypeName System.IO.Compression.GZipStream($streamGZipFileOutput,[System.IO.Compression.CompressionMode]::Compress)
for($iBytes = $streamGZipFileInput.Read($bytesGZipFileBuffer, 0,$bytesGZipFileBuffer.Count);
$iBytes -gt 0;
$iBytes = $streamGZipFileInput.Read($bytesGZipFileBuffer, 0,$bytesGZipFileBuffer.Count)){
$streamGZipFileArchive.Write($bytesGZipFileBuffer,0,$iBytes)
}
$streamGZipFileArchive.Dispose()
$streamGZipFileInput.Close()
$streamGZipFileOutput.Close()
Get-Item $OutputFile
}
catch { throw $_ }
}
Get-ChildItem -Path $SourceDirectory -Recurse -Exclude "*.7z"|ForEach-Object{
if($($_.Attributes -band [System.IO.FileAttributes]::Directory) -ne [System.IO.FileAttributes]::Directory){
#Current file
$curFile = $_
#Check the file wasn't modified recently
if($curFile.LastWriteTime.Date -le (get-date).adddays(-5)){
$containedDir=$curFile.Directory.FullName.Replace($SourceDirectory,$DestinationDirectory)
#if target directory doesn't exist - create
if($(Test-Path -Path "$containedDir") -eq $false){
New-Item -Path "$containedDir" -ItemType directory
}
Write-Host $("Archiving " + $curFile.FullName)
Compress-File -InputFile $curFile.FullName -OutputFile $("$containedDir\" + $curFile.Name + ".7z")
Remove-Item -Path $curFile.FullName
}
}
}
This actually seems to work, insofar as it creates individual archives for each eligible log, but I need to "bundle" up the logs into one mega-archive, and I can't seem to figure out how to recurse (to get sub-level items) and do a foreach (to confirm age) without having that foreach produce individual archives.
I haven't even gotten into the Move and Delete phase, because I can't seem to get the archiving stage to work properly, but I certainly don't mind grinding away at that once this gets figured out (I've already spent two full days trying to figure this one!).
I greatly appreciate any and all suggestions! If I've not explained something, or been a bit unclear, please let me know!
EDIT1: Part of the requirement, which I completely forgot to mention, is that I need to keep the structure in the new location. So the new location will have
C:\ServerLogs --> C:\Archive\
C:\ServerLogs\App1 --> C:\Archive\App1
C:\ServerLogs\2ndApp --> C:\Archive\2ndApp
C:\Archive
C:\Archive\App1\archivedlogs.zip
C:\Archive\2ndApp\archivedlogs.zip
And I have absolutely no idea how to specify that the logs from App1 need to go to App1.
EDIT2: For this latter part, I used Robocopy - It maintains the folder structure, and if you feed it ".zip" as an argument, it'll only do the .zip files.
this line $Args = a -mx9 $archive $log likely needs to have the right side value wrapped in double quotes OR each non-variable wrapped in quotes with a comma between each so that you get an array of args.
another method would be to declare an array of args explicitly. something like this ...
$ArgList = #(
'a'
'-mx9'
$archive
$log
)
i also recommend you NOT use an automatic $Var name. take a look at Get-Help about_Automatic_Variables and you will see that $Args is one of those. you are strongly recommended NOT to use any of them for anything other than reading. writing to them is iffy. [grin]
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).
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.
I am still pretty new to scripting and "programming" at all. if you miss any information here let me know.
This is my working zip function:
$folder = "C:\zipthis\"
$destinationFilePath = "C:\_archive\zipped"
function create-7zip{
param([string] $folder,
[String] $destinationFilePath)
write-host $folder $destinationFilePath
[string]$pathToZipExe = "C:\Program Files (x86)\7-Zip\7zG.exe";
[Array]$arguments = "a", "-tzip", "$destinationFilePath", "$folder";
& $pathToZipExe $arguments;
}
Get-ChildItem $folder | ? { $_.PSIsContainer} | % {
write-host $_.BaseName $_.Name;
$dest= [System.String]::Concat($destPath,$_.Name,".zip");
(create-7zip $_.FullName $dest)
}
create-7zip $folder $destinationFilePath
now I want him to zip special folders which I already sorted out :
get-childitem "C:\zipme\" | where-Object {$_.name -eq "www" -or $_.name -eq "sql" -or $_.name -eq "services"}
This small function finds the 3 folders I need called www, sql and services. But I didn't manage to insert this into my zip function, so that exactly this folders are zipped and put into C:\_archive\zipped
Because a string is used instead of an array, he tried always to look for a folder called wwwsqlservice which is not there. I tried to put an array using #(www,sql,services) but i had no success, so whats the right way, if there is one?
It should compatible with powershell 2.0, no ps3.0 cmdlets or functions please.
thanks in advance!
Here's a really simple example of what you want to do, removed from the context of your function. It assumes that your destination folders already exist (You can just use Test-Path and New-Item to create them if they don't), and that you're using 7z.exe.
$directories = #("www","sql","services")
$archiveType = "-tzip"
foreach($dir in $directories)
{
# Use $dir to update the destination each loop to prevent overwrites!
$sourceFilePath = "mySourcePath\$dir"
$destinationFilePath = "myTargetPath\$dir"
cmd /c "$pathToZipExe a $archiveType $destinationFilePath $sourceFilePath"
}
Overall it looks like you got pretty close to a solution, with some minor changes needed to support the foreach loop. If you're confident that create-7zip works fine for a single folder, you can substitute that for the cmd /c line above. Here's a listing of some handy example usages for 7zip on the command line.