Perform file verification check with Copy-Item - powershell

The following script creates a folder named the specified date on the servers in servers.txt, then copies the folder from which the script is run to that folder.
For example, it creates the folder "3-22-15 (night)" on SERVER1, SERVER2, etc., then copies "Directory Containing the Script" to "3-22-15 (night)".
$CurrentLocation = get-location
$DeploymentDate = "3-22-15 (night)"
Foreach($server in get-content "servers.txt"){
New-Item -ErrorAction SilentlyContinue -ItemType directory -Path \\$server\C$\Deployments\$DeploymentDate
copy-item -Path $CurrentLocation -Destination \\$server\C$\Deployments\$DeploymentDate\ -ErrorAction SilentlyContinue -recurse
}
How do I modify the script to include file verification for each file that is copied to \\$server\C$\Deployments\$DeploymentDate\?
I would like it to output an error message with the file that does not pass the verification check, but to continue copying.
I was going to try something like this:
function SafeCopy ($SourcePath,$DestinationPath,$SourceFileName) {
#MD5 Check Function
function Check-MD5 ($FilePath) {
$md5=New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$hash=[System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($FilePath)))
Return $hash
} # EndOf function Check-MD5
$MD5Source=Check-MD5 $SourcePath
Copy-Item $SourcePath\$SourceFileName $DestinationPath
$MD5Destination=Check-MD5 $DestinationPath
if (Test-Path $DestinationPath) {
if ($MD5Destination -match $MD5Source) {
Write-Host "`nThe file `"$SourcePath`" has been copied in `"$DestinationPath`" successfully.`n" -ForegroundColor DarkGreen
} else {
Write-Host "`nThe file `"$SourcePath`" has been copied in `"$DestinationPath`" but the CRC check failed!`n" -ForegroundColor DarkRed
}
} else {
Write-Host "`nThe file `"$SourcePath`" has not been copied in `"$DestinationPath`"!`n" -ForegroundColor DarkRed
}
} # EndOf function SafeCopy
But I'm not sure how to implement it.

I am not a talented scripter nor do I play one on the radio but I've put this together and it seems to work. As mentioned, the larger the filesize being compared, the slower it goes.
Param
(
[parameter(Mandatory=$true,Position=1,ValueFromPipeLine=$true)][string]$source,
[parameter(Mandatory=$true,Position=2,ValueFromPipeLine=$true)][string]$dest
)
$countsource = #(Get-ChildItem -Path $source -Directory)
$countdest = #(Get-ChildItem -Path $dest -Directory)
IF($countsource = $countdest){
"$source equals $dest"
}
Else{
"$source does not match $dest"
}
Sample usage and output. TODO: Add folder item count compare, warn or break if the folders do not have the same number of items (subfolders and files).
So copy the folder then run Folder-Compare as a function once the copy is finished.
PS C:\Scripts> .\Folder-Compare.ps1 -source C:\Scripts\temp -dest C:\aaa
C:\Scripts\temp does not match C:\aaa

Related

Powershell : Search the Subdirectory and copy the file to that directory

I have been working on a Powershell script from past 2 weeks and I haven't made much progress in that.
So I'm trying to copy a file called version.properties from the root of my gradle project to the Subdirectories like "src/main/resources", "src/main/webapp" and "src/main/application".
If i hard code the path it's working, but im trying to make it generic by finding the directory and copying my file to that directory.
I want my version.properties file to be copied to "resources","webapp" and "application" directory after i run my powershell script.
How can i do it? Any suggestions are appreciated.
$SourceDirectory = "Projectroot\version.properties"
$folders = gci $SourceDirectory -Recurse -Directory
$jar = "src/main/resources"
$ear = "src/main/application"
$war = "src/main/webapp"
foreach ($folder in Sfolders) {
if (Test-Path $folder/$jar) {
write-host "copying to $folder/$jar"
Copy-Item-Path "{$SourceDirectory}\version.properties" -Destination $folder/$jar -Recurse -Force
}
elseif (Test-Path $folder/$ear) {
write-host "copying to $folder/$ear"
Copy-Item-Path "{$SourceDirectory}\version.properties" -Destination $folder/$ear -Recurse -Force
}
elseif (Test-Path $folder/$war) {
write-host "copying to $folder/$war"
Copy-Item-Path "{$SourceDirectory}\version.properties" -Destination $folder/$war -Recurse -Force
}
else {
Write-Host "No such path"
}
}
Assuming this is the path structure of a project:
#ProjectRoot
#ProjectRoot\version.properties <--- File
#ProjectRoot\src
#ProjectRoot\src\main
#ProjectRoot\src\main\application
#ProjectRoot\src\main\resources
#ProjectRoot\src\main\webapp
The following script will do what you seek.
$SourceDirectory = "C:\temp\Projectroot"
$DestinationDirectories = 'resources','application','webapp'
foreach ($I in $DestinationDirectories) {
$CurrentDest = "$SourceDirectory\src\main\$I"
if (Test-Path -Path $CurrentDest) {
Copy-Item -Path "$SourceDirectory\version.properties" -Destination $CurrentDest
} else {
Write-Warning "Path not found: $CurrentDest"
}
}
If I didn't get the path structure, please clarify which is it.
I am assuming too that "main" is a static keyword here but if it is not, that script might need to be adjusted to reflect that.

script file, wait function until end of generation files

I have a script file (batch file) which generate three files in a specific folder. Then i have a ps1 file which copy / move the generated files to another server / folders. Separately, everything is working properly
I'd like if it's possible to merge this, and have a wait function between the two scripts. In fact launching the copy / move ps1 function, only when the three files was correctly generated.
The following assumes:
that the files are created and written in full in a single operation.
that it is the appearance of a *.zip file that signals that all files of interest have been created (though they may still in the process of being written to), as you've indicated in a later comment.
$inFolder = '.' # set to the folder of interest.
$outFolder = './out' # ditto
Write-Verbose -vb 'Waiting for a *.zip file to appear...'
while (-not (Test-Path "$inFolder/*.zip")) { Start-Sleep 1 }
# Get a list of all files.
$files = Get-ChildItem -File $inFolder
Write-Verbose -vb 'Waiting for all files to be written completely...'
$files | ForEach-Object {
do {
# Infer from the ability to obtain an exclusive lock that the file has
# has been written in its entirety.
try { [IO.File]::Open($_.FullName, 'Open', 'Read', 'None').Dispose(); return }
catch { Start-Sleep 1 }
} while ($true)
}
# Move the files elsewhere
Write-Verbose -vb 'Moving...'
$files | Move-Item -Destination $outFolder -WhatIf
Note: The -WhatIf common parameter in the last command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
param(
[String]$sourceDirectory = "c:\tmp\001",
[String]$destDirectory = "c:\tmp\001"
)
Get-ChildItem $sourceDirectory | ? {
#this step wait while locks free
[bool]$flag
while (!$flag) {
try {
$FileStream = [System.IO.File]::Open($_,'Open','Write')
$FileStream.Close()
$FileStream.Dispose()
$flag = $true
}
catch{
Start-ScheduledTask -s 1
$null
}
}
$true
} | Copy-Item -Destination $destDirectory

Powershell Scripting Variables

I have set some variables in PowerShell. The variables are created at the beginning of my script. However, the values for the variables are being executed at start which in turns gives and error message. Ex:
$checker = get-item -path C:\users\user\desktop\Foldername
$finder = Test-path -Path $checker
if($finder -eq $finder )
{
}
Else
{
Create-Item -Path C:/users/user/desktop -name "Foldername" -itemtype Directory
}
I do know that if I run this it will give me an error because the directory never existed and I can just change the variable order to avoid errors.
My question is that this script is going to be more lines of code than this and I would have to create the variable right when its needed to avoid errors.
How can I use these variables like a regular programming language where the variables are ignored until called upon.
Using Get-Item and checking with Test-Path afterwards is not a good design. Two better ways:
Use Get-Item only and check for $null to check for its existence:
$checker = Get-Item -Path C:\users\user\desktop\Foldername -ErrorAction SilentlyContinue
if ($checker) {
# do something with the existing folder
} else {
Create-Item -Path C:/users/user/desktop -Name "Foldername" -ItemType Directory
}
Use Test-Path only to check for its existence:
if (Test-Path -Path C:\users\user\desktop\Foldername) {
# do something with the existing folder
} else {
Create-Item -Path C:/users/user/desktop -Name "Foldername" -ItemType Directory
}

Passing down a variable to function creates an array

I have already asked about Powershell return values in Powershell, but I can simply no wrap my head around why the following New-FolderFromName is returning an array - I expected one value(a path or a string) in return:
$ProjectName="TestProject"
function New-FolderFromPath($FolderPath){
if($FolderPath){
if (!(Test-Path -Path $FolderPath)) {
Write-Host "creating a new folder $FolderName..." -ForegroundColor Green
New-Item -Path $FolderPath -ItemType Directory
}else{
Write-Host "Folder $FolderName already exist..." -ForegroundColor Red
}
}
}
function New-FolderFromName($FolderName){
if($FolderName){
$CurrentFolder=Get-Location
$NewFolder=Join-Path $CurrentFolder -ChildPath $FolderName
New-FolderFromPath($NewFolder)
return $NewFolder
}
}
$ProjectPath=New-FolderFromName($ProjectName)
Write-Host $ProjectPath
Also tried to add the following to New-FolderFromPath as this function seems to be the problem or altering the parameter:
[OutputType([string])]
param(
[string]$FolderPath
)
This happens as a Powershell function will return everything on the pipeline instead of what's just specified with return.
Consider
function New-FolderFromName($FolderName){
if($FolderName){
$CurrentFolder=Get-Location
$NewFolder=Join-Path $CurrentFolder -ChildPath $FolderName
$ret = New-FolderFromPath($NewFolder)
write-host "`$ret: $ret"
return $NewFolder
}
}
#output
PS C:\temp> New-FolderFromName 'foobar'
creating a new folder foobar...
$ret: C:\temp\foobar
C:\temp\foobar
See, the New-FolderFromPath returned a value which originates from New-Item. The easiest way to get rid of extra return values is to pipe New-Item to null like so,
New-Item -Path $FolderPath -ItemType Directory |out-null
See also another a question about the behaviour.

New-Item executed even though it's within an 'if' statement

I created a script to pull JPEG snapshots from 2 IP cameras. For me to keep them organized I added some lines to check the date and create a folder matching it. The script also checks if the folder exists and if it does, should skip to the snapshot capture.
Everything works fine as intended but it seems for one reason or another the script still tries to create the folder and shows and error in my PS console that the directory exists.
$chk_path = Test-Path "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
$Make_SnapShot_Folder = New-Item -ItemType Directory -Path "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
$Camera_A = (new-object System.Net.WebClient).DownloadFile('http://10.0.0.132/snap.jpeg',"C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))\Camera_A$((Get-Date).ToString('HH-mm-ss')).jpeg")
$Camera_B = (new-object System.Net.WebClient).DownloadFile('http://10.0.0.132/snap.jpeg',"C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))\Camera_B$((Get-Date).ToString('HH-mm-ss')).jpeg")
if (-not ($chk_path) ) {
write-host "C:\SnapShots doesn't exist, creating it"
$Make_ScrapShot_Folder
} else {
write-host "C:\SnapShots exists, Saving SnapShots"
}
Camera_A
Camera_B
Unlike other languages probably you've used, PowerShell will execute the code when you assign it to a variable not when the variable is called.
So you're actually creating the folder with this line:
$Make_SnapShot_Folder = New-Item -ItemType Directory -Path "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
You just can re-arrange your code to make it do what you want, and to be more efficient:
$day = Get-Date -Format 'yyyy-MM-dd'
$snapshots_dir = "C:\SnapShots\$day"
if (Test-Path $snapshots_dir) {
Write-Host "$snapshots_dir exists, Saving SnapShots"
} else {
Write-Host "$snapshots_dir doesn't exist, creating it"
New-Item -ItemType Directory -Path $snapshots_dir
}
$timestamp = Get-Date -Format 'HH-mm-ss'
(New-Object System.Net.WebClient).DownloadFile('http://10.0.0.132/snap.jpeg',"$snapshots_dir\Camera_A$timestamp.jpeg")
(New-Object System.Net.WebClient).DownloadFile('http://10.0.0.132/snap.jpeg',"$snapshots_dir\Camera_B$timestamp.jpeg")
The following code snippet should work:
$str_path = "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
$chk_path = Test-Path $str_path
if ($chk_path) {
$Make_SnapShot_Folder = Get-Item -Path $str_path
} else {
$Make_SnapShot_Folder = New-Item -ItemType Directory -Path $str_path
}
$Make_SnapShot_Folder ### this outputs a DirectoryInfo
Move this line:
$Make_SnapShot_Folder =
New-Item -ItemType Directory -Path "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
To here:
if (-not ($chk_path) ) {
<-- Here
write-host "C:\SnapShots doesn't exist, creating it"
$Make_ScrapShot_Folder <-- Remove this line