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.
-
16\$\begingroup\$ Please post the answer parts of this as an answer, not as part of the question. \$\endgroup\$Not that Charles– Not that Charles2014年02月12日 03:33:15 +00:00Commented Feb 12, 2014 at 3:33
-
\$\begingroup\$ @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. \$\endgroup\$unclemeat– unclemeat2014年02月12日 04:17:44 +00:00Commented Feb 12, 2014 at 4:17
9 Answers 9
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
-
3\$\begingroup\$ @ankp-morpork Hey, I'm a bit late. But I want to inform you that the
ifstatement can be golfed further. The best one should be:if %a%==%b% (echo true)else echo false\$\endgroup\$stevefestl– stevefestl2017年07月16日 14:12:22 +00:00Commented Jul 16, 2017 at 14:12
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.
-
1\$\begingroup\$ Furthermore, the result is printed without an additional CRLF. \$\endgroup\$Jeff Wu– Jeff Wu2020年04月02日 05:56:58 +00:00Commented Apr 2, 2020 at 5:56
-
\$\begingroup\$ Further-furthermore, if the rules allow trash output,
@goto&set/a%1+%2also works. \$\endgroup\$Jeff Wu– Jeff Wu2020年04月19日 11:46:17 +00:00Commented Apr 19, 2020 at 11:46
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.
-
\$\begingroup\$ 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. \$\endgroup\$unclemeat– unclemeat2014年03月02日 06:26:16 +00:00Commented Mar 2, 2014 at 6:26
-
2\$\begingroup\$ Window'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. \$\endgroup\$Jerry Jeremiah– Jerry Jeremiah2015年06月03日 05:16:47 +00:00Commented Jun 3, 2015 at 5:16
-
2\$\begingroup\$ Unix style linefeeds will work just fine in CMD scripts, at least in recent versions of Windows. \$\endgroup\$user2428118– user24281182017年01月18日 14:36:47 +00:00Commented Jan 18, 2017 at 14:36
-
1\$\begingroup\$ In fact, CMD even removes CR characters before parsing: stackoverflow.com/questions/4094699/… \$\endgroup\$schnaader– schnaader2017年10月17日 12:18:00 +00:00Commented Oct 17, 2017 at 12:18
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).
-
\$\begingroup\$ 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-pIwhich would only count as 2 extra bytes. \$\endgroup\$Artyer– Artyer2016年11月24日 17:33:50 +00:00Commented Nov 24, 2016 at 17:33
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:~%==~?)
-
\$\begingroup\$ I like your last sentence reminding us this will save up some quotes :) +1 for that. \$\endgroup\$stevefestl– stevefestl2017年05月10日 08:37:50 +00:00Commented May 10, 2017 at 8:37
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.
-
\$\begingroup\$ or
set/aa=1,b=2,c=3,d=4,e=5,f=6,e=7...(there are much more efficient ways than this) \$\endgroup\$Jeff Wu– Jeff Wu2020年04月18日 09:47:54 +00:00Commented Apr 18, 2020 at 9:47
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.
-
1\$\begingroup\$ Alternatively you could do
goto:lwhich saves the same 1 byte and has the added bonus of keeping your colon intact \$\endgroup\$Matheus Avellar– Matheus Avellar2017年09月13日 02:37:05 +00:00Commented Sep 13, 2017 at 2:37 -
\$\begingroup\$ @MatheusAvellar: I have added the content accordingly to your comment \$\endgroup\$stevefestl– stevefestl2017年09月13日 09:13:49 +00:00Commented Sep 13, 2017 at 9:13
-
\$\begingroup\$ Furthermore, even though
goto:nonexistant_labelis a fatal error for thecmd.exeparser, commands can still be executed after if they are concatenated, unlikeFORorIF. Bonus: the command is executed as if it were the console:goto&set/a5actually prints 5! \$\endgroup\$Jeff Wu– Jeff Wu2020年04月18日 09:56:39 +00:00Commented Apr 18, 2020 at 9:56
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
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
-
\$\begingroup\$ Posting multiple answers to tips questions isn't bad. \$\endgroup\$Erik the Outgolfer– Erik the Outgolfer2018年10月13日 12:01:44 +00:00Commented Oct 13, 2018 at 12:01
-
\$\begingroup\$ -10 bytes: pastebin.com/ebQY0H87. use \r line terminators. \$\endgroup\$Jeff Wu– Jeff Wu2020年04月18日 09:51:06 +00:00Commented Apr 18, 2020 at 9:51