In a batch file I get a folder with a list of subfolders with an unknown number of underscores e.g.:
a_b_10
c_d_e_2
f_17
I need to remove the last token of the names i.e.
a_b
c_d_e
f
Thanks
You could try to get the last part with an underscore and then remove this from your string, but this only works when the last part is unique.
In your sample the last part seems to be always a number in spite of the other parts.
This uses the trick to parse parts of a string by replace the delimiter by a linefeed character.
#echo off
setlocal EnableDelayedExpansion
set LF=^
for /F "delims=" %%X in ('dir /b /AD') do (
call :removeLastPart "%%~X"
)
exit /b
:removeLastPart
set "str=%~1"
for %%L in ("!LF!") DO (
for /F "delims=" %%P in ("!str:_=%%~L!") do set "last=%%P"
)
echo The last part is '!last!'
REM ** now remove the last part by replacing with nothing **
set "str=!str:_%last%=!"
echo !str!
exit /b
#echo off
set "root_dir=C:\scriptests"
setlocal enableDelayedExpansion
for /f %%d in ('dir /b /a:d *_*') do (
call :lastindexof "%%d" _ lio
set "f_name=%%~d"
echo renaming !f_name!
for %%S in (!lio!) do ren !f_name! !f_name:~0,%%S!
)
endlocal
exit /b 0
:lastindexof [%1 - string ; %2 - find last index of ; %3 - if defined will store the result in variable with same name]
#echo off
setlocal disableDelayedExpansion
set "str=%~1"
set "splitter=%~2"
set LF=^
rem ** Two empty lines are required
setlocal enableDelayedExpansion
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!str:%%R=%%L!"
)
)
for /f delims^=^" %%P in ("!var!") DO (
set "last_part=%%~P"
)
if "!last_part!" equ "" if "%~3" NEQ "" (
echo "not contained" >2
endlocal
set %~3=-1
exit
) else (
echo "not contained" >2
endlocal
echo -1
)
call :strlen0.3 str strlen
call :strlen0.3 last_part plen
call :strlen0.3 splitter slen
set /a lio=strlen-plen-slen
endlocal & set lio=%lio%
endlocal & if "%~3" NEQ "" (set %~3=%lio%) else echo %lio%
exit /b 0
:strlen0.3 StrVar [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%A in (2187 729 243 81 27 9 3 1) do (
set /A mod=2*%%A
for %%Z in (!mod!) do (
if "!s:~%%Z,1!" neq "" (
set /a "len+=%%Z"
set "s=!s:~%%Z!"
) else (
if "!s:~%%A,1!" neq "" (
set /a "len+=%%A"
set "s=!s:~%%A!"
)
)
)
)
endlocal & if "%~2" neq "" (set %~2=%len%) else echo **%len%**
exit /b
This solution will create the "renfolders.bat.txt" file for you to check in notepad, and run it as a batch file if you are happy with it.
This uses a helper batch file called repl.bat - download from: https://www.dropbox.com/s/qidqwztmetbvklt/repl.bat
Place repl.bat in the same folder as the batch file or in a folder that is on the path.
dir *_* /b /s /ad |repl ".*\\(.*)_.*" "ren \q$&\q \q$1\q" xa >"renfolders.bat.txt"
I just figured out this solution:
for /f %%f in ('dir /b /AD c:\MainFolder\') do (
set var=%%f
set var=!var:_= !
set /a count=0
for %%i in (!var!) do (set /a count+=1)
set /a count2=0
for %%i in (!var!) do (
set /a count2+=1
if /I !count2! equ 1 (set var2=%%i) else if not !count2! equ !count! (set var2=!var2!_%%i)
)
echo !var2!
)
Here is a novel approach
For each folder containing at least one _ in name:
create a temporary empty file with the same name
rename the temporary file, stripping off everything after the final _
read the new name into a variable
strip off the final _ from the name
For an explanation of how the rename works, see How does the Windows RENAME command interpret wildcards?
#echo off
setlocal enableDelayedExpansion
set "loc=%temp%\removeToken"
md "%loc%"
for /d %%F in (*_*) do (
copy nul "%loc%\%%F" >nul
ren "%loc%\*" "*_"
for %%A in ("%loc%\*") do set "new=%%~nxA"
del /q "%loc%\*"
echo old=%%F
echo new=!new:~0,-1!
echo(
)
rd "%loc%"
EDITED - Wrong copy of the code posted
It separates the elements of the directory name and iterates over them discarting the last element
#echo off
setlocal enabledelayedexpansion
for /d %%a in (*_*) do (
set "old=%%~na" & set "new=" & set "previous="
for %%b in ("!old:_=" "!") do (
if not defined previous (
set "previous=%%~b"
) else if not defined new (
set "new=!previous!"
) else set "new=!new!_!previous!"
set "previous=%%~b"
)
echo ren "%%~fa" "!new!"
)
endlocal
Related
I got a file containing a string on each line like this:
fruit-apple-1.5.6
vegtable-sla-mc5-6.5-16515
extra-huh-9.5-511-515
extra-3.2
I am iterating over it and want it to remove the part of the string on the right after in find "-" + any number "-0","-1","-2","-9",...
so output should be
fruit-apple
vegtable-sla-mc5
extra-huh
extra
this is code i have but it only works with a "-" i cant combine it so it takes "-" + any number like "-1","-5","-2",...
for /f "delims=|" %%A in ("!fileNameCheck:-=|!") do (
echo stripped string = %%A
)
complete code not necessary i think but in case u need it below
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set "RawPath=%~dp0"
FOR /F "USEBACKQ TOKENS=*" %%M IN ("%RawPath%/mods") DO (
REM for %%f in (*.jar) do (
Set "fileNameCheck=%%M"
for /f "delims=|" %%A in ("!fileNameCheck:-=|!") do (
Echo [46m%%A[0m
if exist "%~dp0%%A*.jar" (
REM echo [32mFound %%A "%~dp0%%A*.jar"[0m
if exist "%~dp0%%M" (
REM echo [42mUp to Date[0m [32m%%A "%~dp0%%M"[0m
) else (
for %%j in (*.jar) do (
echo %%j |find "%%A" >nul
if not errorlevel 1 (
echo [41mDifferent Version[0m [31m%%j [0m[90mNewer version[0m [32m%%M[0m
)
)
)
) else (
REM echo [31mMissing %%A[0m
)
)
)
pause
Given that the strings do not contain any of the caracters ?, *, < and >, the following script should do the trick:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0."
set "_FILE=mods.txt"
rem // Read file line by line:
for /F usebackq^ delims^=^ eol^= %%L in ("%_FILE%") do (
rem // Store current line, initialise variables:
set "LINE=%%L" & set "PREV=-" & set "COLL=-"
setlocal EnableDelayedExpansion
rem /* Split line string at every `-` and iterate through items;
rem (`?`, `*`, `<`, `>` must not occur in the string!): */
for %%I in ("!LINE:-=" "!") do (
endlocal & set "ITEM=%%~I"
rem // Check whether item only consists of numerals and `.`:
(
rem // This loop executes when there are other characters:
for /F "delims=0123456789. eol=." %%J in ("%%~I") do (
setlocal EnableDelayedExpansion
rem /* Rebuilt string with currently found items; if there
rem are other items following numeric ones, keep them: */
for /F "delims=" %%K in ("!COLL!!PREV:~1!-!ITEM!") do (
endlocal & set "COLL=%%K" & set "PREV=-"
)
)
) || (
rem // This section runs when there are numerals and `.`:
setlocal EnableDelayedExpansion
for /F "delims=" %%K in ("!PREV!-!ITEM!") do (
rem /* Store numeric items in a separate buffer; if there
rem are no other items following, this is dismissed: */
endlocal & set "PREV=%%K"
)
)
setlocal EnableDelayedExpansion
)
rem // Return rebuilt line string:
echo(!COLL:~2!
endlocal
)
endlocal
exit /B
The example input file mods.txt:
fruit-apple-1.5.6
vegtable-sla-mc5-6.5
extra-huh-9.5
extra-3.2
test-9.15.5
keep-forge
name-subname-10.5-55.5
globalxp-1.16.5
mix-1.2-string-10.5-55.5-more
mix-1.2-string-10.5-55.5-more-3.4
mix-1.2-string-10.5-55.5-more-3.4-5.6-7
8.9
1.2.3-lead
1.2.3-4.5.6-7.8.9-lead-mult
leads to the following output:
fruit-apple
vegtable-sla-mc5
extra-huh
extra
test
keep-forge
name-subname
globalxp
mix-1.2-string-10.5-55.5-more
mix-1.2-string-10.5-55.5-more
mix-1.2-string-10.5-55.5-more
1.2.3-lead
1.2.3-4.5.6-7.8.9-lead-mult
One way is to replace the - with \ and then use variable substitution to strip the %%~ni part from the string. We just need to remove the first and last occurrence then from the string. findstr will determine that the end of string needs to be -num.*
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%A in ('dir /b /s *.jar ^| findstr /RC:"-[0-9].*$"') do (
set "line=%%~nxA"
for %%i in ("!line:-=\!") do set "final=%%~pi"
set "final=!final:~0,-1!-"
echo !final! | findstr /RC:"\\[0-9].*$" >nul 2>&1
if !errorlevel! equ 0 (
for %%s in ("!final!") do set "final=%%~ps"
set final=!final!
)
set "final=!final:~0,-1!"
set "final=!final:~1!"
echo !final:\=-!
)
the edited code should take care of the additional - as per your comment example name-subname-10.5-55.5
Here's an untested idea, which may not be particularly quick, especially if you have a large number of strings in your mods file:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /F %%G In ('Echo Prompt $E ^| %SystemRoot%\System32\cmd.exe /D'
) Do Set "$E=%%G"
For /F Delims^=^ EOL^=^ UseBackQ %%G In ("%~dp0mods") Do (Set "Line=%%G"
SetLocal EnableDelayedExpansion
For /L %%H In (0 1 9) Do For /F Delims^=^ EOL^= %%I In (
'(Echo(^) ^| Set /P "=!Line:-%%H="^&:"!" 0^<NUL') Do Set "Line=%%I"
Echo(%$E%[46m!Line!%$E%[0m
EndLocal
)
Pause
My batch file is silently ending execution on the first loop (for /d "delims= eol=|" %%d in (*.*) do () and I can't tell why.
I'm not calling any other batch files
My subfolder names DO contain spaces, which I tried to handle with "delims= eol=|"
I never see !parent!!folder! echoed and it doesn't pause at the end. What am I doing wrong?
#Echo off
setlocal enabledelayedexpansion
pushd "%~dp0"
set "parent=%~dp0"
echo !parent!
set "destination=\\(test destination folder)\"
rem SET "destination=\\(prod destination folder)\"
set "FileCount=0"
for /d "delims= eol=|" %%d in (*.*) do (
set "folder=%%d"
echo !parent!!folder!
pause
for %%f in ("!parent!!folder!\(file identifying-pattern)*.DAT") do (
echo "File Type 1"
pause
if !FileCount! lss 200 (
set /a !FileCount!+=1
ECHO !FileCount!
ECHO !parent!!folder!%%f
ECHO !destination!%%f
MOVE "%%f" "!destination!"
)
(
for %%f in ("!parent!!folder!\(file identifying-pattern)*.DAT") do (
echo "File Type 2"
pause
if !FileCount! lss 200 (
set /a !FileCount!+=1
ECHO !FileCount!
ECHO !parent!!folder!%%f
ECHO !destination!%%f
MOVE "%%f" "!destination!"
)
)
)
for /d %%d in (*.*) do (
set "folder=%%d"
if not %%d==Archive (
if not %%d==Hold (
dir /b "!folder!"|find /v "">nul && ECHO "!folder! NOT EMPTY"||rmdir "!parent!!folder!"
)
)
)
pause
popd
I've took a guess at what you were doing, trying not to change the structure too much!
You'll need to add your destinations on lines 4 and 5 and your file patterns to lines 8 and 9 as necessary. (Please make sure you do not end your destination paths with back slashes):
#Echo Off
CD /D "%~dp0" 2>Nul || Exit /B
Set "Destination=\\(test destination folder)"
Rem Set "Destination=\\(prod destination folder)"
If Not Exist "%destination%\" Exit /B
Set "Pattern1=(file identifying-pattern)"
Set "Pattern2=(file identifying-pattern)"
Set /A Type1Count=Type2Count=0
SetLocal EnableDelayedExpansion
For /D %%A In (*) Do (
For %%B In ("%%A\%Pattern1%*.dat") Do (
Echo "File Type 1"
Set /A Type1Count +=1
If !Type1Count! Lss 200 (
Echo !Type1Count!. "%CD%\%%A\%%B" --^> "%Destination%\%%B"
If Not Exist "%Destination%\%%B\" MD "%Destination%\%%B"
Move /Y "%%B" "%Destination%\%%B">Nul
)
)
For %%C In ("%%A\%Pattern2%*.dat") Do (
Echo "File Type 2"
Set /A Type2Count +=1
If !Type2Count! Lss 200 (
Echo !Type2Count!. "%CD%\%%B\%%C" --^> "%Destination%\%%C"
If Not Exist "%Destination%\%%C\" MD "%Destination%\%%C"
Move /Y "%%C" "%Destination%\%%C">Nul
)
)
If /I Not "%%A"=="Archive" (
If /I Not "%%A"=="Hold" (
Set "file="
For %%D In (*) Do Set "file=1"
If Defined file (Echo %%A NOT EMPTY) Else RD "%%A" 2>Nul
)
)
)
Pause
If you're happy with it in your test you can Remark line 4 and unRemark line 5.
How to split string with the delim=string
E.g:
split string
"sssssMssssMYSTR___M___MYSTRzzzzzzMzzzz"
by
delim="MYSTR"
and result should be:
sssssMssss
___M___
zzzzzzMzzzz
Such code
for /f "tokens=1,2 delims=MYSTR" %%A in ("sssssMssssMYSTR___M___MYSTRzzzzzzMzzzz") do (
set fn1=%%A
)
doesn't work/ It splits only using first letter by 'M'
How to split by word?
#echo off
setlocal EnableDelayedExpansion
set "string=sssssMssssMYSTR___M___MYSTRzzzzzzMzzzz"
rem Do the split:
set i=1
set "fn!i!=%string:MYSTR=" & set /A i+=1 & set "fn!i!=%"
set fn
Output:
fn1=sssssMssss
fn2=___M___
fn3=zzzzzzMzzzz
You may review this topic for a detailed explanation on the method used...
try with split.bat (you can use both as a function or a separate bat file (does not handle well !,% and ")) :
#echo off
setlocal
call :split "sssssMssssMYSTR___M___MYSTRzzzzzzMzzzz" MYSTR 1 spl1
echo %spl1%
call :split "sssssMssssMYSTR___M___MYSTRzzzzzzMzzzz" MYSTR 2 spl2
echo %spl2%
call :split "sssssMssssMYSTR___M___MYSTRzzzzzzMzzzz" MYSTR 3
rem echo %spl2%
endlocal & exit /b %errorlevel%
:split [%1 - string to be splitted;%2 - split by;%3 - possition to get; %4 - if defined will store the result in variable with same name]
::http://ss64.org/viewtopic.php?id=1687
setlocal EnableDelayedExpansion
set "string=%~2%~1"
set "splitter=%~2"
set /a position=%~3
set LF=^
rem ** Two empty lines are required
echo off
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!string:%%~R%%~R=%%~L!"
set "var=!var:%%~R=%%~L!"
if "!var!" EQU "!string!" (
echo "%~1" does not contain "!splitter!" >&2
exit /B 1
)
)
)
if "!var!" equ "" (
endlocal & if "%~4" NEQ "" ( set "%~4=")
)
if !position! LEQ 0 ( set "_skip=" ) else (set "_skip=skip=%position%")
for /f "eol= %_skip% delims=" %%P in ("!var!") DO (
if "%%~P" neq "" (
set "part=%%~P"
goto :end_for
)
)
set "part="
:end_for
if not defined part (
endlocal
echo Index Out Of Bound >&2
exit /B 2
)
endlocal & if "%~4" NEQ "" (set %~4=%part%) else echo %part%
exit /b 0
I am new to using batch files so could someone please help me split a string i am getting from a file.
I am using %USERPROFILE% to get my string.
The string is: "C:\Users\nicholas"
I would like to keep the C:\ part, but get rid of the \Users\nicholas part.
for /f "tokens=2 delims=\" %A in ('set userprofile') do echo %A\
See for /?
Also
echo %userprofile:~0,3%
If you carefully read the output of set /?, you'll find the answer:
May also specify substrings for an expansion.
%PATH:~10,5%
would expand the PATH environment variable, and then use only the 5
characters that begin at the 11th (offset 10) character of the expanded
result.
So, you can use something like this to get the first 3 characters of your string:
> echo %userprofile:~0,3%
C:\
I As you need the drive where where the users are located you can use directly
%systemdrive% variable - this is the drive where the windows is installed
II the easiest way to get a drive from path:
for %%a in ("%userprofile%") do echo %%~da\
%~da - expands a path to its drive only
III over-complicated but powerful way (split function that can be used for a different things):
#echo off
call :split "%userprofile%" "\" 1 drive
echo %drive%\
goto :eof
:split [%1 - string to be splitted;%2 - split by;%3 - possition to get; %4 - if defined will store the result in variable with same name]
::http://ss64.org/viewtopic.php?id=1687
setlocal EnableDelayedExpansion
set "string=%~2%~1"
set "splitter=%~2"
set /a position=%~3
set LF=^
rem ** Two empty lines are required
echo off
for %%L in ("!LF!") DO (
for /f "delims=" %%R in ("!splitter!") do (
set "var=!string:%%~R%%~R=%%~L!"
set "var=!var:%%~R=%%~L!"
if "!var!" EQU "!string!" (
echo "%~1" does not contain "!splitter!" >&2
exit /B 1
)
)
)
if "!var!" equ "" (
endlocal & if "%~4" NEQ "" ( set "%~4=")
)
if !position! LEQ 0 ( set "_skip=" ) else (set "_skip=skip=%position%")
for /f "eol= %_skip% delims=" %%P in (""!var!"") DO (
if "%%~P" neq "" (
set "part=%%~P"
goto :end_for
)
)
set "part="
:end_for
if not defined part (
endlocal
echo Index Out Of Bound >&2
exit /B 2
)
endlocal & if "%~4" NEQ "" (set %~4=%part%) else echo %part%
exit /b 0
I need to get file name with out the file extension, folder name out putted to a csv file. I am able to get file name and folder name using:
#ECHO OFF
SETLOCAL
PUSHD "%~1"
FOR /f "delims=" %%i IN ("%cd%") DO SET directory=%%~nxi
(
FOR /f "delims=" %%i IN ('dir /b /a-d /on') DO (
SETLOCAL enabledelayedexpansion
ECHO "%%i","!directory!"
endlocal
)
)>filelist.csv
How can I rewrite this so the file extension is removed and if there are subfolders it will grab the subfolder name too?
#echo off
setlocal enableextensions disabledelayedexpansion
if not "%~1"=="" (
(for /f "tokens=*" %%i in ('dir /s /b /on "%~1\*"') do (
set "file=%%~dpni"
setlocal enabledelayedexpansion
echo(!file:%~dp1=!
endlocal
)) > filelist.csv
) else (
call "%~f0" "%cd%"
)
endlocal
Not sure about the final format. Try and comment.
EDITED - to handle case exposed by Andriy M
#echo off
setLocal
pushd "%~1"
set "cur_path=%cd:~2%"
setLocal enableDelayedExpansion
FOR /f "delims=" %%i IN ('dir /b /s /a-d /on') DO (
SET "file_path=%%~dpni"
SET "file_path=!file_path:~2!"
SET "file_path=!file_path:%cur_path%=!"
ECHO "!file_path!","%%~di%cur_path%"
)
endLocal
endLocal
EDIT
with additional ~ replacing (comparatively slow - could be optimized with macros... ):
#echo off
pushd .
set "cur_path=%cd:~2%"
call :wavereplacer "%cur_path%" "-" nw_cur_path
setlocal enableDelayedExpansion
FOR /f "delims=" %%i IN ('dir /b /s /a-d /on') DO (
SET "file_path=%%~dpni"
SET "file_path=!file_path:~2!"
SET "file_path=!file_path:%cur_path%=!"
CALL :wavereplacer "!file_path!" "-" file_path
ECHO "!file_path!","%%~di%nw_cur_path%"
)
endlocal
endlocal
goto :eof
:wavereplacer String Replacer [RtnVar]
setlocal
rem the result of the operation will be stored here
set "result=#%~1#"
set "replacer=%~2"
call :strlen0.3 result wl
call :strlen0.3 replacer rl
:start
set "part1="
set "part2="
rem splitting the string on two parts
for /f "tokens=1* delims=~" %%w in ("%result%") do (
set "part1=%%w"
set "part2=%%x"
)
rem calculating the count replace strings we should use
call :strlen0.3 part1 p1l
call :strlen0.3 part2 p2l
set /a iteration_end=wl-p1l-p2l
rem creating a sequence with replaced strings
setlocal enableDelayedExpansion
set "sequence="
for /l %%i in (1,1,%iteration_end%) do (
set sequence=!sequence!%replacer%
)
endlocal & set "sequence=%sequence%"
rem adjust the string length
set /a wl=wl+iteration_end*(rl-1)
rem replacing for the current iteration
set result=%part1%%sequence%%part2%
rem if the second part is empty the task is over
if "%part2%" equ "" (
set result=%result:~1,-1%
goto :endloop
)
goto :start
:endloop
endlocal & if "%~3" neq "" (set %~3=%result%) else echo %result%
exit /b
:strlen0.3 StrVar [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%A in (2187 729 243 81 27 9 3 1) do (
set /A mod=2*%%A
for %%Z in (!mod!) do (
if "!s:~%%Z,1!" neq "" (
set /a "len+=%%Z"
set "s=!s:~%%Z!"
) else (
if "!s:~%%A,1!" neq "" (
set /a "len+=%%A"
set "s=!s:~%%A!"
)
)
)
)
endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b