3

I am trying to write a function that takes in a parameter and classifies it as a folder name (FOLDER), a RELATIVE_PATHNAME, or an ABSOLUTE_PATHNAME.

What I have so far is this:

@echo off
call :classifyInput C:\Users\MikeW
echo C:\Users\MikeW is of type %_inputType%
call :classifyInput documentation
echo documentation is of type %_inputType%
goto :EOF

:classifyInput
SETLOCAL
set _inputType=
rem // a string is a "name of folder" iff it has no '/', '\\' or ':' in it, and a pathname otherwise
echo %1%|findstr /i [\/:] >nul
if %errorlevel% == 1 set _inputType=FOLDER
echo %errorlevel%
rem // a string is a "relative pathname" iff it has no ':' as its second character, and an absolute pathname otherwise
if _inputType == [] & "%1:~2,1% == :" (
    _inputType=ABSOLUTE_PATHNAME
    echo You gave me absolute pathname.
)

ENDLOCAL & set _inputType=%_inputType%

And my function so far behaves the same way for both cases.

Furthermore, a Google search for how to find the characters /, \, : is returning nothing useful.

Mofi
  • 46,139
  • 17
  • 80
  • 143
Mike Warren
  • 3,796
  • 5
  • 47
  • 99
  • I am well aware that I didn't handle `RELATIVE_PATHNAME`. That was intentional, as I didn't get to it, as I'm trying to figure out what the hell is going wrong with what I have thus far. – Mike Warren Jan 07 '17 at 22:03
  • I'm at the point where I want to just write a program to send my CMD variables to, that would return something back to the CMD process. Problem is I don't know how to do that, either.... – Mike Warren Jan 07 '17 at 22:06
  • Relative paths may also contain `:` -- think of `C:folder`, for instance... – aschipfl Jan 08 '17 at 20:08
  • @aschipfl It is right that `C:folder` is a relative path specifying subfolder `folder` in current directory on drive `C:`. But it can be assumed that `C:folder` is approximately to 99.999999999% a mistyped absolute path than an intentionally used relative path. In more than 20 years of Windows computer usage I have never seen that somebody used such a path intentionally as relative path. However, I adapted the code in my answer for checking on absolute path. – Mofi Jan 14 '17 at 18:27
  • @Mofi, every drive has its own current path since MS-DOS times; back then it was convenient to use relative paths like `D:file.ext` particularly in case you have more than one disk drives; this "relict" is still there in Windows `cmd`. Anyway, I just wanted to point out that there could occur special cases that might treated wrongly in case paths are not handled by path-related functions but by string manipulation functions... – aschipfl Jan 14 '17 at 19:32

3 Answers3

4

To answer your question to find the characters /, \, : with findstr you can just use a regex like this:

findstr "[\\/:]"

EDIT: or if you want to find \\, / or : in a string as described in the comments of your script you can use:

findstr /r "\\\\ / :"

Because the \ is the escape character in regex, you'll need to escape it with itself in order to get the real \. findstr uses the whitespace in its regular expressions as OR operator.

Before I let you continue, I'd like to point out some little errors in your code and help you improve it:

  • the comparison _inputType == [] will compare the literal string "_inputType" with the string "[]". If you want to check if a variable is empty or not, use the if defined var that will turn true only if %var% is not empty (use if not for the opposite behaviour) or use if "%var%" == "". But be sure you use the same delimiters because the == operator will compare each single character: if abc == "abc" will never turn be taken but if "abc" == "abc" will.

  • substring extraction will only work on environment variables that has been defined with set. It won't work on arguments variables (nor on loop variables). So %1:~2,1% won't get what you want. It will just replace %1 with the value of the argument in there and give something like C:\Users\MikeW:~2,1% for example. If you want to extract from an argument you'll have to set a variable with the value of the argument and extract from that variable:

    set input=%~1
    rem now you can use %input:~2,1%
    

    In your code I read that you want the 2nd character of the input: %input:~2,1% will give you the 3rd, not the 2nd. In case of substring, read %var:~n,k% as k characters after the n-th character. So what you need is %input:~1,1%

  • I presume you tried to perform an AND operation on this line:

    if _inputType == [] & "%1:~2,1% == :"
    

    Unfortunately, the AND and the OR operators don't exist in batch, so you'll have to "mock" the AND yourself, by using consecutive if-statements (after having applied the modifications proposed above):

    set input=%~1
    if not defined _inputType if "%input:~1,1%" == ":"
    

Finally I got this:

@echo off
call :classifyInput C:\Users\MikeW
echo C:\Users\MikeW is of type %_inputType%
call :classifyInput documentation
echo documentation is of type %_inputType%
call :classifyInput Documents\Folder\inside
echo Documents\Folder\inside is of type %_inputType%
goto :EOF

:classifyInput
SETLOCAL
set _inputType=
set input=%~1

rem // a string is a "name of folder" iff it has no '/', '\\' or ':' in it, and a pathname otherwise
echo %input%|findstr /r "\\\\ / :" >nul
if %errorlevel% == 1 set _inputType=FOLDER

echo %errorlevel%

rem // a string is a "relative pathname" iff it has no ':' as its second character, and an absolute pathname otherwise
if not defined _inputType if "%input:~1,1%" == ":" (
    set _inputType=ABSOLUTE_PATHNAME
    echo You gave me absolute pathname.
) else (
    set _inputType=RELATIVE_PATHNAME
)

ENDLOCAL & set _inputType=%_inputType%

The script above will result in this:

0
You gave me absolute pathname.
C:\Users\MikeW is of type ABSOLUTE_PATHNAME
1
documentation is of type FOLDER
0
Documents\Folder\inside is of type RELATIVE_PATHNAME

PS: If you can remove the echo %errorlevel%, you can simplify the :classifyInput function like this:

:classifyInput
SETLOCAL
set _inputType=
set input=%~1

rem // a string is a "name of folder" iff it has no '/', '\\' or ':' in it, and a pathname otherwise
echo %input%|findstr /r "\\\\ / :" >nul
if %errorlevel% == 1 (
    set _inputType=FOLDER
) else (
     if "%input:~1,1%" == ":" (
        set _inputType=ABSOLUTE_PATHNAME
        echo You gave me absolute pathname.
    ) else (
        set _inputType=RELATIVE_PATHNAME
    )
)

ENDLOCAL & set _inputType=%_inputType%
J.Baoby
  • 2,167
  • 2
  • 11
  • 17
0

The commands SET and IF can be used to check if a string assigned to an environment variable contains a specific character or string.

Example:

@echo off
setlocal EnableExtensions
for %%I in ("C:\Users\MikeW" "Folder\FileName" "Folder or file name") do call :ColonBackslash %%I
endlocal
goto :EOF

:ColonBackslash
set "Argument=%~1"

rem Substitute in argument string all colons by nothing and compare this
rem string with unmodified argument string. The argument string contains
rem a colon if the modified and the unmodified strings are not equal.

if not "%Argument::=%" == "%Argument%" (
    echo Colon in argument:        %Argument%
    goto :EOF
)

rem Substitute in argument string all backslashes by nothing and compare
rem this string with unmodified argument string. The argument string
rem contains a backslash if the modified and the unmodified strings
rem are not equal.

if not "%Argument:\=%" == "%Argument%" (
    echo Backslash in argument:    %Argument%
    goto :EOF
)
echo No colon or backslash in: %Argument%
goto :EOF

This batch file outputs:

Colon in argument:        C:\Users\MikeW
Backslash in argument:    Folder\FileName
No colon or backslash in: Folder or file name

Comparing strings with substituted characters/strings with unmodified string can be used to determine type of input string as demonstrated with the batch code below:

@echo off
setlocal EnableExtensions
for %%I in ("C:\Users\MikeW" "C:\Users\Mi:keW" "documentation" "\\server\share\folder" "\\server\share\folder1\..\folder2" "\\server\share\fo:lder" "\\server\" "\\server\share\folder\..\" "%SystemRoot%\..\" ".\Folder" "..\..\Folder" "\Windows" ">:Invalid") do call :classifyInput %%I
endlocal
goto :EOF

:classifyInput
rem Subroutine called with no parameter or with just "" as parameter.
if "%~1" == "" goto :EOF
set "Argument=%~1"
rem Replace all forward slashes by backslashes in argument string.
set "Argument=%Argument:/=\%"

rem Is the second character a colon and third character a backlash?
if "%Argument:~1,2%" == ":\" goto AbsolutePath

rem Is there a colon anywhere else than at second position in argument?
rem C:Folder is interpreted as invalid argument and not as relative
rem path to subfolder "Folder" of current directory on drive C:.
if not "%Argument::=%" == "%Argument%" (
    echo Invalid argument:  %1
    goto :EOF
)

rem Starts the argument with two backslashes?
if "%Argument:~0,2%" == "\\" goto PathUNC

rem Starts the argument with \ for a path relative to current drive?
if "%Argument:~0,1%" == "\" (
    echo Relative path:     %1
    goto :EOF
)

rem Starts the argument with .\ for a path relative to current directory?
if "%Argument:~0,2%" == ".\" (
    echo Relative path:     %1
    goto :EOF
)

rem Starts the argument with ..\ for a path relative to parent directory?
if "%Argument:~0,3%" == "..\" (
    echo Relative path:     %1
    goto :EOF
)

rem Contains the argument a backslash for a path relative to current directory?
if not "%Argument:\=%" == "%Argument%" (
    echo Relative path:     %1
    goto :EOF
)

echo Name without path: %1
goto :EOF

:AbsolutePath
set "Remaining=%Argument:~2%"
rem Is there a colon anywhere else after second position in argument?
if not "%Remaining::=%" == "%Remaining%" (
    echo Invalid argument:  %1
    goto :EOF
)

rem Is the first character a drive letter if second character is a colon?
set "FirstIsLetter="
for /F "delims=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" %%# in ("%Argument:~0,1%") do set "FirstIsLetter=%%#"
if not "%FirstIsLetter%" == "" (
    echo Invalid argument:  %1
    goto :EOF
)

rem Contains the absolute path also .\ or ..\ in path?
if "%Argument:.\=%" == "%Argument%" (
    echo Absolute path:     %1
) else (
    echo Abs. + rel. path:  %1
)
goto :EOF

:PathUNC
rem Does the UNC path contain also a colon?
if not "%Argument::=%" == "%Argument%" (
    echo Invalid argument:  %1
    goto :EOF
)

set "ServerName="
set "ShareName="
set "Remaining="
for /F "tokens=1,2* delims=\" %%A in ("%Argument%") do (
    set "ServerName=%%A"
    set "ShareName=%%B
    set "Remaining=%%C"
)

rem Is there no share name specified after server name in UNC path?
if "%ShareName%" == "" (
    echo Invalid argument:  %1
    goto :EOF
)

rem Is there an invalid share name specified after server name in UNC path?
if "%ShareName%" == "." (
    echo Invalid argument:  %1
    goto :EOF
)

rem Is there an invalid share name specified after server name in UNC path?
if "%ShareName%" == ".." (
    echo Invalid argument:  %1
    goto :EOF
)

rem Contains the UNC path also .\ or ..\ in remaining path?
if "%Remaining:.\=%" == "%Remaining%" (
    echo UNC path:          %1
) else (
    echo UNC  + rel. path:  %1
)
goto :EOF

The output is:

Absolute path:     "C:\Users\MikeW"
Invalid argument:  "C:\Users\Mi:keW"
Name without path: "documentation"
UNC path:          "\\server\share\folder"
UNC  + rel. path:  "\\server\share\folder1\..\folder2"
Invalid argument:  "\\server\share\fo:lder"
Invalid argument:  "\\server\"
UNC  + rel. path:  "\\server\share\folder\..\"
Abs. + rel. path:  "C:\WINDOWS\..\"
Relative path:     ".\Folder"
Relative path:     "..\..\Folder"
Relative path:     "\Windows"
Invalid argument:  ">:Invalid"

But this batch code is not complete for checking for invalid paths, folder or file names in an argument string. There are still a lot of possible errors not detected by this batch code.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • rem /?
  • set /?
  • setlocal /?

It would be better to let Windows kernel check if the argument specifies an existing folder or file independent if the argument string is without or with a path of any type and being valid at all. See the topics:

And in case of testing for validity on non existing folder/file, just create the file/folder, suppress the error message using 2>nul (see the Microsoft article Using command redirection operators) and check with if errorlevel 1 or with if exist "%~1" if the folder/file could be created successfully, or if this operation failed because of an invalid folder/file name string, denied access, missing permissions, etc.

Community
  • 1
  • 1
Mofi
  • 46,139
  • 17
  • 80
  • 143
0

Preface: This does not answer how to search a string for /, \, :; it concentrates on the proper classification of a path!


First off, I strongly recommend to avoid to do path classification using pure string manipulation functions, because this is very prone to errors; use path-related features instead!

Then you need to clarify what the routine should output for the following cases:

  • input is something like name: you call it FOLDER, but this could also point to a file instead of a folder, or it does not even exist, so we do not know whether it is a file or a folder; I classify such items PURENAME; note that this is nothing but a particular relative path;
  • input is something like .\name: this I classify RELATIVE, similar to you (RELATIVE_PATHNAME);
  • input is something like D:\name: this I classify ABSOLUTE, similar to you (ABSOLUTE_PATHNAME);
  • input is something like D:name: there is a colon :, so you would call it ABSOLUTE_PATHNAME, although this constitutes a path relative to the current directory of drive D:, hence I classify it RELATIVE;
  • input is something like \name: there is no drive specified, so I classify it RELATIVE, as it points to the root of the current drive;
  • input is something like \\Host\Share\name (UNC path): this needs to be handled correctly (some commands are not capable of dealing with such paths); I classify them ABSOLUTE;
  • input contains forward-slashes /: many commands do not treat them as path separators, therefore I replace all / by \ before classification;
  • input contains quotation marks ": they are not valid characters to be used for file and folder names, therefore I remove all of them, as many commands ignore them in paths anyway;
  • input contains additional colons : beside the one after the drive letter (illegal path), or it contains forbidden characters like <, >, |: the input is not checked for validity;
  • input is not checked against existence;

Here is a script that classifies path items you enter in a prompt (empty input terminates script):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

:LOOP
rem // Derive user input string here:
set "INPUT=" & set /P INPUT="Enter a path: "
rem // Quit script in case of empty input:
if not defined INPUT exit /B
rem // Remove quotation marks `"`:
set "INPUT=%INPUT:"=%
rem // Replace all `/` characters by `\`:
if defined INPUT set "INPUT=%INPUT:/=\%"
rem // Call classification sub-routine:
call :CLASSIFY ITYPE "%INPUT%"
rem // Return resulting class:
echo "%INPUT%" is of type %ITYPE%.
rem // Loop back to user input:
goto :LOOP

endlocal
exit /B


:CLASSIFY  rtn_class  val_path
    setlocal DisableDelayedExpansion
    set "CLA="
    rem // Check whether argument is empty:
    if "%~2"=="" set "CLA=EMPTY" & goto :RETURN
    rem /* Check whether argument contains wild-cards (like `?` and `*`);
    rem    the `for` loop iterates exactly once in case no wild-cards are present;
    rem    if there are wild-cards, the loop iterates wither zero times (no matching
    rem    items found), or as many times as there are matching items, hence the
    rem    wild-cards becime resolved (replaced), so the `if` condition fails: */
    set "CLA=WILDCARD"
    for %%I in ("%~2") do if /I "%~2"=="%%~I" set "CLA="
    if defined CLA goto :RETURN
    rem // Check whether argument is a pure name:
    if /I "%~2"=="%~nx2" set "CLA=PURENAME" & goto :RETURN
    rem // Check whether argument is a simple relative path:
    if /I "%~d2"=="" set "CLA=RELATIVE" & goto :RETURN
    rem // Check whether argument is a simple absolute path:
    if /I "%~2"=="%~f2" set "CLA=ABSOLUTE" & goto :RETURN
    if /I "%~2"=="%~s2" set "CLA=ABSOLUTE" & goto :RETURN
    rem /* Check whether argument is a complex relative or absolute path; for this,
    rem    the drive is split off from the argument to cover `D:path` items too: */
    set "DRV=%~d2" & set "ARG=%~2"
    if defined DRV call set "STR=%%ARG:*%DRV%=%%"
    if defined DRV if defined STR set "STR=%STR:^^=^%"
    if /I not "%DRV%%STR%"=="%ARG%" set "STR=%ARG%"
    if not defined STR set "STR=."
    rem // The `for` loop iterates once only and is needed to resolve `~` modifiers:
    for %%I in ("%STR%") do (
        rem /* Now check whether argument is an absolute or relative path; for this,
        rem    the argument is converted to an absolute path, related to the current
        rem    working directory first; then the current directory is substituted by
        rem    a temporary drive named `#:`, whose root becomes the current working
        rem    directory temporarily; the argument is again converted to an absolute
        rem    path, related to the temporary working directory; if the result is
        rem    the same as the one from the previous path conversion, the argument
        rem    holds an absolute path, otherwise it holds a relative one: */
        set "LNG=%%~fI"
        set "SHT=%%~sI"
        pushd .
        subst #: . & #:
        set "CLA=ABSOLUTE"
        if /I not "%%~fI"=="%LNG%" if /I not "%%~sI"=="%SHT%" set "CLA=RELATIVE"
        popd & subst #: /D
    )
    :RETURN
    rem // Return resulting class:
    endlocal & set "%~1=%CLA%"
    exit /B

The script returns the following classes:

  • PURENAME: (relative) path consists of a pure name only (similar to your class FOLDER);
  • RELATIVE: path is relative (no drive is given, path does not begin from root);
  • ABSOLUTE: path is absolute (drive is given, path begins from root, or path is UNC path);
  • WILDCARD: path contains global wild-cards like ? and/or *;
  • EMPTY: path is empty (something like "" has been entered);
aschipfl
  • 33,626
  • 12
  • 54
  • 99