Strip folder, file and extension from a string?

Microsoft Windows
Post Reply
User avatar
MigrationUser
Posts: 336
Joined: 2021-Jul-12, 1:37 pm
Contact:

Strip folder, file and extension from a string?

Post by MigrationUser »

24 Jun 2016 11:43
NDog

Is it possible to strip a folder, file and extension from a string only?

eg - \\uncpaththatdoesnot-exist\1\2\3\4\5\6\7\8\9\really long path\abcdefghijklmnopqrstuvwxyz\foo!OS!bar%PROMPT%baz\ 34234 \Technical Experts Exchange ss64 rocks\RD 123\VPN 456\parent folder\myscript.sh

return these values
dir=\\uncpaththatdoesnot-exist\1\2\3\4\5\6\7\8\9\really long path\abcdefghijklmnopqrstuvwxyz\foo!OS!bar%PROMPT%baz\ 34234 \Technical Experts Exchange ss64 rocks\RD 123\VPN 456\parent folder
file=myscript
extension=.sh

or with an incomplete path
eg - foo!OS!bar%PROMPT%baz\ 34234 \Technical Experts Exchange ss64 rocks\RD 123\VPN 456\parent folder\myscript.sh

return these values
dir=foo!OS!bar%PROMPT%baz\ 34234 \Technical Experts Exchange ss64 rocks\RD 123\VPN 456\parent folder
file=myscript
extension=.sh

the problem is that foo!OS!bar%PROMPT%baz could be considered a valid path, and or I might have a really long path.

I do not want to process the folder\file, only the string directly.

Last edited by NDog (24 Jun 2016 12:18)

cmd, vbs, ps, bash
autoit, python, swift

----------------------------

#2 24 Jun 2016 14:52
bluesxman

How is the string getting into your script? Handling special characters, like "%", could make this a tricky proposition.

cmd | *sh | ruby | chef

----------------------------

#3 24 Jun 2016 15:18
NDog

If I get it working I will post the script for you so you can see how the string is imported.
I want to split string based on the last delimiter.

eg
$var = adsflkj\sdfasd\fas\e23 \234 %325367\ \\\ sdflkjher
Everything to the left of the last '\' = $var[0] = adsflkj\sdfasd\fas\e23 \234 %325367\ \\
Everything to the right of the last '\' = $var[1] = sdflkjher

I'm interested to see if it's been done before... I have run into this issue many times with batch and would like to finally solve it.
If not possible theres always vbscript...

cmd, vbs, ps, bash
autoit, python, swift

----------------------------

#4 24 Jun 2016 18:50
RG

Try something like this: Edited so that it does not require SETLOCAL ENABLEDELAYEDEXPANSION

Code: Select all

@echo off
set "String=\\uncpaththatdoesnot-exist\1\2\3\4\5\6\7\8\9\really long path\abcdefghijklmnopqrstuvwxyz\foo!OS!bar%PROMPT%baz\ 34234  \Technical Experts Exchange ss64 rocks\RD 123\VPN 456\parent folder\myscript.sh"
set "Folder="
set "File="
call :ParseString "%String%"
CALL set "ParentFolder=%%String:%File%=%%"
set "ParentFolder=%ParentFolder:~0,-1%"
echo(Dir=%ParentFolder%
call :DisplayFileExt "%File%"
pause
goto :eof

:ParseString & rem this is a recursive routine
rem 1=String
for /f "delims=\ tokens=1*" %%a in ('echo(%~1') do (
rem    call set "Folder=%%File%%"
   set File=%%a
   if not "%%b"=="" call :ParseString "%%b"
   )
goto :eof

:DisplayFileExt
REM 1=File.Ext
echo(File=%~n1
echo(Ext=%~x1
goto :eof
Last edited by RG (27 Jun 2016 04:02)

Windows Shell Scripting and InstallShield

----------------------------

#5 24 Jun 2016 19:47
NDog

You are right with the %% characters bluesxman! I don't think batch will play nicely with strings with those haha.

I'm still curious to know if this is possible in batch... Any takers?

In the meantime I have offloaded to vbsscript.

The following script is useful to copy files or folders from one location to another, preserving the file/folder structure on the destination. Simply paste the valid subpath at the top of the script that you want copied and the root source under _src= and the script will take care of the rest.

If you are trying to copy paths or strings with % or & characters it won't work. If you ^cancel^ them maybe? It handles the usual stuff well ! and spaces.

Code: Select all

copy-osx.cmd

@echo off&cls
goto :start

### folder pair ###
Archive\Keka
Desktop\Cool Retro Term
File Transfer\uTorrent

iLife\iMovie 9 updates
Video\Quicktime Pro



### file pairs ###
KeepUpdates.rtf

## archive
Archive\DropDMG\DropDMG-3.3.dmg

## Office 2011
OSX\Office\Microsoft Office 2011\SW_DVD5_Office_Mac_Standard_2011w_SP3_English_ISO_MLF_X18-77844.ISO
OSX\Office\Microsoft Office 2011\Office2011-1465Update_EN-US.dmg

## development
Development\Python\pycharm-professional-2016.1.dmg
Development\Python\python-3.5.1-macosx10.6.pkg
Development\Swift\Xcode 7.3.1.dmg
Development\Swift\install-xcode.sh

## desktop
Desktop\Flux\Flux.zip
NTFS Support\Paragon NTFS\OSX 10.11\Batman Paragon NTFS 14.dmg


:start
@echo off&cls
setlocal
color 9f
title %~n0 - ndog
:: copy-osx.cmd 
:: last updated @ 24/06/2016 - ndog
::
:: copies files or folders from the source '_src' to the destination '_dst'
:: desired file and folder paths are specified at the top of the script
::
:: advantage of this script is you do not have to define a destination location
:: as the script automatically creates directory structure on the destination based on the source
:: it is fast and efficient and will not copy files needlessly

set _src=\\saturn\~USB\install\OSX
set _dst=D:\install\osx

set _robocopy=%~dp0robocopy.exe
set _fsync=%~dp0fsync.exe

:: read pairs into memory
for /f "tokens=* usebackq" %%g in (%0) DO (
  if exist "%_src%\%%g" call :sub_addarray "%%g"
  if "%%g" equ ":start" goto :exitloop
  )

:exitloop
:: menu
echo Source: %_src%
echo Destination: %_dst%
echo.
echo Located pairs...
for /f "usebackq tokens=1-2* delims==" %%g in (`set _pair_ 2^>nul`) do (echo ^> %%h)
echo.
pause
color 4f

:: offload to vbscript to process file/folder paths
set _tmpvbs=%temp%\splitpath.vbs
if not exist "%_tmpvbs%" goto :sub_stringsplitvbs

:: process pairs in memory
for /f "usebackq tokens=1-2* delims==" %%g in (`set _pair_`) do (
  rem echo g %%g
  rem echo h %%h
  rem cscript //nologo "%_tmpvbs%" "%%h"
  for /f "tokens=*" %%i in ('cscript //nologo "%_tmpvbs%" "%%h"') do (
    rem echo i %%i
    call :sub_processpair %%i
	)
  )

:end
:: finished
color 2f
pause
endlocal


:sub_addarray
  set _string=%1
  set _string=%_string:~1%
  set _string=%_string:~0,-1%
  
  set /a _filecount+=1
  if %_filecount% lss 10 set _pair_0%_filecount%=%_string%
  if %_filecount% geq 10 set _pair_%_filecount%=%_string%
  set _string=
  goto :eof


:sub_processpair
  :: %1 = folder, %2 = file
  rem echo %1
  rem echo %2
  set _dir=%1
  set _dir=%_dir:~1%
  set _dir=%_dir:~0,-1%
  
  md "%_dst%\%_dir%" 2>nul
  
  if [%2] == [] (
    rem folder operation
	"%_fsync%" "%_src%\%_dir%" "%_dst%\%_dir%" /f /c
	goto :eof
    )
	
  rem file operation
  set _file=%2
  set _file=%_file:~1%
  set _file=%_file:~0,-1%
  rem echo %_dir%
  rem echo %_file%
  if defined _dir (
    "%_robocopy%" "%_src%\%_dir%" "%_dst%\%_dir%" "%_file%" /NJH /NJS
	) else (
	  "%_robocopy%" "%_src%" "%_dst%" "%_file%" /NJH /NJS
	  )
  goto :eof

  
:sub_stringsplitvbs
  echo ' full path passed as an argument encapsulated with "" speech marks>"%_tmpvbs%"
  echo filepath = wscript.arguments(0)>>"%_tmpvbs%"
  echo.>>"%_tmpvbs%"
  echo ' split path into array based on '\' character>>"%_tmpvbs%"
  echo patharray = split(filepath, "\")>>"%_tmpvbs%"
  echo ' look for presence of a file if '.' character in string>>"%_tmpvbs%"
  echo dotfile = instrrev(filepath, ".")>>"%_tmpvbs%"
  echo.>>"%_tmpvbs%"
  echo ' check for path in root>>"%_tmpvbs%"
  echo if ubound(patharray) = 0 then>>"%_tmpvbs%"
  echo   if dotfile ^> 0 then>>"%_tmpvbs%"
  echo     file = filepath>>"%_tmpvbs%"
  echo     wscript.echo """" + """ """ + file + """">>"%_tmpvbs%"
  echo     wscript.quit>>"%_tmpvbs%"
  echo   else>>"%_tmpvbs%"
  echo     dir = filepath>>"%_tmpvbs%"
  echo     wscript.echo """" + dir + """">>"%_tmpvbs%"
  echo     wscript.quit>>"%_tmpvbs%"
  echo   end if>>"%_tmpvbs%"
  echo end if>>"%_tmpvbs%"
  echo.>>"%_tmpvbs%"
  echo ' loop through elements to build a complete parent path>>"%_tmpvbs%"
  echo for i = lbound(patharray) to ubound(patharray) - 1 >>"%_tmpvbs%"
  echo   parent_dir = parent_dir + patharray(i) + "\">>"%_tmpvbs%"
  echo next>>"%_tmpvbs%"
  echo.>>"%_tmpvbs%"
  echo ' look for file>>"%_tmpvbs%"
  echo ' return string encapsulated "folder" "file">>"%_tmpvbs%"
  echo if dotfile ^> 0 then>>"%_tmpvbs%"
  echo   file = patharray(ubound(patharray))>>"%_tmpvbs%"
  echo   dir = left(parent_dir, len(parent_dir) - 1)>>"%_tmpvbs%"
  echo   wscript.echo """" + dir + """ """ + file + """">>"%_tmpvbs%"
  echo   wscript.quit>>"%_tmpvbs%"
  echo else>>"%_tmpvbs%"
  echo   dir = filepath>>"%_tmpvbs%"
  echo   wscript.echo """" + dir + """">>"%_tmpvbs%"
  echo   wscript.quit>>"%_tmpvbs%"
  echo end if>>"%_tmpvbs%"

  goto :eof
 


cmd, vbs, ps, bash
autoit, python, swift

----------------------------
#6 25 Jun 2016 07:35
NDog

Thanks RG

I see your script does what I asked. I will try to get it integrated into my script as it seems to work well. Will post back here when I get it working!

cmd, vbs, ps, bash
autoit, python, swift

----------------------------

#7 26 Jun 2016 10:26
NDog

@RG
I like how your code is clever and strips the %File% string off the end original %String%, resulting in dividing the string in half effectively. I learnt something new today! 8-)
However is it possible to do this without setlocal enabledelayedexpansion? :D

Code: Select all

setlocal enabledelayedexpansion
set "ParentFolder=!String:%File%=!"
set "ParentFolder=%ParentFolder:~0,-1%"
echo(Dir=%ParentFolder%
For example this will strip '(Video)' away

Code: Select all

set str=4\5\6\(Video)
set str=%str:(Video)=%
echo %str% ---- str 
but this wont strip '4124 2354.txt' away

Code: Select all

set str=1\2\3\4124 2354.txt
set file=4124 2354.txt
set str=%str:%file%=%
echo -- %str% --
Last edited by NDog (26 Jun 2016 14:55)

cmd, vbs, ps, bash
autoit, python, swift

----------------------------

#8 27 Jun 2016 04:03
RG

Thanks @NDog, I edited my code above. It no longer requires setlocal enabledelayedexpansion

Windows Shell Scripting and InstallShield

----------------------------

#9 27 Jun 2016 04:27
Shadow Thief

Interestingly, it seems that batch will treat the string like a path even when the path does not exist.

Code: Select all

@echo off

set "fake_path=C:\this\path\is\definitely\fake.jpg"

:: %fake_path% is being treated like a string here
for /f %%A in ("%fake_path%") do (
	echo Path: %%~dpA
	echo File: %%~nA
	echo Extension: %%~xA
)

pause
returns

Path: C:\this\path\is\definitely\
File: fake
Extension: .jpg

Last edited by Shadow Thief (27 Jun 2016 04:32)

----------------------------

#10 27 Jun 2016 09:24
bluesxman

RG: your answer seems to fall foul of what I was talking about with "%" handling.

Here's the output I get:

Code: Select all

Dir=\\uncpaththatdoesnot-exist\1\2\3\4\5\6\7\8\9\really long path\abcdefghijklmnopqrstuvwxyz\foo!OS!bar:: $p$_:: $d $t $g baz\ 34234  \Technical Experts Exchange ss64 rocks\RD 123\VPN 456\parent folder
File=myscript
Ext=.sh
Note that the first line now contains the contents of my "PROMPT" variable ":: $p$_:: $d $t $g"

There are a couple of possibilities I can think of for making this word in pure cmd.

- Pre-processing of the input value to replace "%" with "%%" (arguably not pure cmd as this would, by definition, need an external tool)
- Input value arrives via a text input file or as the result of a command (such that this can be fed into for /f)

Last edited by bluesxman (29 Jun 2016 08:32)

cmd | *sh | ruby | chef

----------------------------

#11 27 Jun 2016 15:44
Aacini
Shadow Thief wrote:

Interestingly, it seems that batch will treat the string like a path even when the path does not exist.
Yes. When you try to get certain values of a file name that don't exist, there are "default values" if the file name don't include they; for example, the current drive and path. Only the file date, size and attributes are returned as empty strings if the file not exist.

----------------------------

#12 28 Jun 2016 08:17
foxidrive
bluesxman wrote:

RG: your answer seems to fall foul of what I was talking about with "%" handling.
My own personal view is that people get what they deserve if they try to handle percent signs as regular characters in batch code. big_smile

Having an unbalanced number of percent signs plus an environment variable in there will often lead to baldness! smile

----------------------------

#13 29 Jun 2016 11:26
NDog

Thanks RG, bluesxman and foxidrive - I'm always learning from you!
Also bluesxman I stole your percentage idea from the wim forum post

I hope you will give this script a test run and let me know if you think it could be improved!

Code: Select all

copy-pairs-wintest.cmd

@echo off&cls
goto :start

### folder pairs ###
repair
system32\DriverStore\FileRepository
system32\drivers\etc


### file pairs ###
hh.exe
system32\accwiz.exe
Media\start.wav




:start
@echo off&cls
setlocal
color 9f
set title=copy-pairs v1.2
title %title%
:: last updated @ 29/06/2016 - ndog
:: credits - RG (stringsplit method), bluesxman (percentage bar)
::
:: requires fsync.exe and robocopy.exe in the same folder as this script
:: fsync - http://www.vicobiscotti.it/en/fsync.htm
:: robocopy - https://www.microsoft.com/en-nz/download/details.aspx?id=17657
::
:: copies files or folders from the source '_src' to the destination '_dst'
:: desired file and folder paths are specified at the top of the script
:: advantage of this script is you do not have to define destinations for each pair
:: as the script automatically creates directory structure on the destination based on the source
:: it is fast and efficient and will not copy files needlessly


:: define source and destination location
set _src=C:\WINDOWS
set _dst=C:\test\syncpairs

:: dependancies
set _robocopy=%~dp0robocopy.exe
set _fsync=%~dp0fsync.exe


:: read pairs into memory
for /f "tokens=* usebackq" %%g in (%0) DO (
  if exist "%_src%\%%g" call :sub_addarray "%%g"
  if "%%g" equ ":start" goto :exitloop
  )

:exitloop
:: menu
echo Source: %_src%
echo Destination: %_dst%
echo.
echo Located pairs...
for /f "usebackq tokens=1-2* delims==" %%g in (`set _pair_ 2^>nul`) do (echo ^> %%h)
echo.
echo MAKE SURE YOU BACKUP ANY MODIFIED FILES ON DESTINATION BEFORE CONTINUING!!!
pause
color 4f

:: process pairs in memory
for /f "usebackq tokens=1-2* delims==" %%g in (`set _pair_`) do (
  rem echo g %%g
  rem echo h %%h
  for /f "delims=\ tokens=1*" %%i in ('echo %%h') do (
    rem echo i %%i
    rem echo j %%j
    set /a _processcount += 1
    if [%%j] == [] (
      REM root path - no '\' found
      call :parse_fileorfolder "%%i"
      ) else (
      REM subpath - '\' found
      call :sub_processfullpath "%%h"
      )
    call :global_processpair
    set _folder=&set _file=&set _fileextension=
    )
  )

:end
:: finished
color 2f
ping localhost -n 5 >nul
endlocal



:sub_addarray
  set _string=%~1
  set /a _paircount+=1
  if %_paircount% lss 10 set _pair_0%_paircount%=%_string%
  if %_paircount% geq 10 set _pair_%_paircount%=%_string%
  set _string=
  goto :eof


:parse_fileorfolder
  :: determine if a string is a file or folder based on the presence of a '.' + extenstion
  if [%~x1] == [] (
    set _folder=%~1
    goto :eof)
  set _file=%~n1
  set _fileextension=%~x1
  goto :eof


:sub_processfullpath
  :: check for path in subfolder ('\' characters found)
  set _string=%~1
  call :parse_filepath "%_string%"
  call :parse_fileorfolder "%_rightstring%"
  call :parse_parentfolder "%_string%" "%_rightstring%"
  set _string=
  goto :eof


:parse_filepath
  :: recurse through a "folder\file.ext" string and retrieve the right most variable after final '\' character
  for /f "delims=\ tokens=1*" %%g in ('echo %~1') do (
    set _rightstring=%%g
    if not [%%h] == [] call :parse_filepath "%%h"
    )
  goto :eof


:parse_parentfolder
  :: find value to left of final '\' character of the path string
  set _path=%~1
  set _right=%~2
  call set _parentfolder=%%_path:%_right%=%%
  set "_parentfolder=%_parentfolder:~0,-1%"
  goto :eof


:global_processpair
  :: read global variables
  
  :: percent bar
  set /a _perc=( %_processcount% * 100 ) / %_paircount%
  if defined _folder title %title% - [%_processcount%/%_paircount%] %_perc%%% - %_folder%
  if defined _file title %title% - [%_processcount%/%_paircount%] %_perc%%% - %_file%%_fileextension%
  
  :: create: folder operation - root
  if defined _folder if not defined _parentfolder md "%_dst%\%_folder%" 2>nul
  :: create: folder operation - subfolder
  if defined _folder if defined _parentfolder md "%_dst%\%_parentfolder%\%_folder%" 2>nul
  
  :: folder operation - root
  if defined _folder if not defined _parentfolder "%_fsync%" "%_src%\%_folder%" "%_dst%\%_folder%" /f /c
  :: folder operation - subfolder
  if defined _folder if defined _parentfolder "%_fsync%" "%_src%\%_parentfolder%\%_folder%" "%_dst%\%_parentfolder%\%_folder%" /f /c
  :: file operation - root
  if defined _file if not defined _parentfolder "%_robocopy%" "%_src%" "%_dst%" "%_file%%_fileextension%" /NJH /NJS
  :: file operation - subfolder
  if defined _file if defined _parentfolder "%_robocopy%" "%_src%\%_parentfolder%" "%_dst%\%_parentfolder%" "%_file%%_fileextension%" /NJH /NJS
  
  goto :eof
Last edited by NDog (29 Jun 2016 11:32)

cmd, vbs, ps, bash
autoit, python, swift

----------------------------

#14 03 Jul 2016 23:27
Sponge Belly

Hi NDog,

You can extract from the last backslash to the end of string like so:

set "filename=%longpath:\=" & set "filename=%"

Bizarre, but it works! smile

Read this DosTips Thread:

http://www.dostips.com/forum/viewtopic.php?f=3&t=6429

for the full story.

HTH!

- SB

----------------------------

#15 04 Jul 2016 08:42
bluesxman

That hurts my brain.

cmd | *sh | ruby | chef

----------------------------

#16 08 Jul 2016 09:14
foxidrive
bluesxman wrote:

That hurts my brain.
Agree. My brain exploded.
Post Reply