4

I am trying to write a batch file that can take user input of an FTP link, parse out the user, password and domain, write them to a file and the call the file as input for the FTP command. I have successfully written it, up until someone had a password with an ! in it.

The FTP links are system generated and may contain special characters (ie: !, ^, *).

I am using setlocal delayedexpansion as I read the full string (ie: ftp://userid:password@some.domain.com) in a for loop using the /, :, and @ as delimiters.

I have tried to get creative and do endlocal delayedexpansion after parseing the output, but then the tokens don't work because I am ending the expansion. I have also tried writing each token to its own file and then assigning the content of the file as a variable with no luck.

I think there should be a method to do this, but I keep hitting a wall.

Here is a snippet of my code:

@ECHO OFF
setlocal enableextensions enabledelayedexpansion
echo Example FTP link:
echo.
echo "ftp://xxxxxxxx:yyyyyyyy@some.where.com"
echo.
set /p ftplink= Please enter full FTP link:  
echo.
set /p ftpfile= Please provide the file name you want to download:  

:FTPbat
:: Generate a bat file for FTP retrieval
for /f "tokens=1-4 delims=/:@" %%a in ("%ftplink%") do (
    set ftpdomain=%%~d
    set ftpuser=%%~b
    set ftppass=%%~c
    )

    echo open %ftpdomain%> get.ftp
    echo %ftpuser%>> get.ftp
    echo %ftppass%>> get.ftp
    echo binary>> get.ftp
    echo hash>> get.ftp
    echo mget %ftpfile%>> get.ftp
    echo disconnect>> get.ftp
    echo quit>> get.ftp 
:GetFTPFile
CLS
FTP -i -s:get.ftp

Here is an example FTP link:

ftp://username:pas!word@somewhere.com

Unfortunately, the special character can be anywhere in the password string or potentially in the username. Also, the username and password are not a uniform length (ie: 8 chars each). I found that out the hard way.

Any and all help is greatly appreciated.

Compo
  • 36,585
  • 5
  • 27
  • 39
JChase
  • 65
  • 5

2 Answers2

3

It's trivial (If you know what to do).
The key is to expand the FOR-paramter only in disabled delayed expansion mode (else the delayed expansion phase will modify ! characters).
But to use the variable content, use always the delayed expansion mode(Because only delayed expansion can safely expand any content).
Therefore you need to toggle the mode.

Reference: SO: How does the Windows Command Interpreter (CMD.EXE) parse scripts?

SETLOCAL EnableDelayedExpansion    
(
  for /f "tokens=1-4 delims=/:@" %%a in ("!ftplink!") do (
    SETLOCAL DisableDelayedExpansion
    set "ftpdomain=%%~d"
    set "ftpuser=%%~b"
    set "ftppass=%%~c"

    SETLOCAL EnableDelayedExpansion    
    echo open !ftpdomain!
    echo !ftpuser!
    echo !ftppass!
    echo binary
    echo hash
    echo mget !ftpfile!
    echo disconnect
    echo quit
    ENDLOCAL
    ENDLOCAL
  )
) > get.ftp

This code is simplyfied, better is to use

...
(echo open !ftpdomain!)
(echo(!ftpuser!)
(echo(!ftppass!)
(echo binary)
...

The enclosing parentheses avoid problems with trailing white spaces.
The ugly opening parenthesis after echo( is to avoid problems with empty variables or variables containing problematic stuff like /?, ON´ orOFF`.

jeb
  • 78,592
  • 17
  • 171
  • 225
  • That's the way it is with answers. Once you know them they are trivial. Until you do they can be aggravating. Thank you for the solution and the explanation and link. Now it can be trivial for more of us. :) – JChase Mar 11 '19 at 21:51
1

You could of course just not set those variables at all:

@Echo Off
Echo Example FTP link:
Echo(
Echo "ftp://username:password@domain.ext"
Echo(
Set /P "ftplink= Please enter full FTP link: "
Echo(
Set /P "ftpfile= Please provide the file name you want to download: "

(For /F "Tokens=2-4 delims=/:@" %%A In ("%ftplink%")Do (Echo open %%C
    Echo %%A
    Echo %%B
    Echo binary
    Echo hash
    Echo mget %ftpfile%
    Echo disconnect
    Echo quit))>get.ftp

ClS
FTP -i -s:get.ftp

You could still save those variables for later use in the script, if needed, you just don't need to use them within the loop!

(For /F "Tokens=2-4 delims=/:@" %%A In ("%ftplink%")Do (Set "ftpuser=%%A"
    Set "ftppass=%%B"
    Set "ftpdomain=%%C"
    Echo open %%C
    Echo %%A
    Echo %%B
    Echo binary
    Echo hash
    Echo mget %ftpfile%
    Echo disconnect
    Echo quit))>get.ftp
Compo
  • 36,585
  • 5
  • 27
  • 39