In a batch script can "Windows Explorer style" copy & paste be used?
For example
copy example.exe
some arbitrary commands
paste example.exe
Update: copying to the clipboard can do done through cmd but
It looks like it's not possible to paste anything but text with out third party utilities like WinClip & Paste
Using PowerShell it is possible to copy a list of files and folders to [Windows.Forms.Clipboard]::SetFileDropList($collection) and to paste using [Windows.Forms.Clipboard]::GetFileDropList() along with standard file copy or stream reader / writer methods. These methods truly use the clipboard, so files copied through Explorer can be pasted from the console, and vice versa. As a bonus, using scripting to manipulate the clipboard FileDropList allows you to append files to the list from multiple locations -- something the GUI interface doesn't allow.
PowerShell scripts can be created as Batch + PowerShell polyglots. So the answer to your question is, yes, it is possible to do what you want with .bat scripts.
fcopy.bat:
Usage:
fcopy.bat [switch] filemask [filemask [filemask [etc.]]]
fcopy.bat /?
Code:
<# : fcopy.bat -- https://stackoverflow.com/a/43924711/1683264
#echo off & setlocal
if "%~1"=="" ( goto usage ) else if "%~1"=="/?" goto usage
set args=%*
rem // kludge for PowerShell's reluctance to start in a dir containing []
set "wd=%CD%"
powershell -STA -noprofile "iex (${%~f0} | out-string)"
goto :EOF
:usage
echo Usage: %~nx0 [switch] filemask [filemask [filemask [...]]]
echo example: %~nx0 *.jpg *.gif *.bmp
echo;
echo Switches:
echo /L list current contents of clipboard file droplist
echo /C clear clipboard
echo /X cut files (Without this switch, the action is copy.)
echo /A append files to existing clipboard file droplist
goto :EOF
: end batch / begin powershell #>
$col = new-object Collections.Specialized.StringCollection
Add-Type -AssemblyName System.Windows.Forms
$files = $()
$switches = #{}
# work around PowerShell's inability to start in a directory containing brackets
cd -PSPath $env:wd
# cmd calling PowerShell calling cmd. Awesome. Tokenization of arguments and
# expansion of wildcards is profoundly simpler when using a cmd.exe for loop.
$argv = #(
cmd /c "for %I in ($env:args) do #echo(%~I"
cmd /c "for /D %I in ($env:args) do #echo(%~I"
) -replace "([\[\]])", "```$1"
$argv | ?{$_.length -gt 3 -and (test-path $_)} | %{ $files += ,(gi -force $_).FullName }
$argv | ?{$_ -match "^[/-]\w\W*$"} | %{
switch -wildcard ($_) {
"?a" { $switches["append"] = $true; break }
"?c" { $switches["clear"] = $true; break }
"?l" { $switches["list"] = $true; break }
"?x" { $switches["cut"] = $true; break }
default { "Unrecognized option: $_"; exit 1 }
}
}
if ($switches["clear"]) {
[Windows.Forms.Clipboard]::Clear()
"<empty>"
exit
}
if ($switches["list"] -and [Windows.Forms.Clipboard]::ContainsFileDropList()) {
$cut = [windows.forms.clipboard]::GetData("Preferred DropEffect").ReadByte() -eq 2
[Windows.Forms.Clipboard]::GetFileDropList() | %{
if ($cut) { write-host -f DarkGray $_ } else { $_ }
}
}
if ($files.Length) {
$data = new-object Windows.Forms.DataObject
if ($switches["cut"]) { $action = 2 } else { $action = 5 }
$effect = [byte[]]($action, 0, 0, 0)
$drop = new-object IO.MemoryStream
$drop.Write($effect, 0, $effect.Length)
if ($switches["append"] -and [Windows.Forms.Clipboard]::ContainsFileDropList()) {
[Windows.Forms.Clipboard]::GetFileDropList() | %{ $files += ,$_ }
}
$color = ("DarkGray","Gray")[!$switches["cut"]]
$files | select -uniq | %{ write-host -f $color $col[$col.Add($_)] }
$data.SetFileDropList($col)
$data.SetData("Preferred DropEffect", $drop)
[Windows.Forms.Clipboard]::Clear()
[Windows.Forms.Clipboard]::SetDataObject($data, $true)
$drop.Close()
}
paste.bat:
Usage:
paste.bat [destination]
Code:
<# : paste.bat -- https://stackoverflow.com/a/43924711/1683264
#echo off & setlocal
set args=%*
set "wd=%CD%"
powershell -STA -noprofile "iex (${%~f0} | out-string)"
goto :EOF
: end batch / begin powershell #>
$files = #()
[uint64]$totalbytes = 0
# kludge for PowerShell's reluctance to start in a path containing []
cd -PSPath $env:wd
Add-Type -AssemblyName System.Windows.Forms
if (-not [Windows.Forms.Clipboard]::ContainsFileDropList()) { exit }
# cut = 2; copy = 5
$de = [Windows.Forms.Clipboard]::GetData("Preferred DropEffect")
if ($de) {$cut = $de.ReadByte() -eq 2} else {$cut = $false}
function newdir ([string]$dir) {
[void](md $dir)
write-host -nonewline "* " -f cyan
write-host -nonewline "Created "
write-host $dir -f white
}
if ($env:args) {
[string[]]$argv = cmd /c "for %I in ($env:args) do #echo(%~I"
if (test-path -PSPath $argv[0] -type Container) {
try { cd -PSPath $argv[0] }
catch { write-host -f red "* Unable to change directory to $($argv[0])"; exit 1 }
} else {
try { newdir $argv[0] }
catch { write-host -f red "* Unable to create $($argv[0])"; exit 1 }
cd -PSPath $argv[0]
}
}
Add-Type #'
using System;
using System.Runtime.InteropServices;
namespace shlwapi {
public static class dll {
[DllImport("shlwapi.dll")]
public static extern long StrFormatByteSize64(ulong fileSize,
System.Text.StringBuilder buffer, int bufferSize);
}
}
'#
function num2size ($num) {
$sb = new-object Text.StringBuilder 16
[void][shlwapi.dll]::StrFormatByteSize64($num, $sb, $sb.Capacity)
$sb.ToString()
}
# returns the true drive letter, even if the supplied path contains a junction / symlink
function Resolve-Drive ([string]$path) {
while ($path.Length -gt 3) {
$dir = gi -force -PSPath $path
if ($dir.Attributes -band [IO.FileAttributes]::ReparsePoint) {
$path = $dir.Target
if ($path.Length -eq 3) { break }
}
$path = (resolve-path "$path\..").Path
}
$path
}
function Move-File ([string]$from, [string]$to) {
$srcdrive = Resolve-Drive $from
$destdrive = Resolve-Drive (Convert-Path .)
if ($srcdrive -eq $destdrive) {
Move-Item $from $to -force
write-host -n -f green "* "
write-host -n -f white (gi -force -PSPath $to).Name
write-host " moved."
} else {
Copy-File $from $to "Moving"
gi -force -PSPath $from | Remove-Item -force
}
}
# based on https://stackoverflow.com/a/13658936/1683264
function Copy-File {
param([string]$from, [string]$to, [string]$action = "Copying")
if (test-path -type leaf -PSPath $to) { gi -force -PSPath $to | Remove-Item -force }
$ffile = [io.file]::OpenRead($from)
$tofile = [io.file]::OpenWrite($to)
$fileobj = gi -force -PSPath $tofile.Name
$filesize = $ffile.length
$size = num2size $filesize
try {
if ($filesize -ge 16*1024*1024) {
$buffersize = 16*1024*1024
} else { $buffersize = $filesize }
Write-Progress `
-Id 1 `
-Activity "0% $action $size file" `
-status $fileobj.Name `
-PercentComplete 0
$sw = [System.Diagnostics.Stopwatch]::StartNew();
[byte[]]$buff = new-object byte[] $buffersize
[uint64]$total = [uint64]$count = 0
do {
$count = $ffile.Read($buff, 0, $buff.Length)
$tofile.Write($buff, 0, $count)
$total += $count
if (!$ffile.Length) {
$pctcomp = 0
} else {
[int]$pctcomp = ([int]($total/$ffile.Length* 100))
}
[int]$secselapsed = [int]($sw.elapsedmilliseconds.ToString())/1000
if ( $secselapsed -ne 0 ) {
[single]$xferrate = (($total/$secselapsed)/1mb)
} else {
[single]$xferrate = 0.0
}
if ($total % 1mb -eq 0) {
if ($pctcomp -gt 0) {
[int]$secsleft = ((($secselapsed/$pctcomp)* 100)-$secselapsed)
} else {
[int]$secsleft = 0
}
Write-Progress `
-Id 1 `
-Activity ($pctcomp.ToString() + "% $action $size file # " + `
"{0:n2}" -f $xferrate + " MB/s") `
-status $fileobj.Name `
-PercentComplete $pctcomp `
-SecondsRemaining $secsleft
}
} while ($count -gt 0)
$sw.Stop()
$sw.Reset()
}
finally {
$tofile.Close()
$ffile.Close()
$ffile = gi -force -PSPath $from
$fileobj.CreationTime = $ffile.CreationTime
$fileobj.LastWriteTime = $ffile.LastWriteTime
if ( $secselapsed -ne 0 ) {
[string]$xferrate = "{0:n2} MB" -f (($total/$secselapsed)/1mb)
} else {
[string]$xferrate = num2size $fileobj.Length
}
write-host -nonewline "* " -f green
write-host -nonewline $fileobj.Name -f white
write-host (" written in $secselapsed second{0} at $xferrate/s." -f (`
"s" * ($secselapsed -ne 1)));
}
}
[Windows.Forms.Clipboard]::GetFileDropList() | %{
if (test-path -PSPath $_ -Type Leaf) {
$add = #($_.Trim(), ((Convert-Path .) + "\" + (gi -force -PSPath $_).Name))
if ($files -notcontains $add) {
$totalbytes += (gi -force -PSPath $_).Length
$files += ,$add
}
} else {
if (test-path -PSPath $_ -Type Container) {
$src = (Convert-Path -PSPath $_).Trim()
$dest = (Convert-Path .) + "\" + (gi -force -PSPath $src).Name
if (!(test-path -PSPath $dest)) { newdir $dest }
gci -PSPath $src -recurse -force | %{
$dest1 = $dest + $_.FullName.Replace($src, '')
if ((test-path -PSPath $_.FullName -Type Container) -and !(test-path -PSPath $dest1)) {
newdir $dest1
}
if (test-path -PSPath $_.FullName -Type Leaf) {
$add = #($_.FullName.Trim(), $dest1)
if ($files -notcontains $add) {
$totalbytes += $_.Length
$files += ,$add
}
}
}
}
}
}
[string]$totalsize = num2size $totalbytes
$destdrive = resolve-drive (Convert-Path .)
$capacity = (Get-PSDrive ($destdrive -split ':')[0]).Free
if ($totalbytes -gt $capacity) {
write-host -f red "* Not enough space on $destdrive"
exit 1
}
for ($i=0; $i -lt $files.length; $i++) {
Write-Progress `
-Activity "Pasting to $(Convert-Path .)" `
-Status ("Total Progress {0}/{1} files {2} total" `
-f ($i + 1), $files.length, $totalsize) `
-PercentComplete ($i / $files.length * 100)
if ($cut) {
Move-File $files[$i][0] $files[$i][1]
} else {
Copy-File $files[$i][0] $files[$i][1]
}
}
if ($cut) {
[Windows.Forms.Clipboard]::GetFileDropList() | %{
if (test-path -PSPath $_ -type Container) {
gi -force -PSPath $_ | Remove-Item -force -recurse
}
}
[Windows.Forms.Clipboard]::Clear()
}
You can read the clipboard content with powershell,
#echo off
set "myText=This is my text"
rem copy variable content to clipboard
set /p"=%myText%"<nul|clip
rem get clipboard content into variable
set "psCmd=powershell -Command "add-type -an system.windows.forms; [System.Windows.Forms.Clipboard]::GetText()""
for /F "usebackq delims=" %%# in (`%psCmd%`) do set "clipContent=%%#"
echo %clipContent%
exit/B
Related
I am attempting to extract the date last modified from the files in a Windows directory. Here is my basic script:
Function Get-FolderItem {
[cmdletbinding(DefaultParameterSetName='Filter')]
Param (
[parameter(Position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('FullName')]
[string[]]$Path = $PWD,
[parameter(ParameterSetName='Filter')]
[string[]]$Filter = '*.*',
[parameter(ParameterSetName='Exclude')]
[string[]]$ExcludeFile,
[parameter()]
[int]$MaxAge,
[parameter()]
[int]$MinAge
)
Begin {
$params = New-Object System.Collections.Arraylist
$params.AddRange(#("/L","/E","/NJH","/NDL","/BYTES","/FP","/NC","/XJ","/R:0","/W:0","T:W","/TS","/UNILOG:c:\temp\test.txt"))
#params.AddRange(#("/L","/S","/NJH","/BYTES","/FP","/NC","/NDL","/TS","/XJ","/R:0","/W:0"))
If ($PSBoundParameters['MaxAge']) {
$params.Add("/MaxAge:$MaxAge") | Out-Null
}
If ($PSBoundParameters['MinAge']) {
$params.Add("/MinAge:$MinAge") | Out-Null
}
}
Process {
ForEach ($item in $Path) {
Try {
$item = (Resolve-Path -LiteralPath $item -ErrorAction Stop).ProviderPath
If (-Not (Test-Path -LiteralPath $item -Type Container -ErrorAction Stop)) {
Write-Warning ("{0} is not a directory and will be skipped" -f $item)
Return
}
If ($PSBoundParameters['ExcludeFile']) {
$Script = "robocopy `"$item`" NULL $Filter $params /XF $($ExcludeFile -join ',')"
} Else {
$Script = "robocopy `"$item`" NULL $Filter $params"
}
Write-Verbose ("Scanning {0}" -f $item)
Invoke-Expression $Script | Out-Null
get-content "c:\temp\test.txt" | ForEach {
Try {
If ($_.Trim() -match "^(?<Children>\d+)\s(?<FullName>.*)") {
$object = New-Object PSObject -Property #{
FullName = $matches.FullName
Extension = $matches.fullname -replace '.*\.(.*)','$1'
FullPathLength = [int] $matches.FullName.Length
FileHash = Get-FileHash -LiteralPath "\\?\$($matches.FullName)" |Select -Expand Hash
Created = ([System.IO.FileInfo] $matches.FullName).creationtime
LastWriteTime = ([System.IO.FileInfo] $matches.FullName).LastWriteTime
Characters = (Get-Content -LiteralPath "\\?\$($matches.FullName)" | Measure-Object -ignorewhitespace -Character).Characters
Owner = (Get-ACL $matches.Fullname).Owner
}
$object.pstypenames.insert(0,'System.IO.RobocopyDirectoryInfo')
Write-Output $object
} Else {
Write-Verbose ("Not matched: {0}" -f $_)
}
} Catch {
Write-Warning ("{0}" -f $_.Exception.Message)
Return
}
}
} Catch {
Write-Warning ("{0}" -f $_.Exception.Message)
Return
}
}
}
}
$a = Get-FolderItem "C:\TargetDirectory\Folder" | Export-Csv -Path C:\Temp\output.csv -Encoding Unicode
The script extracts the date last modified of filepaths less than 260 characters. It returns a nonsense date of 1600-12-31 4:00:00 PM for files longer than 260 characters. Here is the line that is not working:
LastWriteTime = ([System.IO.FileInfo] $matches.FullName).LastWriteTime
My first attempt to solve this problem was to find a command that began with Get- because such commands were useful in extracting filehashes, filepaths, character counts and owner names of files longer than 260 characters. For example:
Owner = (Get-ACL $matches.Fullname).Owner
Characters = (Get-Content -LiteralPath "\\?\$($matches.FullName)" | Measure-Object-ignorewhitespace -Character).Characters
FileHash = Get-FileHash -LiteralPath "\\?\$($matches.FullName)" |Select -Expand Hash
Get-Date however seemed to be about getting the current date.
In my second attempt, I went back to Boe Prox's original blogpost on this script and noticed that his script had two components that were missing from mine:
a robocopy switch /TS
Date = [datetime]$matches.Date
I added to my script however doing so return an error: WARNING: Cannot convert null to type "System.DateTime". I rechecked the file in the directory, and it clearly has a date.
I reexamined the documentation on Get-Date and tried
Date = Get-Date -Format o | ForEach-Object { $matches -replace ":", "." }
However, this returned WARNING: Cannot convert value "2018/03/05 18:06:54 C:TargetDirectory\Folder\Temp.csv to type "System.IO.FileInfo". Error: " Illegal characters in path."
(N.B. In other posts, people have suggested changing the server settings to permit the existence of files longer than 260 characters. This is not an option for me because I do not have access to the servers.)
Once you hit 260 characters in the path, you hit the old Windows MAX_PATH limitation. In order to get around that, you have to prepend your path with \\?\.
In your code above, you do that for Characters and FileHash but you don't do that when retrieving LastWriteTime. e.g. Changing the path to this will work:
Created = ([System.IO.FileInfo] "\\?\$($matches.FullName)").creationtime
LastWriteTime = ([System.IO.FileInfo] "\\?\$($matches.FullName)").LastWriteTime
The alternative way is to use the Get-ChildItem cmdlet along with \\?\ prepended to the path to retrieve most of the fields you want without having to query it multiple times:
get-content "c:\temp\test.txt" | ForEach {
Try {
If ($_.Trim() -match "^(?<Children>\d+)\s(?<FullName>.*)") {
$file = Get-ChildItem "\\?\$($matches.FullName)"
$object = New-Object PSObject -Property #{
FullName = $file.FullName
Extension = $file.Extension
FullPathLength = $file.FullName.Length
FileHash = Get-FileHash -LiteralPath "\\?\$($matches.FullName)" |Select -Expand Hash
Created = $file.CreationTime
LastWriteTime = $file.LastWriteTime
Characters = (Get-Content -LiteralPath "\\?\$($matches.FullName)" | Measure-Object -ignorewhitespace -Character).Characters
Owner = (Get-ACL $matches.Fullname).Owner
}
$object.pstypenames.insert(0,'System.IO.RobocopyDirectoryInfo')
Write-Output $object
} Else {
Write-Verbose ("Not matched: {0}" -f $_)
}
} Catch {
Write-Warning ("{0}" -f $_.Exception.Message)
Return
}
}
The goal is to reset the current cmd.exe shell to have the original set of environment variables. This should include deleting current environment variables that were created after the cmd.exe shell started.
The System and User environment variables can be read from the registry. But, the dynamic variables such as ALLUSERSPROFILE, APPDATA, LOGONSERVER, etc. are not in those locations. Where can those be found?
Because of this, the code cannot delete a variable created after the cmd.exe shell was started. This is because it may be one of the dynamic variables.
Put both of these files in the same directory.
=== Do-Environment.bat
#ECHO OFF
SET "TEMPFILE=%TEMP%\do-environment-script.bat"
powershell -NoLogo -NoProfile -File "%~dp0Do-Environment.ps1" >"%TEMPFILE%"
type "%TEMPFILE%"
CALL "%TEMPFILE%"
EXIT /B %ERRORLEVEL%
=== Do-Environment.ps1
$Vars = #{}
$UserVars = 'HKCU:\Environment'
$SystemVars = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
(Get-Item -Path $SystemVars).Property |
ForEach-Object {
$KeyName = $_
$KeyValue = (Get-Item -Path $SystemVars).GetValue($KeyName, $null)
$Vars[$KeyName] = $KeyValue
}
(Get-Item -Path $UserVars).Property |
ForEach-Object {
$KeyName = $_
$KeyValue = (Get-Item -Path $UserVars).GetValue($KeyName, $null)
if (($null -eq $Vars[$KeyName]) -or ($KeyName -ne 'Path')) {
$Vars[$KeyName] = $KeyValue
} else {
$Vars[$KeyName] = $Vars[$KeyName] + ';' + $KeyValue
}
#'SET "{0}={1}"' -f #($KeyName, $KeyValue)
}
$Vars.Keys | Sort-Object | ForEach-Object { 'SET "{0}={1}"' -f #($_, $Vars[$_]) }
I have written a powershell script for archival of old log files or say some output file of web application which is in TBs but the script is taking very long time. I have done some improvement but not able to speed up more from here.
Code:
#region Archive Files using 7zip
[cmdletbinding()]
Param
(
[Parameter(Mandatory=$true, HelpMessage = "Path needs to be with trailing slash at the end of location." )]
[string]$SourceFilesPath
)
$7zip = "C:\Program Files\7-Zip\7z.exe"
$FilePath = ""
foreach ( $filename in $(Get-ChildItem $SourceFilesPath -Force -Recurse | where {$_.LastWriteTime -lt (get-date).AddDays(-1).ToShortDateString()}))
{
$FilePath = Get-ItemProperty $filename.FullName
$ZipFilePath = $filename.Directory.ToString() + "\ZippedFiles" + "\Archive_" + $filename.LastWriteTime.ToString("MMddyyyy") + ".7z"
$tempPath = ("-w"+"C:\Temp")
$OutputData = &$7zip a $tempPath -t7z $ZipFilePath $FilePath
$OutputData
if ($OutputData -contains "Everything is OK")
{
Remove-Item $FilePath -Force
Write-Output "File removed $FilePath"
}
Get-Item $ZipFilePath | ForEach-Object {$_.LastWriteTime = $filename.LastWriteTime}
}
#endregion
#region Archive Files using 7zip
[cmdletbinding()]
Param
(
[Parameter(Mandatory = $true, HelpMessage = 'Path needs to be with trailing slash at the end of location.' )]
[string]$SourceFilesPath
)
Import-Module ..\Invoke-Parallel.ps1 # Download from PSGallery
$7zip = 'C:\Program Files\7-Zip\7z.exe'
$fileToArchive = $(Get-ChildItem $SourceFilesPath -Force -Recurse | Where-Object -FilterScript {
$_.LastWriteTime -lt (Get-Date).AddDays(-1).ToShortDateString()
})
$counter = 0
$groupSize = 2000 # Will group items by 2,000 increments
$groups = $fileToArchive | Group-Object -Property {
[math]::Floor($counter++ / $groupSize)
}
$groups
# This will spawn multiple instances of 7zip - depending on how many groups of 2,000 files exist
$groups.Group | Invoke-Parallel -ScriptBlock {
$FilePath = $null
$fileName = $_
$FilePath = Get-ItemProperty -Path $fileName.FullName
$ZipFilePath = $fileName.Directory.ToString() + '\ZippedFiles' + '\Archive_' + $fileName.LastWriteTime.ToString('MMddyyyy') + '.7z'
$tempPath = ('-w'+'C:\Temp')
$OutputData = &$Using:7zip a $tempPath -t7z $ZipFilePath $FilePath
$OutputData
if ($OutputData -contains 'Everything is OK')
{
Remove-Item $FilePath -Force
Write-Output -InputObject "File removed $FilePath"
}
Get-Item $ZipFilePath | ForEach-Object -Process {
$_.LastWriteTime = $fileName.LastWriteTime
}
}
I have a script set to enter a for each loop every-time a file is created. Once in the loop it will move a file to a another folder and if the same file has to be moved 3 times it will move the file to a different table and remove the record of it from the hash table.
My issue is when I run the script it does not do anything that I write inside the for each loop. Only if I write script above it. Can someone please advise?
$folder = 'C:\Users\jnwankwo\Documents\IUR Test\r' # Enter the root path you want to monitor.
$Failedfolder = 'C:\Users\jnwankwo\Documents\IUR Test\r'
$filter = '*.*' # You can enter a wildcard filter here.
$Files = #{}
$Counter = 1
$folder = 'C:\Users\jnwankwo\Documents\IUR Test\r' # Enter the root path you want to monitor.
$Failedfolder = 'C:\Users\jnwankwo\Documents\IUR Test\r'
$filter = '*.*' # You can enter a wildcard filter here.
$Files = #{}
$Counter = 1
# In the following line, you can change 'IncludeSubdirectories to $true if required.
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}
Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
ForEach ($file in $folder)
{
$fName = $file.Name
if (-not $Files.ContainsKey($fName))
{
$Files.Add($fName,$Counter)
}
if (($Files.ContainsKey($fName)) -and ($Files.Item($fName) -lt 3))
{
Move-Item 'C:\Users\jnwankwo\Documents\IUR Test\r\*.txt' 'C:\Users\jnwankwo\Documents\IUR Test' -force
$Files.Set_Item($fName,$Counter++)
}
ElseIf (($Files.ContainsKey($fName)) -and ($Files.Item($fName) -eq 3))
{
$Files.clear()
Move-Item 'C:\Users\jnwankwo\Documents\Failed\' $Failedfolder -force
}
}
}
# To stop the monitoring, run the following commands:
# Unregister-Event FileCreated
I have found one thing in your code.
Change ForEach ($file in $folder) to ForEach ($file in (gci $folder))
Here you go, you will have to change the folders back though :)
$folder = 'C:\temp\test' # Enter the root path you want to monitor.
$filter = '*.*' # You can enter a wildcard filter here.
# In the following line, you can change 'IncludeSubdirectories to $true if required.
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName,LastWrite'}
Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
$folder = 'C:\temp\test' # Enter the root path you want to monitor.
$Failedfolder = 'C:\temp\test3'
$Files = #{}
$Counter = 1
ForEach ($file in (gci $folder))
{
$fName = $file.Name
if (-not $Files.ContainsKey($fName))
{
$Files.Add($fName,$Counter)
}
if (($Files.ContainsKey($fName)) -and ($Files.Item($fName) -lt 3))
{
Move-Item $file.Fullname 'C:\Users\jnwankwo\Documents\IUR Test' -force
$Files.Item($fName) = $Counter++
}
ElseIf (($Files.ContainsKey($fName)) -and ($Files.Item($fName) -eq 3))
{
$Files.clear()
Move-Item $file.Fullname $Failedfolder -force
}
}
}
Addition:
To store your Hashtable to a file and re-import it on the next run you can use the following code:
#store hashtable to file
$Files.GetEnumerator() | % { "$($_.Name)=$($_.Value)" } | Out-File files_ht.txt
#to import again
$Files = Get-Content files_ht.txt | Convertfrom-StringData
This should enable you to have the data from the hashtable persistent
If I want to output a file hello.txt, how can I:
check for existence
append -1 at the end if it does
check that hello-1.txt doesn't exist
loop until hello-{integer}.txt isn't found
Another possibility:
if ( test-path hello.txt )
{
$i=0
do { $i++ }
until ( -not ( test-path "hello-$i.txt" ) )
$filename = "hello-$i.txt"
}
else { $filename = 'hello.txt' }
$filename
This code should fulfill all of your requirements. See in-line comments for details. Let me know if it needs any modifications.
# 1. Check for existence of hello.txt
$FilePath = "$PSScriptRoot\hello.txt";
if (Test-Path -Path $FilePath) {
# 2. Rename the file to "hello-1.txt" if it exists
Move-Item -Path $FilePath -Destination $FilePath.Replace('hello.txt', 'hello-1.txt');
}
# 3. Test that hello-1.txt doesn't exist
$FilePath2 = "$PSScriptRoot\hello-1.txt";
Test-Path -Path $FilePath2;
# 4. Loop until hello-*.txt doesn't exist
while (Get-ChildItem -Path $PSScriptRoot\hello-[0-9].txt) {
# Loop
Start-Sleep -Seconds 5;
Write-Host -Object 'Looping ...';
}