.bat man returns

Or how to make Windows work for you

Moses Holmström
12 min readAug 26, 2014

Some guy on the Internet asked if it was possible to take screenshots at regular intervals and upload them to an FTP server automatically. Oh, and it should work in Windows.

Windows doesn’t have the robust toolset that makes these kinds of tasks trivial in Linux, which is why I chose to accept the challenge. Here, I’ll be outlining the methods I used to solve this problem, along with sharing the scripts I came up with.

I’ll do my best to explain things as clearly as possible, but due to the inherently technical nature of these things, it’s not always too straightforward. If you have trouble understanding some parts, don’t be embarrased to ask for clarification!

I’ve also added some additional notes as comments within this article, so don’t forget to check those out as well.

Taking screenshots at regular intervals

Making your own tools in Windows isn’t too easy. The existing tools are somewhat fragmented in regard to each other and don’t always play along too well. Often, only a graphical interface is provided, which severely restricts automation via scripting. Thankfully, many people have made programs to alleviate this problem. One of such programs, the one I’ll be utilizing in this task, is NirCmd:

NirCmd is a small command-line utility that allows you to do some useful tasks without displaying any user interface.

Perfect. You may go ahead and download this free tool now. I placed it in C:\Windows\ for easy access from scripts, but you could also just put it in the same folder as your script.

NirCmd provides several different commands, which can help with a large range of tasks. One of those commands is savescreenshot. To use it, one needs to run NirCmd with command line parameters, specifying which action nircmd should perform, along with additional settings for that command. For example, the command

nircmd.exe savescreenshot it_works.png

would open NirCmd, instruct it to take a screenshot and save it as “it_works.png” into the current working folder. This is a great starting point.

To actually automate it, we’ll be creating a Windows Batch Script (also known as a bat file), to run a sequence of commands. Bat scripts are text files which have special keywords for different operations. Let’s look at the anatomy of a bat script:

Code listing for screenshot.bat

On the first row, we use set to create a variable, with the value of 5000. It will later be used to control the frequency of the screenshots, but I’ll dissect that in detail later.

Next we have a row starting with a colon. This indicates a label. The main functions of a label are 1) to give the code some structure and 2) to have a point in code to reference with. If we skip to the very last row, we see a goto loop. In essence, once the computer has carried out the task on each row, the goto command will make it jump to a row defined by this label, and continue from there. So in this case, everything between :loop and goto loop will be repeated ad infinitum. The label doesn’t have to be :loop, it might as well be :supercalifragilistic, but it’s generally better to have descriptive names.

Since our aim is to save many screenshots, we need a way to generate a unique name for each one. And since this is a time-related operation, it makes sense to use the current date and time as the screenshot’s filename. We accomplish this as follows (this gets a bit technical, just hang on):

  1. We use a special command wmic (Windows Management Instrumentation Command-line, a rather terribly named tool, I think) to access the current date and time for the local timezone. The whole command, wmic os get localdatetime returns an output such as “LocalDateTime 20140826121246.769000+180”. As you might’ve already deducted, the long number here is the current date and time in millisecond precision. That’s a bit more information than what we need, so we’ll trim some of it out.
  2. We add the setting /format:list to make wmic’s output slightly different. This makes the output read “LocalDateTime=20140826121246.769000+180", the minor difference being an equality sign in between.
  3. The wmic command is wrapped within a for loop. In essence, this makes the computer repeat commands until a predefined condition is reached. In this case the condition is to run twice, once for each side of the equality sign. This command might not be easy to decipher, but the key here is the “tokens=2 delims==” option. In layman’s terms, it splits up whatever line of text is passed to it at each “=” character. This is why we did step 2; wmic’s output can thus be separated to “LocalDateTime” and “20140826121246.769000+180". We’re only interested in the second part, which is saved to the variable “datetime” with set.
  4. On line 4, we snip out the excess parts out from datetime. This is accomplished with a substring operator. Basically, we specify a point in a text (the nth character) to start selecting characters, and we specify how many characters will be selected after that. Anything left outside that selection will be discarded. So with %datetime:~0,8% we start reading the datetime variable at the very first character and stop at the 8th. This leaves us with “20140826", the current date. For the time, we use %datetime:~8,6% which leaves us with “121246", the current time. We then concatenate these two snippets together with a dash in between, and set it as the final value of datetime. Thus the value of datetime after row 4 is “20140826–121246". Neat!

So now that we have a filename to use for the screenshots, we can actually take the screenshot. As I told before, we use nircmd to take and save the screenshot, but this time we’ll be using our datetime variable from before, as the filename. When the batch script runs, any variables between percentage marks will be evaluated to the value of that variable. So in this case the command

nircmd.exe savescreenshot “%datetime%.png”

will be evaluated by Windows to the command

nircmd.exe savescreenshot “20140826–121246.png”

That’s a lot of stuff in just 4 rows of code. To recap, we made a delay variable (which we haven’t used yet, hold on), we made a label (also not used yet), we parsed and formatted the current date and time from wmic and took a screenshot using that as the filename.

On line 6 we use nircmd again. This time we instruct it to run another script, “upload.bat”. We make it run hidden with the option hide, so that there will be no annoying popups each time a screenshot is uploaded (more on this later). We also supplement this command with the filename we gave to the screenshot earlier.

Line 7 is the final command before looping back to line 2. I must warn you, this is a bit of a hack! Windows does not provide a command to make the computer wait a set amount of time. However, in batch scripts, the computer will only* perform the next line once the previous line has finished. This allows us to create a delay by running any program which takes some time to finish.

One suitable program for this is ping. Ping is used when one wants to check the connectivity to another computer over the Internet. It sends a small packet of data to another computer and measures how long it takes for the other computer to respond. If the computer is unreachable (eg. there are connection problems, or the computer doesn’t even exist), ping will give up after a set amount of time, called TTL for Time-to-Live. This behavior is perfect for our needs. Besides, using tools in ways they were never intended to is great fun!

We use ping to send a packet to the IP address 1.0.0.0, which definitely isn’t assigned to any computer. This way we can be sure the command will take the amount of time specified with TTL. Ping sends four packets by default, but we only need one. Adding a parameter -n 1 accomplishes this. We set the TTL with the parameter -w %delay%, part of which we specified at the very start of the script. So when the script is run, the variable will evaluate to -w 5000, giving us a TTL of 5000ms or 5 seconds. Finally, we pipe the command’s output to nul, to prevent it from printing out text. Our 5 second delay is now complete. After this delay, the script starts again from row 2.

So that’s our first script, “screenshot.bat”. You can test it if you want, just double click on the batch file and screenshots should start appearing in the same folder, every 5 seconds.

I have provided links to all the scripts and tools used at the bottom of this article. No need to type them down manually, but I do strongly encourage you to modify and play with them! Don’t take my word for anything, experimenting is the key to learning.

Automatically uploading a file via FTP

Now that we have the timed screenshot part tackled, we can move on to uploading the saved screenshots to an FTP server. For this, we don’t need nircmd, so if you only came here for this part, there’s no need to download it.

Code listing for upload.bat

FTP, or File Transfer Protocol allows people to send files from computers to other computers over the Internet. In our case, we’ll be transfering screenshots, but this script is general enough to let you transfer almost any kinds of files. Let’s find out what it does and why.

The first four rows are used to configure the FTP connection. They are, as labeled by the variable name;

  1. The FTP server’s IP address, which could as well be in the form of ftp.example.com
  2. The username for the FTP server, as usually access is restricted to registered users only.
  3. The password for the user
  4. The path to which files are to be transferred to.

It’s not strictly necessary to assign this information to variables first, but it’s a lot more convenient to have user configurable data at the top of the script.

Luckily, Windows ships with a command line ftp client. It’s called ftp.exe and it works by connecting to an FTP server and performing various commands to accomplish different tasks. These commands cannot be directly passed as parameters, so we’ll be creating a temporary file, in which we store the required commands.

On line 6 a new command echo is introduced. As it’s name suggests, echo will repeat any parameters given to it, albeit variables will first be evaluated. For example, the command echo user %FTP_username% would print out user 1337h4x0r. The angle bracket (>) instructs Windows to pass along the output of a command to another command, or in this case to a file called ftpcmd.dat. Once this batch script reaches line 6, Windows will create a new file, which has the contents “user 1337h4x0r”. This is the first command we wish ftp.exe to perform for us later on.

The double angle bracket (>>, two characters, not to be confused with ») on line 7 instructs Windows to append to the file if it already exists. If we only used a single bracket, Windows would overwrite the file we previously wrote to, which we don’t want to happen. Line 7 will thus print out the ftp password onto a new line in ftpcmd.dat, which in this example would be “rosebud”.

We want to also allow the user to upload without setting a remote directory, in which case the root folder of the FTP server is used. Line 8 is the beginning of a conditional code block. The condition here is, that the variable FTP_remote_dir must be set (ie. not empty). In essence, if line 4 looked like this instead:

set FTP_remote_dir=

then anything after the open parenthesis on line 8 and before the closed parenthesis on line 10, would be skipped over. This ensures that the output of the echo command on line 9 will only be inserted to ftpcmd.dat if the user actually wishes to switch folders (the command cd is short for Change Directory).

Line 11 has a special variable %1. This is evaluated to whatever parameter is passed to this script. As you might remember from the previous section, we launched upload.bat with a parameter containing the filename we used to save the screenshot with. If you wished to use this as a standalone script, you could run it like so:

upload.bat myAwesomeFile.zip

and line 11 would then print out “put myAwesomeFile.zip” as the current last line in ftpcmd.dat.

The last command to be inserted into ftpcmd.dat is quit. After line 12, the entire contents of ftpcmd.dat would look like this:

user 1337h4xor
rosebud
cd screenshots
put 20140826–121246.png
quit

Let’s quickly dissect this. The first command tells the FTP server we wish to login with this username. The FTP server would then prompt for a password, to which the second line is given as reply. The third command instructs the server to change the current directory to /screenshots/. The fourth line would initiate the transfer of the screenshot file, after which the last line disconnects.

Returning to upload.bat, the line 14 will invoke ftp.exe, along with some parameters:

  • -n instructs ftp.exe to skip automatic login. Without this it would try to login with your Windows user account, which probably isn’t what you’d want.
  • -s:ftpcmd.dat tells ftp.exe to use our ftpcmd.dat file as a list of commands to perform once connected.
  • %FTP_hostname% will be evaluated to the address we set on the first line, and it instructs ftp.exe to connect to said address.

This is actually all that we need to upload with FTP. The rest of the script just deletes the screenshot from the local directory (in order to save disk space), deletes the ftpcmd.dat (we don’t need it, as a new one will be created once this script is run again) and resets the variables we set in the beginning.

You may go ahead and test this script, but be sure to replace the example address, username, password and folder to some real ones. Of course, you need an FTP account for this to work. Most webhotels provide one.

Final tweaks

If you tested the screenshot.bat before, you might’ve noticed that it opens a command window, obstructing a part of the screenshot! With upload.bat this problem is averted by NirCmd’s hide parameter, but for screenshot.bat it’s not as viable. To keep the command window hidden, we need yet another script, this time a VBscript instead of a batch file.

Code listing for start_hidden.vbs

To be honest, I have very little experience with VBscripts. The code above is a slightly adapted version of a snippet I found online. I can take a guess on what each line does, but don’t quote me on this.

  1. We set the script runner program as hidden.
  2. We create a new command window within the hidden script runner
  3. We run our screenshot.bat from within that command window
  4. ???

Frankly, I don’t even care about the details of what it does, as long as it works, and it does. If you’re more interested than me, you could whip out a VBscript reference and find out. If you do, please share your findings in the comments!

Recap and downloads

We now have three scripts, each with a different purpose:

  • screenshot.bat, which takes and saves a screenshot with the current date and time, then passes the filename along to:
  • upload.bat, which creates a list of FTP commands and invokes ftp.exe to move the screenshot into a folder within the remote server
  • start_hidden.vbs, which launches screenshot.bat hidden, so that it stays invisible.

Keep in mind that since the screenshot.bat is invisible, you can no longer close it by conventional means. It will keep taking screenshots and filling the FTP server’s disk. Since the screenshots are saved in PNG format, they may end up taking a lot of space quite quickly.

To stop screenshot.bat, you’ll need to open up Windows Task Manager, locate “cmd.exe” in the processes tab, select it and click End Process.

See below for the script files available as github gists. I’ve also annotated the scripts with some brief comments (lines starting with a double colon).

I hope that by now you feel like you’ve learned something new. If so, I wish you the best of luck with future endeavours in programming. As this also concludes my very first article on medium, I would much appreciate receiving feedback. Did I explain things clearly and thoroughly enough, or would you have preferred more brief annotations? In any case, there’s more to come. Until then, happy hacking!

-thykka

--

--