9

So I've been struggling to find any information about batch files in MS-DOS through all the information about CMD, but basically I'm trying to create a folder like 'Month-year'. I've managed to create a variable with the date with the help of an answer I got on SO.

I now need to create a substring of that variable. I have no idea how to do this as I recently learned that string manipulation seems to be a feature of CMD, and not DOS as I thought. Any suggestions?

7
  • 3
    MS-DOS' default shell (COMMAND.COM) doesn't have any functions for string manipulation past basic text substitution from parameters. There is no string handling - unless you employ some external tool/script language - which of course your script must deliver, as they are not provided by DOS. (That is beside the chance that SO may reach more people knowledgeable of such hacks than RC.SE)
    – Raffzahn
    Commented Jun 15, 2022 at 15:39
  • 6
    that's why don't call cmd "DOS" because they're very different. The capability of command.com is extremely limited and if possible you should use a 3rd party shell like 4DOS instead
    – phuclv
    Commented Jun 15, 2022 at 16:54
  • 1
    I strongly endorse @phuclv 's suggestion of using 4DOS. Commented Jun 15, 2022 at 17:24
  • 2
    @jeb right, the for %a in (/abc) do echo %a trick, but that gets painful quite quickly ;-). Commented Jun 17, 2022 at 16:04
  • 2
    QBASIC normally came with DOS 6.22, and QBASIC has plenty of ways to handle strings and can even call DOS commands using the "shell" statement. Writing a small basic program seems like an easy way to accomplish your goal.
    – user4574
    Commented Jun 17, 2022 at 17:40

3 Answers 3

19

COMMAND.COM has very few string-manipulation features. Back in DOS days, “complicated” batch files relied on a mixture of crazy tricks and external utilities.

To extract portions of a variable, you could use PC Magazine’s STRINGS utility, featured in volume 10, number 15:

STRINGS _month = MID %_date%, 1, 2

or

STRINGS _month = LEFT %_date%, 2

(Case isn’t significant here.)

The PC Magazine utility collections are somewhat difficult to find, but STRINGS was updated several times over the years and uploaded to the “batch utilities” collection on Simtel, and it can be found in Simtel archives. The latest version can be installed as a COMMAND.COM extension and provides many more functions:

STRINGS _date = DATE
STRINGS _month = LEFT %_date%, 2
STRINGS _year = RIGHT %_date%, 4

As phuclv says, you might want to use another shell such as 4DOS. This includes many functions, including string- and date/time-manipulation functions:

set month=%@left[%date,2]

or more directly, use the special %_month and %_year variables (containing the current month and year).

As you say, the web nowadays is a poor source of information on DOS-specific batch file programming, especially since many pages confuse DOS COMMAND.COM and Windows CMD.EXE features. Batch file programming was popular before the web existed, and as such is documented in books, magazine articles and batch file collections traded on BBSes or uploaded to FTP sites; if you’re interested in learning more, look through Timo Salmi’s BATFAQ and batch file collection, the various utilities on Garbo and Simtel, and books such as Neil J. Rubenking’s PC Magazine DOS Batch File Lab Notes or Rex Last’s MS-DOS Revealed (which concentrates on extending DOS batch files by using assembly language programs, coded using DEBUG). Google Books has an extensive collection of old PC Magazine issues, many of which include batch file tips and tricks.

2
  • 3
    I assume the STRINGS utility is a compiled binary, written in a language like C, and not a batch script in its own right that just provides a simplified user interface by hiding the "crazy tricks"?
    – Cody Gray
    Commented Jun 16, 2022 at 4:52
  • 4
    Yes, it’s a compiled binary. The assembly language source code is included in the archive. Commented Jun 16, 2022 at 5:05
11

COMMAND.COM has no built-in support for string manipulation other than concatenation. Once you start needing such functionality, it might be a good time to start looking for alternatives.

Since you’re targetting MS-DOS 6.22, a viable alternative to batch files might be QBasic, bundled with that version. You can use it as the OS generation’s VBScript of sorts:

' generate directory name
curdate$ = DATE$
dname$ = MID$(curdate$, 1, 2) + "-" + MID$(curdate$, 7, 4)

' create it if it does not exist
ON ERROR GOTO mkdirerr
MKDIR dname$

' show the contents of the directory
SHELL "dir /og " + dname$

' exit to DOS
SYSTEM

' handle errors
mkdirerr:
  IF ERR = 75 THEN RESUME NEXT   ' already exists
  PRINT "mkdir error "; ERR
  SYSTEM

Launching a QBasic program is a little less convenient than a batch file (you have to put qbasic /run before the program file name), but you can create a batch file wrapper that automates this.

A downside is that there is no straightforward way to pass command-line arguments to QBasic programs; your launcher will have to make do with stuffing them into environment variables or temporary files. There is also no way to signal an error with an exit code, but it’s not like batch files are any better in that respect.

10
  • 2
    Given the context, of DOS, it's a fine answer as there is no better way then using an external tool. Also, AFAIR QBASIC does offer access to the command line via a COMMAND$ function/pseudo-variable. CLINE$ = COMMAND$ should bring all parameters into CLINE$. Works with QBASIC /RUN as well as with 'compiled' programs. In fact, since its included since 5.0, it can be assumed. Then again when going that way, one might put all logic into BASIC code.
    – Raffzahn
    Commented Jun 15, 2022 at 17:42
  • 2
    Thank you! I had no idea about Qbasic I'll give it a go next time I'm in work! My other option was going to be just creating a compiled program in C++ or something but I was hoping for a simpler solution. Honestly it's been so hard to find anything about DOS, everything I search turns up 100 results about CMD. Commented Jun 15, 2022 at 18:13
  • 4
    Great answer. I don't understand the widespread disdain of frame-challenge answers on SE. Those are the answers from which I have learned the most. Commented Jun 15, 2022 at 18:40
  • 4
    @WayneConrad Well, I have a hard time to understand that term at all. "Challenging" of a "frame" requires a commonly agreed frame - which rarely exists in reality - even less in context of a short question. That logic aside, isn't all this about providing a insight to an issue, not a narrow on-request-solution? If the way drawn in a question would already be the best (or at least right) one, there would be no question, would it? Answers are about teaching knowledge, not solving jobs - maybe 'doing someones job' is the fundamental reason why SO is the planets prime source of copy&paste.
    – Raffzahn
    Commented Jun 15, 2022 at 19:13
  • 3
    @Davislor More correctly, QBasic was a cut-down version of Microsoft QuickBASIC 4.5 with the compiler and linker omitted. I wanted it so badly as a kid.
    – ssokolow
    Commented Jun 17, 2022 at 2:53
5

It can be done with a pure DOS batch file.

Usage

set str=abcdefghi
call substr 2 3 result
echo Result: "%result%"

Result: "cde"

It uses the fact, that a FOR loop splits a string into two halves when a slash prefixes a text.
The first loop returns only the first character, the second the remaining text.

substr.bat

@echo off
rem Extract a substring from a string
rem The input string has to be stored in "str"
rem @base index of the first character, 0 is the first
rem @length Take @length characters from index or stop at the string end
rem Usage: call substr @base @length @returnvar
rem Attention: Multiple delimiters (space, comma, semicolon) will be squashed into a single one
rem Sample: call substr 2 6 result

REM *** Trampoline to call a function inside this batch file
REM *** Usage: call %0// :myFunction arg1 arg2 ...
for %%L in (%0) do if "%%L"=="/" goto %1

:main
call %0// :split %1
set str=%_right%
call %0// :split %2
if "%3"=="" echo ["%_left%", "%_right%"]
if "%3"=="" goto :clear
set %3=%_left%

:clear
REM Clear temp vars
FOR %%v in (_left _right _idx _splitpos _finish _remain _part) do set %%v=
goto :eof

:split
if NOT %2==0 goto :process
  set _left=
  set _right=%str%
goto :eof

:process
set _left=
set _right=
set _idx=1
set _splitpos=%2
set _finish=0
set _remain=%str%

:loop
set _part=0
FOR %%a in (/%_remain%) DO call %0// :split_char %%a
if %_finish%==1 goto :eof
if NOT "%_remain%"=="" goto :loop
goto :eof

:split_char
if NOT %_part% == 0 goto :_second
if %_idx%==%_splitpos% set _finish=1
set _left=%_left%%2
call inc %_idx% _idx
set _part=1
set _remain=
goto :eof

:_second
if %_part% == 1 set _remain=%2
if NOT %_part% == 1 set _remain=%_remain% %2
set _part=2
if %_finish%==1 set _right=%_remain%
goto :eof

:eof

inc.bat (This could be a part of substr.bat, but for better readablity I split it)

@echo off

for %%L in (%0) do if "%%L"=="/" goto %1

REM Split and reverse number into _valueRev
set _remain=%1
set _valueRev=

:split_loop
set _loop=1

for %%a in (/%_remain%) do call %0// :split %1 %%a
if NOT "%_remain%"=="" goto :split_loop
goto :increment

:split
if %_loop%==2 goto :split_2
set _loop=2
set _remain=
set _valueRev=%3,%_valueRev%
goto :eof

:split_2
set _remain=%3
goto :eof

REM The main increment function
:increment
set _incresult=
set _carry=1
for %%d in (%_valueRev%) do call %0// :incDig %%d
if not "%_carry%"=="" call %0// :incDig 0

if NOT "%2"=="" set %2=%_incresult%
REM Clear temp vars
FOR %%v in (_incresult _carry _loop _valueRev _valueRev_comma _digit _remain) do set %%v=
goto :eof

:incDig
set _digit=%2
if "%_carry%"=="" goto :endinc
set _carry=
if %2==9 set _digit=0
if %2==9 set _carry=1
if %2==8 set _digit=9
if %2==7 set _digit=8
if %2==6 set _digit=7
if %2==5 set _digit=6
if %2==4 set _digit=5
if %2==3 set _digit=4
if %2==2 set _digit=3
if %2==1 set _digit=2
if %2==0 set _digit=1

:endinc
set _incresult=%_digit%%_incresult%
:eof
4
  • Sure this is working under DOS' COMMAND.COM? AFAIR nested FOR loops, which this requires, was only introduced with NT's CMD.EXE. The only trick to create nested FOR loops was to pack second one (or the CALL for it) into a second command processor via COMMAND /C. Likewise (IIRC) the leading slash 'trick' does not work in DOS 7 (Win9x).
    – Raffzahn
    Commented Jun 17, 2022 at 11:03
  • 1
    @Raffzahn Sure, I tested it with MSDOS6.22 (VMWare). The FOR loops are not really nested, inside the loops I just call the own batch file to emulate a call to a function by the trampoline code at the script beginning. Initially I invented the trampolines for windows batch files (cmd.exe) to circumvent the call in pipe limitation
    – jeb
    Commented Jun 17, 2022 at 11:33
  • The called batch file is still executed within the same command processor, so it's nested, but (to my memory) COMMAND does not contain a stack for FOR loops, just CALLs. Unexpected but cool if this works in a real MS-DOS 6.22.
    – Raffzahn
    Commented Jun 17, 2022 at 12:05
  • 2
    Yes, this is fine for MS-DOS 6.22. Nice! BTW you can use :: to introduce comments, instead of REM ;-) (it saves a byte and executes faster). Commented Jun 19, 2022 at 13:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .