Tips for Golfing in Batch

14

2

Windows Batch (CMD) is probably the least suitable language for code golfing.

Avoid newline characters. If you're looking for the shortest code in bytes, as opposed to characters, you will want to get rid of all unnecessary carriage returns (newline characters = 2 bytes). You can use the & operation to do this, trimming one byte for each command.

echo Hello&echo World.

Setting variables. When setting variables using switches, the set parser will ignore spaces.

set /a a+=1
set /p b="User Input: "
:: Will be handled the same as..
set/aa+=1
set/pb="User Input: "

Note that the same parser rules do not apply to all commands - as an example, for loops require spaces. Except between the set, string, or command (i.e. what is inside the brackets) and the word do.

for /f %%a in (file.txt)do ...

Simple mathematical expressions. Also, notice the expression that I used for incrementing in the set /a command. For simple mathematical expressions use += -= *= /= instead of (for example) set /a a=%a%+1.

Gathering command output. To get the output of a command, it can sometimes be useful to output to and read from a file, where you otherwise might have used a for loop to gather the output:

for /f %%a in ('echo test') do set a=%%a
:: Can be shortened to
echo test>f&set/pa=<f

echo test send stdout to a file called f, >f, start a new command & and get the contents of the file in a variable set/pa=<f. Obviously this isn't always useful. But it can be when all you need is a single line of output.

Command output. When suppressing command output, use @ before the commands, unless you need to suppress more than 9 commands, in which case, use @echo off at the start of your script. @echo off is 9 bytes long, and therefore will generally save more bytes with longer scripts.

Commands often do more than just what is obvious - think about what your commands are doing. For example; if you need to set a variable and echo another variable, instead of:

set a=OUTPUT
echo %a%
echo test>f
set /p b=<f

You can use the set command with the /p switch to output %a% for you.

set a=OUTPUT
echo test>f
set /p b=%a%<f

Completely golfed...

set a=OUTPUT&echo test>f&set/pb=%a%<f

A couple of expamples - Count sum of all digits - Goldbach's conjecture.

Any other tips are more than appreciated - I'll keep updating this if I think of anything else.

unclemeat

Posted 2014-02-11T22:52:50.497

Reputation: 2 302

16Please post the answer parts of this as an answer, not as part of the question. – Not that Charles – 2014-02-12T03:33:15.333

@Charles I'm fairly sure how I've done this is an acceptable format for a 'tips question'. Other people can add answers with tips of their own. – unclemeat – 2014-02-12T04:17:44.420

Answers

4

Counters

Whenever you have a variable that will act as a counter starting from 0 you might want to write

set count=0
set/acount+=1

; however, you can eliminate the first lines because whenever set/a is used with an unset variable its value is assumed to be 0

set/acounter+=1

Code Blocks

There a number of ways you can shave off a few bytes when you have to used blocks of code.

Whenever you open a code block, you do not need to insert a line break before the first line of code in that block. The closing parentheses does not have to be on a line of its own either.

If %a%==%b% (
     echo true
) else (
    echo false
)

can be golfed to

If %a%==%b% (echo true)else (echo false)

Printing Without Trailing New Line

The common pattern for this is

echo|set/p=text

but, you can save 2 bytes changing echo to cd

cd|set/p=text

ankh-morpork

Posted 2014-02-11T22:52:50.497

Reputation: 1 350

2@ankp-morpork Hey, I'm a bit late. But I want to inform you that the if statement can be golfed further. The best one should be: if %a%==%b% (echo true)else echo false – stevefestl – 2017-07-16T14:12:22.890

3

Ampersands

Although &'s make the code seem shorter, as it uses less lines, it uses just as many characters as a newline. This means that

echo a&echo b

is as long as

echo a
echo b

so your code can stay readable without wasting characters.

There may be exceptions because some text editors combine line feed and carridge return for each new line.

kitcar2000

Posted 2014-02-11T22:52:50.497

Reputation: 2 689

2Window's carraige return linefeed is two characters and many text editors will count it as two, but when I first started answering golf questions one of the comments left said that in PPCG line endings are always counted as one character. – Jerry Jeremiah – 2015-06-03T05:16:47.333

2Unix style linefeeds will work just fine in CMD scripts, at least in recent versions of Windows. – user2428118 – 2017-01-18T14:36:47.343

1

In fact, CMD even removes CR characters before parsing: https://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/4095133#4095133

– schnaader – 2017-10-17T12:18:00.083

I included this in the original post because a newline counts for two bytes in every text editor I use. Thanks for adding this clarification. – unclemeat – 2014-03-02T06:26:16.720

3

Directly print the result of a numeric calculation

If the challenge requires you to output a number that needs to be calculated, you can use cmd/c set/a to output the result of the calculation directly instead of saving the calculation and then echoing it. Example:

@set/a a=%1+%2
@echo %a%

becomes

@cmd/cset/a%1+%2

Edit: Apparently no spaces are necessary at all, saving 2 more bytes.

Neil

Posted 2014-02-11T22:52:50.497

Reputation: 95 035

2

Delayed Expansion

Instead of using the lengthy setLocal enableDelayedExpansion you can use cmd /v on (or cmd/von) which will start a new interpreter with delayed variable expansion enabled.

I have yet to implement this so I don't have any examples, but you could use this in conjunction with the /c switch. Example:

@cmd/von/cset test=test^&echo !test!

Notice the use of the carat before the ampersand. if you need to use multiple ampersands or any other special characters, you may be better off enclosing everything after the /c switch in quotation marks:

@cmd/von/c"set test=test&set test1=test1&echo !test! !test1!"

This method also has the advantage of only having to use one @ character to mask all input, and can therefore be used, without /von, as a shorter alternative to @echo off (or multiple @ symbols).

9 characters: @echo off
6 characters: @cmd/c

For more lengthy scripts, which you can't do on one line, you can do the following to save some bytes:

@!! 2>nul||cmd/q/v/c%0&&exit/b

Or, ungolfed:

@!! 2>nul || cmd /q /v on /c %0 && exit/b

Replace @echo off&setLocal enableDelayedExpansion with the above.

When you first run your script !! will not expand at all, so you recieve an error because "!!" is not a command. The error output is then piped to nul (2>nul). When this first 'command' fails (||), it calls a new instance of cmd, which calls the batch file itself (using /v (on) to enable delayed expansion, and /q to turn echo off). Then !! will expand to nothing, as there is no variable called <NULL>. The rest of your script then runs. After which it will return to the original instance of cmd, and (&&) exit with the /b condition to keep the window open (the exit is so it doesn't continue to run through your script in the context of the first cmd instance, with echo on and delayed expansion off).

unclemeat

Posted 2014-02-11T22:52:50.497

Reputation: 2 302

For this, running batch files with cmd /q /v on /c <filename> should only count as 2 bytes because they are two "flags" similar to the Perl flags like -pI which would only count as 2 extra bytes. – Artyer – 2016-11-24T17:33:50.357

1

Start of a string

The notation %var:~start,end% extracts a substring of the variable var. However if start is 0 then you can omit it entirely. We already know that you can omit ,end if you want the rest of the string, which makes %var:~% an almost useless synonym for %var%, except that it returns ~ when %var% is empty. (Perhaps you could use it in a test: if %var:~%==~?)

Neil

Posted 2014-02-11T22:52:50.497

Reputation: 95 035

I like your last sentence reminding us this will save up some quotes :) +1 for that. – stevefestl – 2017-05-10T08:37:50.713

1

Shortening IF EQU statements

if %a% equ %b% do_something
if %a%==%b% do_something

Using == instead of equ saves 3 bytes.


Shortening quotes in IF statements

This is a traditional so-called "safer" if comparsion.

if "%a%"=="%b%" do_something

To shorten this statement, do the following when the input can only be alphanumerical/empty:

if %a%.==%b%. do_something

Saving a total of 2 bytes.


Challenges that require output

If the question says something like:

Outputs at least 1 visible character.

then you could just do this:

cd

The cd shows the current directory to STDOUT.


GOTOing a label

Here's some methods of gotoing a label, ordered in descending order of bytes.

goto :l
goto l
goto:l

The above method saves 1 byte.

stevefestl

Posted 2014-02-11T22:52:50.497

Reputation: 539

1Alternatively you could do goto:l which saves the same 1 byte and has the added bonus of keeping your colon intact – Matheus Avellar – 2017-09-13T02:37:05.853

@MatheusAvellar: I have added the content accordingly to your comment – stevefestl – 2017-09-13T09:13:49.080

1

Omitting space between command and parameters with slashes

Sorry about the uninformative title. What I'm trying to get at is that for some (not all) Windows command, you can omit the space in between the command/program name and the parameter, as long as that parameter starts with a slash. For example:

for /f %%G in ...

becomes

for/f %%G in ...

-1 Byte!

Piping output instead of using ERRORLEVEL

Using ERRORLEVEL to determine whether a command ran correctly is not the shortest or most elegant method. Instead, you can pipe output. This works as follows (remember that any command after the && operator only executes if the command before the && ran without errors. Any command after the || operator, on the other hand, will run only if the command before it did NOT run properly. My example is:

net session>nul
if %errorlevel%==0 (echo 1) else (echo 0)

This could be replaced with:

net session>nul&&echo 1||echo 0

-26 Bytes, wow! Note that this does not work with commands like choice, where the ERRORLEVEL has a special value according to some input.

Regards,
Gabe

Gabriel Mills

Posted 2014-02-11T22:52:50.497

Reputation: 778

1

Using parentheses effectively

Hi again,

Sorry to post a second answer but I felt this one deserved it. I have noticed that people often use one of the following methods to display command output without displaying that pesky C:\Windows\System32>echo foo line before each command.
The first method (using echo off):

@echo off
echo foo
echo bar
echo foobar
echo barfoo

The second method:

@echo foo
@echo bar
@echo foobar
@echo barfoo

However, neither of these methods are as short as the following:

@(echo foo
echo bar
echo foobar
echo barfoo)

Pretty handy, right? On another note, this can be used to pipe multiple lines of output to a file or device easily. For example, this lengthy code:

echo foo>file.txt
echo bar>file.txt
echo foobar>file.txt
echo barfoo>file.txt

becomes significantly shorter:

(echo foo
echo bar
echo foobar
echo barfoo)>file.txt

Thanks for reading this rather lengthy answer, and I hope this helps you. Regards,
Gabe

Gabriel Mills

Posted 2014-02-11T22:52:50.497

Reputation: 778

Posting multiple answers to [tag:tips] questions isn't bad. – Erik the Outgolfer – 2018-10-13T12:01:44.067

1

Repetitive strings

Set repetitive code blocks in variables, where the number of bytes used to set the variable + the number of bytes to output the variable are less than the number of bytes to just call the code directly.

For an example, see: Draw the combinations that add up to 100

You save 1 byte per usage of the set command, if you create a variable (named with one character, like "s") which contains set<space>. This only generates value if you use the set command more than 12 times. (Note there is a character at the end of the string set s=set).

set s=set 
%s%a=1
%s%b=2
%s%c=3
%s%d=4
%s%e=5
%s%f=6
%s%g=7
%s%h=8
%s%i=9
%s%j=10
%s%k=11
%s%l=12
%s%m=13

The above is 1 byte shorter than..

set a=1
set b=2
set c=3
set d=4
set e=5
set f=6
set g=7
set h=8
set i=9
set j=10
set k=11
set l=12
set m=13

This is probably a pretty poor example - but it gets the point across.

unclemeat

Posted 2014-02-11T22:52:50.497

Reputation: 2 302