David
David

Reputation: 585

Unable to delete a directory with square brackets in name using WinSCP and PowerShell

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
}

Upvotes: 2

Views: 1385

Answers (2)

Martin Prikryl
Martin Prikryl

Reputation: 202504

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.

Upvotes: 1

ravikanth
ravikanth

Reputation: 25810

I'd use the escape sequence in the commandline:

Remove-Item 'C:\Scripts\Test`[1`].txt'

Or use the -LiteralPath parameter.

Remove-Item -LiteralPath C:\scripts\test[1].txt

The -LiteralPath parameter does not try to convert the string.

Upvotes: 0

Related Questions