added link

This commit is contained in:
2019-01-30 14:24:46 +01:00
parent ae5699c189
commit 489d5722d7

View File

@ -1,465 +1,465 @@
#=============================================================================== #===============================================================================
# #
# DIRECTORY: # DIRECTORY:
# (variable) # (variable)
# #
# FILE: # FILE:
# copyfiles.ps1 (or copyfiles.exe) # copyfiles.ps1 (or copyfiles.exe)
# #
# USAGE: # USAGE:
# Step 1 (cmd.exe): # Step 1 (cmd.exe):
# C:\>powershell -ExecutionPolicy Unrestricted # C:\>powershell -ExecutionPolicy Unrestricted
# Step 2: # Step 2:
# PS C:\> E:\copyfiles.ps1 <Sourcepath> <Destinationpath> [Options] # PS C:\> E:\copyfiles.ps1 <Sourcepath> <Destinationpath> [Options]
# or # or
# PS C:\> E:\copyfiles.ps1 -s[ourcepath] <Path> -d[estinationpath] <Path> [Options] # PS C:\> E:\copyfiles.ps1 -s[ourcepath] <Path> -d[estinationpath] <Path> [Options]
# #
# OPTIONS: # OPTIONS:
# -l[ogfilepath] <Path> : relative or absolute path to the logfile # -l[ogfilepath] <Path> : relative or absolute path to the logfile
# -i[nfofilepath] <Path> : relative or absolute path to the infofile # -i[nfofilepath] <Path> : relative or absolute path to the infofile
# -v[erify] : turn on output of success # -v[erify] : turn on output of success
# -q[uiet] : turn off all output # -q[uiet] : turn off all output
# -h[elp] : show a short help # -h[elp] : show a short help
# #
# EXIT STATES: # EXIT STATES:
# True (0) = success # True (0) = success
# False (1) = sourcepath is empty # False (1) = sourcepath is empty
# False (2) = sourcepath has to exist # False (2) = sourcepath has to exist
# False (3) = destinationpath is empty # False (3) = destinationpath is empty
# False (4) = cannot create destinationpath there # False (4) = cannot create destinationpath there
# False (5) = destinationpath already exist # False (5) = destinationpath already exist
# False (6) = logfilepath and infofilepath are equal # False (6) = logfilepath and infofilepath are equal
# False (7) = logfilepath already exist # False (7) = logfilepath already exist
# False (8) = cannot create logfile there # False (8) = cannot create logfile there
# False (9) = unsupported extension for logfile # False (9) = unsupported extension for logfile
# False (10) = infofilepath already exist # False (10) = infofilepath already exist
# False (11) = cannot create infofile there # False (11) = cannot create infofile there
# False (12) = unsupported extension for infofile # False (12) = unsupported extension for infofile
# #
# DESCRIPTION: # DESCRIPTION:
# Copy a file or directory (incl. files) from a given sourcepath # Copy a file or directory (incl. files) from a given sourcepath
# to a given destinationpath and verify that the content of the # to a given destinationpath and verify that the content of the
# file(s) has not changed. # file(s) has not changed.
# Optionally output success in addition to fails. # Optionally output success in addition to fails.
# Optionally write metadata to an infofile and/or each step # Optionally write metadata to an infofile and/or each step
# into a logfile. # into a logfile.
# Optionally suppress all output. # Optionally suppress all output.
# #
# REQUIREMENTS: # REQUIREMENTS:
# Powershell 4.0 (first Version with Get-FileHash commandlet) # Powershell 4.0 (first Version with Get-FileHash commandlet)
# #
# BUGS: # BUGS:
# --- # ---
# #
# NOTES: # NOTES:
# Syntax checked with PSScriptAnalyzer: # Syntax checked with PSScriptAnalyzer:
# PS C:\> Invoke-ScriptAnalyzer E:\copyfiles.ps1 # PS C:\> Invoke-ScriptAnalyzer E:\copyfiles.ps1
# #
# Tested on # Tested on
# - Microsoft Windows 10 (1703) Pro (x64) # - Microsoft Windows 10 (1703) Pro (x64)
# with # with
# - PowerShell Version 5.1.15063.608 ($PSVersionTable.PSVersion) # - PowerShell Version 5.1.15063.608 ($PSVersionTable.PSVersion)
# #
# AUTHOR: # AUTHOR:
# Patrick Neumann, patrick@neumannsland.de # Patrick Neumann, patrick@neumannsland.de
# #
# COMPANY: # COMPANY:
# (privately) # (privately)
# #
# VERSION: # VERSION:
# 0.9.0 (beta) # 0.9.0 (beta)
# #
# LINK TO THE MOST CURRENT VERSION: # LINK TO THE MOST CURRENT VERSION:
# (Sorry, I bet, I'm not allowed to publish it over GitHub!) # https://vcs.neumannsland.de/casualscripter/copyfiles
# #
# CREATED: # CREATED:
# 2017-09-29 # 2017-09-29
# #
# COPYRIGHT (C): # COPYRIGHT (C):
# 2017 - Patrick Neumann # 2017 - Patrick Neumann
# #
# LICENSE: # LICENSE:
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# WARRANTY: # WARRANTY:
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
# TODO: # TODO:
# - More testing (Powershell on macOS and/or ArchLinux) # - More testing (Powershell on macOS and/or ArchLinux)
# #
# HISTORY: # HISTORY:
# 0.9.0 - Patrick Neumann - Initial (for the peer reviewer eyes only) release # 0.9.0 - Patrick Neumann - Initial (for the peer reviewer eyes only) release
# #
#=============================================================================== #===============================================================================
#=== CONFIGURATION (user) ====================================================== #=== CONFIGURATION (user) ======================================================
# Has to be at the top of the code (excl. comments)! # Has to be at the top of the code (excl. comments)!
[CmdletBinding()] [CmdletBinding()]
Param( Param(
[Parameter(Position=1)] # "Mandatory=$True" vs. easy -help [Parameter(Position=1)] # "Mandatory=$True" vs. easy -help
[string]$sourcepath, [string]$sourcepath,
[Parameter(Position=2)] # "Mandatory=$True" vs. easy -help [Parameter(Position=2)] # "Mandatory=$True" vs. easy -help
[string]$destinationpath, [string]$destinationpath,
[string]$logfilepath, # like log= support of dc3dd [string]$logfilepath, # like log= support of dc3dd
[string]$infofilepath, # like hlog= support of dc3dd [string]$infofilepath, # like hlog= support of dc3dd
[switch]$verify, # like --check support in GNU md5sum/sha1sum [switch]$verify, # like --check support in GNU md5sum/sha1sum
[switch]$quiet, [switch]$quiet,
[switch]$help [switch]$help
) )
#=== CONFIGURATION (dynamic) =================================================== #=== CONFIGURATION (dynamic) ===================================================
# Date of start # Date of start
Get-Date -Format d | New-Variable -Name CURDATE -Option constant Get-Date -Format d | New-Variable -Name CURDATE -Option constant
# Time of start # Time of start
Get-Date -Format T | New-Variable -Name CURTIME -Option constant Get-Date -Format T | New-Variable -Name CURTIME -Option constant
# Timezone of start (as difference from UTC because CEST is not supported) # Timezone of start (as difference from UTC because CEST is not supported)
Get-Date -UFormat "UTC%Z" | New-Variable -Name CURTZ -Option constant Get-Date -UFormat "UTC%Z" | New-Variable -Name CURTZ -Option constant
#=== CONFIGURATION (static) ==================================================== #=== CONFIGURATION (static) ====================================================
# Filename of the script # Filename of the script
New-Variable -Name MYNAME -Value $MyInvocation.MyCommand.Name -Option constant New-Variable -Name MYNAME -Value $MyInvocation.MyCommand.Name -Option constant
# Version of the script # Version of the script
New-Variable -Name VERSION -Value "0.9.0" -Option constant New-Variable -Name VERSION -Value "0.9.0" -Option constant
# Creation date of the script # Creation date of the script
New-Variable -Name CREATED -Value "2017-09-29" -Option constant New-Variable -Name CREATED -Value "2017-09-29" -Option constant
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# First output. # First output.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# nmap like output: # nmap like output:
# Starting Nmap 7.40 ( https://nmap.org ) at 2017-09-26 21:13 CEST # Starting Nmap 7.40 ( https://nmap.org ) at 2017-09-26 21:13 CEST
$scriptinfo = "Starting $MYNAME $VERSION ($CREATED) at $CURDATE $CURTIME $CURTZ" $scriptinfo = "Starting $MYNAME $VERSION ($CREATED) at $CURDATE $CURTIME $CURTZ"
Write-Output "$scriptinfo`n" Write-Output "$scriptinfo`n"
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Help. # Help.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
$helpstring = @" $helpstring = @"
NAME NAME
copyfiles.ps1 (or copyfiles.exe) copyfiles.ps1 (or copyfiles.exe)
SYNOPSIS SYNOPSIS
Copy a file or directory from sourcepath to destinationpath. Copy a file or directory from sourcepath to destinationpath.
SYNTAX SYNTAX
PS C:\> E:\copyfiles.ps1 <Sourcepath> <Destinationpath> [Options] PS C:\> E:\copyfiles.ps1 <Sourcepath> <Destinationpath> [Options]
or or
PS C:\> E:\copyfiles.ps1 -s[ourcepath] <Path> -d[estinationpath] <Path> [Options] PS C:\> E:\copyfiles.ps1 -s[ourcepath] <Path> -d[estinationpath] <Path> [Options]
DESCRIPTION DESCRIPTION
Copy a file or directory (incl. files) from a given sourcepath Copy a file or directory (incl. files) from a given sourcepath
to a given destinationpath and verify that the content of the to a given destinationpath and verify that the content of the
file(s) has not changed. file(s) has not changed.
Optionally output success in addition to fails. Optionally output success in addition to fails.
Optionally write metadata to an infofile and/or each step Optionally write metadata to an infofile and/or each step
into a logfile. into a logfile.
Optionally suppress all output. Optionally suppress all output.
REQUIREMENTS REQUIREMENTS
Powershell 4.0 (first Version with Get-FileHash commandlet). Powershell 4.0 (first Version with Get-FileHash commandlet).
You have to open the Powershell with unrestricted execution policy or You have to open the Powershell with unrestricted execution policy or
use "a" .exe version! use "a" .exe version!
PARAMETERS PARAMETERS
-l[ogfilepath] <Path> -l[ogfilepath] <Path>
Relative or absolute path to the logfile. Relative or absolute path to the logfile.
-i[nfofilepath] <Path> -i[nfofilepath] <Path>
Relative or absolute path to the infofile. Relative or absolute path to the infofile.
-v[erify] -v[erify]
Turn on output of success (in addition to fails) Turn on output of success (in addition to fails)
-q[uiet] -q[uiet]
Turn off all output. Turn off all output.
-h[elp] -h[elp]
Show this help. Show this help.
"@ "@
If($help){ If($help){
Write-Output $helpstring Write-Output $helpstring
Exit $True Exit $True
} }
#=== FUNCTION ================================================================== #=== FUNCTION ==================================================================
# NAME: Write-Error-and-Exit-PN # NAME: Write-Error-and-Exit-PN
# DESCRIPTION: Write red text to the console and exit with individual exitcode. # DESCRIPTION: Write red text to the console and exit with individual exitcode.
# PARAMETER 1: string # PARAMETER 1: string
# PARAMETER 2: integer # PARAMETER 2: integer
#=============================================================================== #===============================================================================
Function Write-Error-and-Exit-PN Function Write-Error-and-Exit-PN
{ {
[CmdletBinding()] [CmdletBinding()]
Param( Param(
[Parameter(Mandatory=$True, Position=1)] [Parameter(Mandatory=$True, Position=1)]
[string]$message, [string]$message,
[Parameter(Mandatory=$True, Position=2)] [Parameter(Mandatory=$True, Position=2)]
[string]$exitcode [string]$exitcode
) )
# If you want color you have to use Write-Host! # If you want color you have to use Write-Host!
# (But be aware: Write-Host will not be available on "every host"!) # (But be aware: Write-Host will not be available on "every host"!)
Write-Host " ERROR: $message ... EXIT!`n" -ForegroundColor Red Write-Host " ERROR: $message ... EXIT!`n" -ForegroundColor Red
Write-Output $helpstring Write-Output $helpstring
Exit $exitcode Exit $exitcode
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Checks for sourcepath. # Checks for sourcepath.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
if(-Not $sourcepath){ if(-Not $sourcepath){
Write-Error-and-Exit-PN "sourcepath is empty" 1 Write-Error-and-Exit-PN "sourcepath is empty" 1
} }
if(-Not [System.IO.Path]::IsPathRooted($sourcepath)){ if(-Not [System.IO.Path]::IsPathRooted($sourcepath)){
# convert relative into absolut path # convert relative into absolut path
$sourcepath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($sourcepath) $sourcepath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($sourcepath)
} }
if(-Not (Test-Path $sourcepath)){ if(-Not (Test-Path $sourcepath)){
Write-Error-and-Exit-PN "sourcepath has to exist" 2 Write-Error-and-Exit-PN "sourcepath has to exist" 2
} }
#$sourcepathitem = Get-Item $sourcepath #$sourcepathitem = Get-Item $sourcepath
## if a file, just copy it ## if a file, just copy it
#$sourcepathitem -is [System.IO.FileInfo] #$sourcepathitem -is [System.IO.FileInfo]
## after copy a symlink is a file! ## after copy a symlink is a file!
## if a folder, create it and copy the childitems into it ## if a folder, create it and copy the childitems into it
#$sourcepathitem -is [System.IO.DirectoryInfo] #$sourcepathitem -is [System.IO.DirectoryInfo]
## or only copy content to the destinationpath (tailing backslash) ## or only copy content to the destinationpath (tailing backslash)
#$sourcepath.EndsWith([IO.Path]::DirectorySeparatorChar) #$sourcepath.EndsWith([IO.Path]::DirectorySeparatorChar)
## other items are not supported ## other items are not supported
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Checks for destinationpath. # Checks for destinationpath.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
if(-Not $destinationpath){ if(-Not $destinationpath){
Write-Error-and-Exit-PN "destinationpath is empty" 3 Write-Error-and-Exit-PN "destinationpath is empty" 3
} }
if(-Not [System.IO.Path]::IsPathRooted($destinationpath)){ if(-Not [System.IO.Path]::IsPathRooted($destinationpath)){
# convert relative into absolut path # convert relative into absolut path
$destinationpath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($destinationpath) $destinationpath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($destinationpath)
} }
if(-Not (Test-Path (Split-Path $destinationpath))){ if(-Not (Test-Path (Split-Path $destinationpath))){
Write-Error-and-Exit-PN "cannot create destinationpath there" 4 Write-Error-and-Exit-PN "cannot create destinationpath there" 4
} }
# It is not a good idea to use an existing destination because of the # It is not a good idea to use an existing destination because of the
# possiblity of mixing traces from different cases! # possiblity of mixing traces from different cases!
If(Test-Path $destinationpath){ If(Test-Path $destinationpath){
Write-Error-and-Exit-PN "destinationpath already exist" 5 Write-Error-and-Exit-PN "destinationpath already exist" 5
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Like dc3dd you have to give "log" or "hlog" to activate "log" or "info". # Like dc3dd you have to give "log" or "hlog" to activate "log" or "info".
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Checks for logfilepath != infofilepath. # Checks for logfilepath != infofilepath.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
if($logfilepath -and $infofilepath){ if($logfilepath -and $infofilepath){
if($logfilepath -ceq $infofilepath){ if($logfilepath -ceq $infofilepath){
Write-Error-and-Exit-PN "logfilepath and infofilepath are equal" 6 Write-Error-and-Exit-PN "logfilepath and infofilepath are equal" 6
} }
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Checks for logfilepath. # Checks for logfilepath.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# TODO: # TODO:
# Set "logfile.txt" in the destinationpath if a path is missing after "-l". # Set "logfile.txt" in the destinationpath if a path is missing after "-l".
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
$logfileextensions = @(".log", ".txt") $logfileextensions = @(".log", ".txt")
if($logfilepath){ if($logfilepath){
# It is not a good idea to overwrite an existing logfile! # It is not a good idea to overwrite an existing logfile!
if(Test-Path $logfilepath){ if(Test-Path $logfilepath){
Write-Error-and-Exit-PN "logfilepath already exist" 7 Write-Error-and-Exit-PN "logfilepath already exist" 7
} }
if(-Not [System.IO.Path]::IsPathRooted($logfilepath)){ if(-Not [System.IO.Path]::IsPathRooted($logfilepath)){
# convert relative into absolut path # convert relative into absolut path
$logfilepath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($logfilepath) $logfilepath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($logfilepath)
} }
if(-Not (Test-Path (Split-Path $logfilepath))){ if(-Not (Test-Path (Split-Path $logfilepath))){
Write-Error-and-Exit-PN "cannot create logfile there" 8 Write-Error-and-Exit-PN "cannot create logfile there" 8
} }
# Check extension for logfile (.log or .txt) # Check extension for logfile (.log or .txt)
if($logfileextensions -notcontains [System.IO.Path]::GetExtension($logfilepath)){ if($logfileextensions -notcontains [System.IO.Path]::GetExtension($logfilepath)){
Write-Error-and-Exit-PN "unsupported extension for logfile" 9 Write-Error-and-Exit-PN "unsupported extension for logfile" 9
} }
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Checks for infofilepath. # Checks for infofilepath.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# TODO: # TODO:
# Set "infofile.csv" in the destinationpath if a path is missing after "-i". # Set "infofile.csv" in the destinationpath if a path is missing after "-i".
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
$infofileextensions = @(".csv", ".txt") $infofileextensions = @(".csv", ".txt")
if($infofilepath){ if($infofilepath){
# It is not a good idea to overwrite an existing infofile! # It is not a good idea to overwrite an existing infofile!
if(Test-Path $infofilepath){ if(Test-Path $infofilepath){
Write-Error-and-Exit-PN "infofilepath already exist" 10 Write-Error-and-Exit-PN "infofilepath already exist" 10
} }
if(-Not [System.IO.Path]::IsPathRooted($infofilepath)){ if(-Not [System.IO.Path]::IsPathRooted($infofilepath)){
# convert relative into absolut path # convert relative into absolut path
$infofilepath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($infofilepath) $infofilepath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($infofilepath)
} }
if(-Not (Test-Path (Split-Path $infofilepath))){ if(-Not (Test-Path (Split-Path $infofilepath))){
Write-Error-and-Exit-PN "cannot create infofile there" 11 Write-Error-and-Exit-PN "cannot create infofile there" 11
} }
# Check extension for infofile (.csv or .txt) # Check extension for infofile (.csv or .txt)
if($infofileextensions -notcontains [System.IO.Path]::GetExtension($infofilepath)){ if($infofileextensions -notcontains [System.IO.Path]::GetExtension($infofilepath)){
Write-Error-and-Exit-PN "unsupported extension for infofile" 12 Write-Error-and-Exit-PN "unsupported extension for infofile" 12
} }
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Start write to logfile if "-l" is given. # Start write to logfile if "-l" is given.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
if($logfilepath){ if($logfilepath){
"$scriptinfo`n" | Out-File $logfilepath "$scriptinfo`n" | Out-File $logfilepath
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Start write to infofile if "-i" is given. # Start write to infofile if "-i" is given.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
if($infofilepath){ if($infofilepath){
"# $scriptinfo" | Out-File $infofilepath "# $scriptinfo" | Out-File $infofilepath
$csvtablehead = '"FullName"' $csvtablehead = '"FullName"'
$csvtablehead += ',"Length"' $csvtablehead += ',"Length"'
$csvtablehead += ',"Mode"' $csvtablehead += ',"Mode"'
$csvtablehead += ',"Attributes"' $csvtablehead += ',"Attributes"'
$csvtablehead += ',"SymbolicLinkTarget"' $csvtablehead += ',"SymbolicLinkTarget"'
$csvtablehead += ',"LastWriteTime"' $csvtablehead += ',"LastWriteTime"'
$csvtablehead += ',"LastAccessTime"' $csvtablehead += ',"LastAccessTime"'
$csvtablehead += ',"CreationTime"' $csvtablehead += ',"CreationTime"'
$csvtablehead += ',"Owner"' $csvtablehead += ',"Owner"'
$csvtablehead += ',"Group"' $csvtablehead += ',"Group"'
$csvtablehead += ',"MD5"' $csvtablehead += ',"MD5"'
$csvtablehead += ',"SHA1"' $csvtablehead += ',"SHA1"'
$csvtablehead | Out-File $infofilepath -Append $csvtablehead | Out-File $infofilepath -Append
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# We want as much as possible (and skip only devices and don't want errors). # We want as much as possible (and skip only devices and don't want errors).
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
$sourceroot = Get-Item -Path $sourcepath 2>$null $sourceroot = Get-Item -Path $sourcepath 2>$null
if($sourceroot -Is [System.IO.FileInfo]){ if($sourceroot -Is [System.IO.FileInfo]){
# copy only one file # copy only one file
} }
elseif($sourceroot -Is [System.IO.DirectoryInfo]){ elseif($sourceroot -Is [System.IO.DirectoryInfo]){
$sourcefiles = Get-ChildItem -Path $sourcepath ` $sourcefiles = Get-ChildItem -Path $sourcepath `
-Recurse ` -Recurse `
-Attributes ReadOnly, -Attributes ReadOnly,
Hidden, Hidden,
System, System,
Directory, Directory,
Archive, Archive,
Normal, Normal,
Temporary, Temporary,
SparseFile, SparseFile,
ReparsePoint, ReparsePoint,
Compressed, Compressed,
Offline, Offline,
NotContentIndexed, NotContentIndexed,
Encrypted, Encrypted,
IntegrityStream, IntegrityStream,
NoScrubData 2>$null NoScrubData 2>$null
} }
else{ else{
# type of source not supported # type of source not supported
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Main. # Main.
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
New-Item $destinationpath -ItemType "Directory" | Out-Null New-Item $destinationpath -ItemType "Directory" | Out-Null
if($logfilepath){ if($logfilepath){
"Destination path successfully created.`n" | Out-File $logfilepath -Append "Destination path successfully created.`n" | Out-File $logfilepath -Append
} }
ForEach($sourcefile in $sourcefiles){ ForEach($sourcefile in $sourcefiles){
# md5 and sha1 secure like sha256... # md5 and sha1 secure like sha256...
# Tee-Object w/o process substitution... # Tee-Object w/o process substitution...
if($sourcefile -Is [System.IO.FileInfo]){ if($sourcefile -Is [System.IO.FileInfo]){
$md5ofsource = (Get-FileHash -Path $sourcefile.FullName -Algorithm MD5).hash $md5ofsource = (Get-FileHash -Path $sourcefile.FullName -Algorithm MD5).hash
$sha1ofsource = (Get-FileHash -Path $sourcefile.FullName -Algorithm SHA1).hash $sha1ofsource = (Get-FileHash -Path $sourcefile.FullName -Algorithm SHA1).hash
} }
if($infofilepath){ if($infofilepath){
$line = new-object PSObject $line = new-object PSObject
$line | add-member -membertype NoteProperty -name "FullName" -value $sourcefile.FullName $line | add-member -membertype NoteProperty -name "FullName" -value $sourcefile.FullName
$line | add-member -membertype NoteProperty -name "Mode" -value $sourcefile.Mode $line | add-member -membertype NoteProperty -name "Mode" -value $sourcefile.Mode
$line | add-member -membertype NoteProperty -name "Attributes" -value $sourcefile.Attributes $line | add-member -membertype NoteProperty -name "Attributes" -value $sourcefile.Attributes
if($sourcefile.LinkType -eq "SymbolicLink"){ if($sourcefile.LinkType -eq "SymbolicLink"){
$line | add-member -membertype NoteProperty -name "SymbolicLinkTarget" -value $sourcefile.Target $line | add-member -membertype NoteProperty -name "SymbolicLinkTarget" -value $sourcefile.Target
} }
$line | add-member -membertype NoteProperty -name "LastWriteTime" -value $sourcefile.LastWriteTime $line | add-member -membertype NoteProperty -name "LastWriteTime" -value $sourcefile.LastWriteTime
$line | add-member -membertype NoteProperty -name "LastAccessTime" -value $sourcefile.LastAccessTime $line | add-member -membertype NoteProperty -name "LastAccessTime" -value $sourcefile.LastAccessTime
$line | add-member -membertype NoteProperty -name "CreationTime" -value $sourcefile.CreationTime $line | add-member -membertype NoteProperty -name "CreationTime" -value $sourcefile.CreationTime
$line | add-member -membertype NoteProperty -name "Owner" -value (Get-Acl $sourcefile.FullName).Owner $line | add-member -membertype NoteProperty -name "Owner" -value (Get-Acl $sourcefile.FullName).Owner
$line | add-member -membertype NoteProperty -name "Group" -value (Get-Acl $sourcefile.FullName).Group $line | add-member -membertype NoteProperty -name "Group" -value (Get-Acl $sourcefile.FullName).Group
if($sourcefile -Is [System.IO.FileInfo]){ if($sourcefile -Is [System.IO.FileInfo]){
$line | add-member -membertype NoteProperty -name "Length" -value $sourcefile.Length $line | add-member -membertype NoteProperty -name "Length" -value $sourcefile.Length
$line | add-member -membertype NoteProperty -name "MD5" -value $md5ofsource $line | add-member -membertype NoteProperty -name "MD5" -value $md5ofsource
$line | add-member -membertype NoteProperty -name "SHA1" -value $sha1ofsource $line | add-member -membertype NoteProperty -name "SHA1" -value $sha1ofsource
} }
$line | Export-Csv $infofilepath -Delimiter "," -Append -Encoding UTF8 -NoTypeInformation -Force $line | Export-Csv $infofilepath -Delimiter "," -Append -Encoding UTF8 -NoTypeInformation -Force
$line = $null $line = $null
} }
# os independent path directory separator # os independent path directory separator
# and # and
# -Replace = RegExp und .Replace() = Strings # -Replace = RegExp und .Replace() = Strings
$destinationfile = [System.IO.FileInfo](($destinationpath, [IO.Path]::DirectorySeparatorChar, $sourceroot.Name, [IO.Path]::DirectorySeparatorChar, $sourcefile.FullName.Replace($sourcepath, "")) -join "") $destinationfile = [System.IO.FileInfo](($destinationpath, [IO.Path]::DirectorySeparatorChar, $sourceroot.Name, [IO.Path]::DirectorySeparatorChar, $sourcefile.FullName.Replace($sourcepath, "")) -join "")
# Verify (= Verbose) und Quiet funktioniert so nicht wirklich einfach!? # Verify (= Verbose) und Quiet funktioniert so nicht wirklich einfach!?
# Erst msg bauen und dann nix, ausgabe oder in datei mit if!? # Erst msg bauen und dann nix, ausgabe oder in datei mit if!?
Write-Host "$($destinationfile.FullName): " -NoNewline Write-Host "$($destinationfile.FullName): " -NoNewline
# Am besten gleich zusammen mit: # Am besten gleich zusammen mit:
# Logfile support: "Out-File -Append" # Logfile support: "Out-File -Append"
Copy-Item $sourcefile.FullName $destinationfile.FullName | Out-Null Copy-Item $sourcefile.FullName $destinationfile.FullName | Out-Null
if($destinationfile.Exists){ if($destinationfile.Exists){
#$destinationfile.Mode = $sourcefile.Mode # is ReadOnly :-( #$destinationfile.Mode = $sourcefile.Mode # is ReadOnly :-(
$destinationfile.Attributes = $sourcefile.Attributes $destinationfile.Attributes = $sourcefile.Attributes
$destinationfile.LastWriteTime = $sourcefile.LastWriteTime $destinationfile.LastWriteTime = $sourcefile.LastWriteTime
$destinationfile.LastAccessTime = $sourcefile.LastAccessTime $destinationfile.LastAccessTime = $sourcefile.LastAccessTime
$destinationfile.CreationTime = $sourcefile.CreationTime $destinationfile.CreationTime = $sourcefile.CreationTime
Set-Acl $destinationfile.FullName -AclObject (Get-Acl $sourcefile.FullName) Set-Acl $destinationfile.FullName -AclObject (Get-Acl $sourcefile.FullName)
if($destinationfile -Is [System.IO.FileInfo]){ if($destinationfile -Is [System.IO.FileInfo]){
$md5ofdestination = (Get-FileHash -Path $destinationfile.FullName -Algorithm MD5).hash $md5ofdestination = (Get-FileHash -Path $destinationfile.FullName -Algorithm MD5).hash
$sha1ofdestination = (Get-FileHash -Path $destinationfile.FullName -Algorithm SHA1).hash $sha1ofdestination = (Get-FileHash -Path $destinationfile.FullName -Algorithm SHA1).hash
if(($md5ofdestination -eq $md5ofsource) -and ($sha1ofdestination -eq $sha1ofsource)){ if(($md5ofdestination -eq $md5ofsource) -and ($sha1ofdestination -eq $sha1ofsource)){
Write-Host "OK" -ForegroundColor Green Write-Host "OK" -ForegroundColor Green
} else{ } else{
Write-Host "Failed (hash)" -ForegroundColor Red Write-Host "Failed (hash)" -ForegroundColor Red
} }
} }
if($destinationfile -Is [System.IO.DirectoryInfo]){ if($destinationfile -Is [System.IO.DirectoryInfo]){
Write-Host "OK" -ForegroundColor Green Write-Host "OK" -ForegroundColor Green
} }
} else{ } else{
Write-Host "Failed (copy)" -ForegroundColor Red Write-Host "Failed (copy)" -ForegroundColor Red
} }
} }
exit $True exit $True