Copy files which are complete? - powershell

While running a command to copy files from source to destination using powershell, I ran into the problem of how to only copy files which do not have any operation running on them?
If there are 3 files A/B/C, logging is still happening on A, while B and C are complete. I only want to copy B and C using powershell.
Any ideas will be helpful.

Here is something you can start with:
function Test-FileNotInUse
{
[cmdletbinding()]
param(
[parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('FullName')] [string] $filePath
)
process {
$inUse = $false
try {
$fileInfo = New-Object System.IO.FileInfo $filePath
$fileStream = $fileInfo.Open([System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
if ($fileStream) {
$fileStream.Close()
}
}
catch {
$inUse = $true
}
if (!$inUse) {
Write-Output $filePath
}
}
}
$source = "C:\source"
$destination = "C:\destination"
dir $source -recurse | Test-FileNotInUse | %{ copy $_ $destination }

Related

Powershell - Need to check if name is ended with a sign

My script read a path from TFS and I add a string to it but before I need to verify that the path contains or not contains a sign
Example1:
This is the path, in this case I need to add '/database/'
$/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22
Example2: I need to add only 'database/'
$/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22/
Example3: I need to add '/'
$/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22/database
The goal is to continue with the script with the path/database/
so I need to check first the path and then to add or remove the 'database' string
Anyone can help me with that please?
If I understand the question properly, you want to check if the path from TFS ends with a forward slash or not, so you would know what to append to it and for thast you could use a small helper function like this:
function Join-TFSPath {
[CmdletBinding()]
param (
[parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[string] $Path,
[parameter(Mandatory = $false, Position = 1)]
[string[]] $ChildPath,
[char]$Separator = '/'
)
if ($ChildPath.Count) {
"{0}$separator{1}$Separator" -f $Path.TrimEnd("\/"),
(($ChildPath | ForEach-Object { $_.Trim("\/") } |
Where-Object { $_ -match '\S' }) -join $Separator)
}
else {
"{0}$separator" -f $Path.TrimEnd("\/")
}
}
# test if you need to add `database` or not
$tfsPath = '$/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22/database'
$folder = 'database'
if (($tfsPath.TrimEnd("\/") -split '[\\/]')[-1] -ne $folder) {
# use the function adding the $folder as ChildPath
Join-TFSPath -Path $tfsPath -ChildPath $folder
}
else {
# use the function without specifying the ChildPath so it will only ensure it
# ends with the chosen (or in this case default) separator character
Join-TFSPath -Path $tfsPath
}
As per your comment, you could perhaps then use a more dedicated helper function like:
function Append-TFSPath {
[CmdletBinding()]
param (
[parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[string] $Path,
[parameter(Mandatory = $false, Position = 1)]
[string] $ChildPath = 'database',
[char]$Separator = '/'
)
$Path = $Path -replace '[\\/]+$' # trim off final slash(es)
$ChildPath = $ChildPath -replace '^[\\/]|[\\/]$' -replace '\\', $Separator
if ([string]::IsNullOrWhiteSpace($ChildPath) -or ($Path -replace '\\', $Separator) -like "*$ChildPath") {
"{0}$separator" -f $Path
}
else {
"{0}$separator{1}$Separator" -f $Path, $ChildPath
}
}
Then, just send the path as you have received it to the function and it will return the path you want
$tfsPath = '$/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22/database'
$folder = 'V8.6.22/database'
Append-TFSPath -Path $tfsPath -ChildPath $folder
# because 'database' is the default value for the ChildPath parameter, you can leave that out:
# Append-TFSPath -Path $tfsPath
Testcases:
Append-TFSPath -Path '$/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22'
Append-TFSPath -Path '$/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22/'
Append-TFSPath -Path '$/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22/database'
Append-TFSPath -Path '$/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22/database/'
will all return $/Idu Client-Server/CoreBranches/V6.4/Patches/V8.6.22/database/

How to programatically set Shortcuts TargetPath to a website?

I want to use powershell to modify the TargetPath of a shortcut to open a website
I found the below script that almost works
function Set-Shortcut {
param(
[Parameter(ValueFromPipelineByPropertyName=$true)]
$LinkPath,
$Hotkey,
$IconLocation,
$Arguments,
$TargetPath
)
begin {
$shell = New-Object -ComObject WScript.Shell
}
process {
$link = $shell.CreateShortcut($LinkPath)
$PSCmdlet.MyInvocation.BoundParameters.GetEnumerator() |
Where-Object { $_.key -ne 'LinkPath' } |
ForEach-Object { $link.$($_.key) = $_.value }
$link.Save()
}
}
However
Set-Shortcut -LinkPath "C:\Users\user\Desktop\test.lnk" -TargetPath powershell start process "www.youtube.com"
Will default out to attaching a default path if you do not define one to look like:
"C:\Users\micha\Desktop\powershell start process "www.youtube.com""
how do I get rid of that default file path?
BONUS:
I'd be appreciative if someone broke down this line of code:
ForEach-Object { $link.$($_.key) = $_.value }
have you tried to change the properties of the shortcut object directly?
Try this and let me know:
function Set-Shortcut {
[CmdletBinding()]
param (
[Parameter(Mandatory, Position = 0, ValueFromPipeline)]
[System.String]$FilePath,
[Parameter(Mandatory, Position = 1)]
[System.String]$TargetPath
)
Begin {
$shell = new-object -ComObject WScript.Shell
}
Process {
try {
$file = Get-ChildItem -Path $FilePath -ErrorAction Stop
$shortcut = $shell.CreateShortcut($file.FullName)
$shortcut.TargetPath = $TargetPath
$shortcut.Save()
}
catch {
throw $PSItem
}
}
End {
while ($result -ne -1) {
$result = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($shell)
}
}

How to get path of shortcut's icon file?

Is there a way to get the path of an .ico of a short cut?
I know how to change the shortcuts icon, but how do I find the path of the shortcut's icon file?
You could use below function.
It handles both 'regular' shrotcut files (.lnk) as well as Internet shortcut files (.url)
function Get-ShortcutIcon {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[Alias('FullName')]
[string]$Path # needs to be an absulute path
)
switch ([System.IO.Path]::GetExtension($Path)) {
'.lnk' {
$WshShell = New-Object -ComObject WScript.Shell
$shortcut = $WshShell.CreateShortcut($Path)
$iconPath = $shortcut.IconLocation
$iconInfo = if ($iconPath -match '^,(\d+)') {
[PsCustomObject]#{ IconPath = $shortcut.TargetPath; IconIndex = [int]$matches[1] }
}
else {
[PsCustomObject]#{ IconPath = $iconPath; IconIndex = 0 }
}
# clean up
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($WshShell)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
# return the icon information
$iconInfo
}
'.url' {
$content = Get-Content -Path $Path -Raw
$iconPath = [regex]::Match($content, '(?im)^\s*IconFile\s*=\s*(.*)').Groups[1].Value
$iconIndex = [regex]::Match($content, '(?im)^\s*IconIndex\s*=\s*(\d+)').Groups[1].Value
[PsCustomObject]#{ IconPath = $iconPath; IconIndex = [int]$iconIndex }
}
default { Write-Warning "'$Path' does not point to a '.lnk' or '.url' shortcut file.." }
}
}

How to reuse/extend functions on Powershell?

I'm trying to develop 2 functions with Powershell. The first, will check my database status (online/offline). The second function should loop on the first function until a certain state is achieve.
function Get-DBStatus
{
<# .. removed help section for brevity .. #>
[CmdletBinding()]
[OutputType([System.Object])]
param
(
[Parameter(Mandatory = $true)]
[String]$ServerName,
[Parameter(Mandatory = $true)]
[String]$ServerUser,
[Parameter(Mandatory = $true)]
[String]$ServerPassword,
[Parameter(Mandatory = $true)]
[String]$DatabaseName,
)
try
{
$params = #{ ... }
$dbStatus = Invoke-SqlConnection #params | Where-Object {$_.Name -match $AltDBName }
}
catch
{
Write-Error -Message ('An error has occured while ...')
}
if ([String]::IsNullOrEmpty($dbStatus) -eq $false)
{
$dbStatus
}
# <<< function Get-DbStatusOnlyIf
# <<< same parameters as the function above
# <<< get the desired status as a new parameter
# <<< loop the function above until the desired status is achieved or a timeout is reached
}
I'm new to Powershell and I think I shouldn't repeat myself rewriting the same parameters from the first function into the second one since they're dependent. However, I might be wrong, thus the question.
Thank you for your assistance!
You have to rewrite this parameters on your second function and pass them through or add another paramter to your first function that will do the looping. I would go with the second solution.
Try something like that
function Get-DBStatus {
<# .. removed help section for brevity .. #>
[CmdletBinding()]
[OutputType([System.Object])]
param
(
[Parameter(Mandatory = $true)]
[String]$ServerName,
[Parameter(Mandatory = $true)]
[String]$ServerUser,
[Parameter(Mandatory = $true)]
[String]$ServerPassword,
[Parameter(Mandatory = $true)]
[String]$DatabaseName,
$WaitForStatus, #or something like that
[int]$Timeout=10
)
do {
try {
#$params = #{ ... }
$dbStatus = Invoke-SqlConnection #params | Where-Object {$_.Name -match $AltDBName }
}
catch {
Write-Error -Message ('An error has occured while ...')
return
}
if ([String]::IsNullOrEmpty($dbStatus) -eq $false) {
if ($WaitForStatus){
if ($dbStatus -eq $WaitForStatus) {
$dbStatus
$EndLoop = $true
}
else {
Write-Host -NoNewline "." #only for test
Start-Sleep -Seconds 1
$Timeout -= 1
}
}
else{
$dbStatus
$EndLoop = $true
}
}
}
until ($EndLoop -or $Timeout -eq 0)
}
or with recursion
function Get-DBStatus {
<# .. removed help section for brevity .. #>
[CmdletBinding()]
[OutputType([System.Object])]
param
(
[Parameter(Mandatory = $true)]
[String]$ServerName,
[Parameter(Mandatory = $true)]
[String]$ServerUser,
[Parameter(Mandatory = $true)]
[String]$ServerPassword,
[Parameter(Mandatory = $true)]
[String]$DatabaseName,
$WaitForStatus, #or something like that
[int]$timeout = 3
)
if ($WaitForStatus) {
$start = Get-Date
while (((get-date) - $start).TotalSeconds -lt $timeout) {
$res = Get-DBStatus -ServerName $ServerName -ServerUser $ServerUser -ServerPassword $ServerPassword -DatabaseName $DatabaseName
if ($WaitForStatus -eq $res) {
return $res
}
Start-Sleep -Seconds 1
}
}
else {
try {
$params = #{ ... }
$dbStatus = Invoke-SqlConnection #params | Where-Object {$_.Name -match $AltDBName }
}
catch {
Write-Error -Message ('An error has occured while ...')
}
if ([String]::IsNullOrEmpty($dbStatus) -eq $false) {
$dbStatus
}
}
}

Powershell script not starting java app correctly

Hello guys i am trying to create a powershell script to run my Java application with parameters. App starts and seems to be running but but i'm not getting expected output. I runned the code with params using Intelij and it was ok so the problem lies with powershell script not java code. Can you guys check it out maybe you can spot a mistake?
param
(
[Parameter(Mandatory = $true)][string] $directory,
[Parameter(Mandatory = $true)][ValidateSet("bfs","dfs","astr")][string] $algorithm,
[Parameter(Mandatory = $false)][string] $jarPath = "C:\Users\user\Desktop\Repozytoria\FifteenPuzzle\target\Fifteenpuzzle-1.0-SNAPSHOT-jar-with-dependencies.jar"
)
$fileRegex ="[a-zA-Z0-9]+_[0-9]+_[0-9]+.txt";
$files = Get-ChildItem $directory | Where-Object {$_.Name -match $fileRegex} | select FullName
$strategies = #("RDUL","RDLU","DRUL","DRLU","LUDR","LURD","ULDR","ULRD");
$heuristcs = #("hamm","manh");
if($algorithm -eq "astr")
{
foreach($heuristc in $heuristcs)
{
foreach($file in $files)
{
$app = Start-Process java -ArgumentList "'-jar',$jarPath, $file, $algorithm, $heuristic" -PassThru
$app.PriorityClass = "High"
$app.WaitForExit()
}
}
}
else
{
foreach($strategy in $strategies)
{
foreach($file in $files)
{
#$file
$app = Start-Process java -ArgumentList "'-jar',$jarPath, $file, $algorithm, $strategy" -PassThru
$app.WaitForExit()
}
}
}