I am trying to write a PowerShell script that automatically deletes empty directories on our FTP server. I don't have any direct access to the server on which the directories reside - I can only access them via FTP. For this reason, I have written a PowerShell script that uses WinSCP .NET assembly to (try to) delete the empty directories. However, I have a problem, in that many of the directories on the server have square brackets in the directory name.
For example, the directory name might be called: [a]153432
There are lots of these directories, hence my desire to write a script to delete them. This occurs because they've been created by a program that uses a number to create the directories it requires. In order to work on this program, I created an empty directory
/latest/[b]test
My program goes like this:
# program to test deletion of directories with square-brackets in the name.
$HOSTNAME="myftpservername"
$USERNAME="myftpusername"
$PASSWORD="myftppassword"
$DLL_LOCATION="C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
$LOCAL_DIRECTORY="C:\testdir\extended\baseroot"
# Load WinSCP .NET assembly
[Reflection.Assembly]::LoadFrom($DLL_LOCATION) | Out-Null
# Session.FileTransferred event handler
try
{
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::ftp
$sessionOptions.HostName = $HOSTNAME
$sessionOptions.UserName = $USERNAME
$sessionOptions.Password = $PASSWORD
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
$remoteFileWithUnixPath = "/latest/[b]test"
$removalResult = $session.RemoveFiles($remoteFileWithUnixPath)
if ($removalResult.IsSuccess)
{
Write-Host ("Removal of remote file {0} succeeded" -f $remoteFileWithUnixPath)
}
else
{
Write-Host ("Removal of remote file {0} failed" -f $remoteFileWithUnixPath)
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}
catch [Exception]
{
Write-Host $_.Exception.Message
exit 1
}
When I run it, it displays the following message:
PS C:\Users\dbuddrige\Documents\dps> .\delete-squarebracket-dir.ps1
Removal of remote file /latest/[b]test succeeded
However, the directory is not deleted.
I have also tried specifying the directory name with delimiters such as:
$remoteFileWithUnixPath = "/latest/\[b\]test"
But this also fails (but at least it says so):
PS C:\Users\dbuddrige\Documents\dps> .\delete-squarebracket-dir.ps1
Removal of remote file /latest/\[b\]test failed
BUT, if I change the directory name [and the variable $remoteFileWithUnixPath
] to something like
/latest/foo
And then re-run the program, it deletes the directory /latest/foo
just fine.
Does anyone have any ideas what I need to do to get this to work?
The answer to this question was pointed out by Martin Prikryl [Thanks!]
The fully working code follows:
# program to test deletion of directories with square-brackets in the name.
$HOSTNAME="myftpservername"
$USERNAME="myftpusername"
$PASSWORD="myftppassword"
$DLL_LOCATION="C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
$LOCAL_DIRECTORY="C:\testdir\extended\baseroot"
# Load WinSCP .NET assembly
[Reflection.Assembly]::LoadFrom($DLL_LOCATION) | Out-Null
# Session.FileTransferred event handler
try
{
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::ftp
$sessionOptions.HostName = $HOSTNAME
$sessionOptions.UserName = $USERNAME
$sessionOptions.Password = $PASSWORD
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
$remoteFileWithUnixPath = "/latest/[b]test"
$removalResult =
$session.RemoveFiles($session.EscapeFileMask($remoteFileWithUnixPath))
if ($removalResult.IsSuccess)
{
Write-Host ("Removal of remote file {0} succeeded" -f $remoteFileWithUnixPath)
}
else
{
Write-Host ("Removal of remote file {0} failed" -f $remoteFileWithUnixPath)
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}
catch [Exception]
{
Write-Host $_.Exception.Message
exit 1
}
Some methods of the WinSCP .NET assembly, including the the Session.RemoveFiles
, accept a file mask, not a simple path.
Square brackets have special meaning in the file mask.
You should always use the RemotePath.EscapeFileMask
method on file paths, before passing them to assembly methods.