1

Given is a string which is a url of a file, e.g. http://url.com/file.zip. I want to extract the path without the file name, i.e. http://url.com/.

My idea for doing this was to break down the string from the end character by character in a FOR loop until I find the first slash /.

I wrote this script with the help of this guide: How-to: Extract part of a variable (substring)

@echo off
setlocal enabledelayedexpansion
set "fileAdd=https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg"
set /a n=-1
for /L %%G in (0,1,128) do (
    set /a m=!n!-1
    set "o=!%fileAdd:~%m%,%n%!"
    if not "!o!" == "/" set /a n-=1
)
set "webPath=!fileAdd:~0,%n%!"
echo %webPath%
endlocal
pause

The output should be: https://upload.wikimedia.org/wikipedia/commons/9/9f/

Unfortunately, it does not work. The problem is that the line set "o=!%fileAdd:~%m%,%n%!" works fine until I put it inside a FOR loop and then it stops working altogether. I also tried it with CALL: call set "o=!%fileAdd:~%m%,%n%!" That has the same problem.

Is it a simple syntax problem? Or is my approach simply impossible?
Is there a simpler solution than what I came up with?

4 Answers 4

1
@ECHO OFF
SETLOCAL
set "fileAdd=https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg"

:: if fileadd contains a colon, set webpath to the string after the colon
IF "%fileadd%" == "%fileadd::=%" (SET "filename=%fileadd%") ELSE (SET "filename=%fileadd:*:=%")
:: filename now looks like a Windows filename if the / were \
FOR /f %%e IN ("c:%filename:/=\%") DO SET "filename=%%~nxe"

:: IF the filename is guaranteed NOT to be IN the path, then

CALL SET "webpath=%%fileadd:%filename%=%%"

SET web

:: If that is not guaranteed then

:: Now we have the filename IN filename - mechanically remove it from fileadd to yield webpath
SET "webpath=%fileadd%"
:choploop
IF DEFINED filename SET "filename=%filename:~1%"&SET "webpath=%webpath:~0,-1%"&GOTO choploop

SET web
SET file

GOTO :EOF

Fundamentally, the approach transforms the string under examination into a Windows full-filename then finds the filename part and substitutes nothing for the filename.

The :/=\ to change the slashes to backslashes in probably not required. It appears to work on the forward slashes.

Sign up to request clarification or add additional context in comments.

Comments

1

1. Reasons for not working batch file in question

The FOR loop approach in batch file in question does not work because of set "o=!%fileAdd:~%m%,%n%!" has a misplaced first % and %m% and %n% are already expanded by the Windows Command Processor cmd.exe on parsing the entire command block before executing the command FOR. That can be seen on running the batch file in a Command Prompt window without @echo off at top or with changing this line to @echo on. On each loop iteration is executed:

set "o=!https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpgmn!"

%m% and %n% are already replaced by mn before the first FOR loop iteration.
See also: Variables are not behaving as expected

The command line with a removal of the first misplaced % would be:

set "o=!fileAdd:~%m%,%n%!"

That command line would be modified by cmd.exe before first loop iteration to:

set "o=!fileAdd:~,-1!"

%m% not defined at all above the FOR loop is replaced by an empty string and the environment variable reference %n% is replaced by -1 because of the environment variable n is defined with that string value above the FOR loop. This command line is executed repeatedly in the loop.

The correct syntax for using CALL would be:

call set "o=!fileAdd:~%%m%%,%%n%%!"

But there should not be used that command line because the use of CALL results not only in double parsing the command line before execution of command SET, but also searching in the current directory and next in all directories listed semicolon separated in string of environment variable PATH for a file matched by the wildcard pattern set.*. The command CALL is for calling a batch file from within a batch file and would be misused here to get set "o=!fileAdd:~%m%,%n%!" parsed once again by cmd.exe before the execution of command SET.

The batch file in question would not even work with correct command line using the command CALL.

2. Solution using file I/O functions of Windows

Here is a simple solution for this string extraction:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "fileAdd=https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg"
for %%I in ("%SystemDrive%\%fileAdd:/=\%") do for /F "tokens=1* delims=\:" %%G in ("%%~pI") do set "webPath=%%G://%%H"
set "webPath=%webPath:\=/%"
set webPath
endlocal

The url is prepended with the drive letter and the colon of the system drive which is usually C:. All / are additionally replaced by \ to form an absolute file name. The file name is not valid because of the character : after the protocol identifier like http, https, ftp.

The invalid absolute file name is for the example:

C:\https:\\upload.wikimedia.org\wikipedia\commons\9\9f\Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg

The path without drive letter and colon and without the string after last / as got with %~pI is:

\https:\upload.wikimedia.org\wikipedia\commons\9\9f\

This string is parsed by a second FOR loop which splits up the path string into two parts.

  • The first part is the protocol identifier, e.g. http, https, ftp
  • The second part is everything after :\ which is for the example: upload.wikimedia.org\wikipedia\commons\9\9f\

These two strings are concatenated with inserting :// after the protocol identifier.
The resulting string assigned to the environment variable webPath is:

https://upload.wikimedia.org\wikipedia\commons\9\9f\

A simple substring substitution replacing all \ by / converts the string back to an url without the (file name) string after last / and the result is:

https://upload.wikimedia.org/wikipedia/commons/9/9f/

Characters like %&!| in the url are no problem for this code.

This solution has two not obvious visible advantages:

cmd.exe is searching in file system for the directories:

C:\https:
C:\https:\upload.wikimedia.org
C:\https:\upload.wikimedia.org\wikipedia
C:\https:\upload.wikimedia.org\wikipedia\commons
C:\https:\upload.wikimedia.org\wikipedia\commons\9
C:\https:\upload.wikimedia.org\wikipedia\commons\9\9f

But that will be never successful because of second : in the directory paths making all the directory paths invalid. That is good in this special use case.

There is not used the command CALL with the command SET resulting in searching for set.* in current directory and all directories of the environment variable PATH which is never good in case of a file set.bat or set.cmd exists somewhere which is called in this case instead of running the internal command SET.

3. Solution using a GOTO loop

This solution uses a GOTO loop in the batch file which removes one character after the other from the url from the end until reaching last / of the url.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "fileAdd=https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg"
set "webPath=%fileAdd:~0,-1%"
:Loop
if not "%webPath:~-1%" == "/" set "webPath=%webPath:~0,-1%" & goto Loop
set webPath
endlocal

This loop solution looks simple but is horrible for the file system in comparison to the solution using the Windows file I/O functions because of dozens of opening the batch file, seeking for the label Loop, reading and parsing the next command line, closing the batch file, executing the command line and restarting the procedure again until the last / is found in the url. See: Why is a GOTO loop much slower than a FOR loop and depends additionally on power supply?

Comments

1

I think this is the simplest way:

@echo off
setlocal enabledelayedexpansion
set "fileAdd=https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg"
for %%f in ("%fileAdd:https=C%") do set "name=%%~DPf"
set "name=%name:*C:=https:\%"
set "webPath=%name:\=/%"
echo %webPath%

Just change the https: part by C: and the FOR modifiers can manage the name. After the Drive and Path is taken, replace the C:\ back to https:\\


Here it is another very simple method that does not use the FOR filename modifiers:

@echo off
setlocal enabledelayedexpansion
set "fileAdd=https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg"
set "webPath=" & set "file="
for %%a in ("%fileAdd:/=" "%") do (
   set "webPath=!webPath!!file!"
   set "file=%%~a/"
)
echo WebPath: %webPath%
echo File:    %file%

Just separate the name in parts at each / in a simple FOR command and then join the parts again, excepting the last one.


And another one. Perhaps the most simple of all! ;)

@echo off
setlocal enabledelayedexpansion
set "fileAdd=https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg"

for %%f in ("%fileAdd%") do set "webPath=!fileAdd:%%~NXf=!"

echo %webPath%

Here it is another one! It uses the same method of answer 2 above: separate elements by slash and joint them again excepting the last one, but it uses my magic method to split a string in parts, instead of a FOR command:

@echo off
setlocal enabledelayedexpansion
set "fileAdd=https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg"

set "webPath=" & set "file=%fileAdd:/=" & set "webPath=!webPath!!file!/" & set "file=%"

echo WebPath: %webPath%
echo File:    %file%

Comments

1

Using a mix of several good suggestions but tidied up as a CMD function and improved per Mofi's comments. However as noted it will be far slower than for loops. and when running hundreds the fastest is a C program (NOT c#.net which is roughly as slow as a CMD file)

Backsplit https://upload.wikimedia.org/wikipedia/commons/9/9f/Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg

Result from backsplit.cmd

webPath=https://upload.wikimedia.org/wikipedia/commons/9/9f/

Optionally also:

file=Vervet_monkey_(Chlorocebus_pygerythrus)_Maputo.jpg
@echo off
set "web=%~1" 
:loop
set "chr=%web:~-1%"
if "%chr%"=="/" goto Done
set "file=%chr%%file%" & set "web=%web:~0,-1%"
goto loop
:Done
echo webPath=%web%

Optionally you can also echo file=%file%

I mentioned using C or C# and whilst a Windows CMD can easily write a C#.net filter the speed is no better than a for loop. BUT for writing a 2.00 KB (2,048 bytes) filter. I have tested this cmd produces fastest results.

:: Download TinyCCompiler & Extract to temp
curl -Lo "%tmp%\tcc.zip" https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27-win32-bin.zip
tar -xf "%tmp%\tcc.zip" -C "%tmp%"
:: Write C source to temp
echo #include ^<stdio.h^> > "%temp%\lastslash.c"
echo #include ^<string.h^> >> "%temp%\lastslash.c"
echo int main(){char s^[1024^];while(fgets(s,sizeof(s),stdin)){if(s^[0^]=='^"')memmove(s,s+1,strlen(s));char*p=strrchr(s,'/');if(p)*(p+1)='\0';puts(s);}return 0;} >> "%temp%\lastslash.c"
:: Compile with TinyCC
"%tmp%\tcc\tcc.exe" -o "%cd%\lastslash.exe" "%tmp%\lastslash.c"
:end

What you should get is a file called lastslash.exe, which you then use to filter a list. in milliseconds.

lastslash < listfile.txt
http://url.com/
https://cdn.pixabay.com/photo/2023/05/01/12/34/
https://docs.example.org/research/2025/11/08/
https://upload.wikimedia.org/wikipedia/commons/9/9f/
https://videos.example.com/stream/2025/11/08/
https://maps.example.com/tiles/zoom/12/x/345/y/
https://www.example.com/products/electronics/laptops/
https://cdn.site.net/docs/2025/11/08/
https://news.example.com/
https://shop.example.com/products/view/

How it works is fairly basic it disposes of any starting " then gathers characters after that from start of line to last / then disregards character's after that so it parses full lines at highest speed without better optimisation!

You can simply replace / with \\ (note it is doubled) to do similar with c:\path to\ or \\server\path\ but may need to adjust for more complex cases. If we have a directory listing it will be less use :-)

lastback <dirlist
\\advent\users\public\
C:\Users\WDAGUtilityAccount\Desktop\Apps\Programming\c#\pipes\
C:\Users\WDAGUtilityAccount\Desktop\Apps\Programming\c#\pipes\
C:\Users\WDAGUtilityAccount\Desktop\Apps\Programming\c#\pipes\
...

1 Comment

Although your method works, it is advisable to not use it in a real production program. As Mofi said in his answer, any loop assembled via goto instructions is slow, and a loop that processes character by character is much slower... I invite you to use the methods of my answer: the number 2 for a classical FOR loop solution, or the method number 4 that is even faster (because it does not use any FOR)...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.